]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'd822110d3b5625b9dc80ccc442e06fc3cc851d76' into clippyup
authorPhilipp Krones <hello@philkrones.com>
Thu, 1 Dec 2022 17:29:38 +0000 (18:29 +0100)
committerPhilipp Krones <hello@philkrones.com>
Thu, 1 Dec 2022 17:29:38 +0000 (18:29 +0100)
237 files changed:
1  2 
src/tools/clippy/CHANGELOG.md
src/tools/clippy/README.md
src/tools/clippy/book/src/SUMMARY.md
src/tools/clippy/book/src/configuration.md
src/tools/clippy/book/src/development/adding_lints.md
src/tools/clippy/book/src/development/proposals/syntax-tree-patterns.md
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
src/tools/clippy/clippy_lints/src/approx_const.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
src/tools/clippy/clippy_lints/src/checked_conversions.rs
src/tools/clippy/clippy_lints/src/collapsible_if.rs
src/tools/clippy/clippy_lints/src/declared_lints.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/exit.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/from_over_into.rs
src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
src/tools/clippy/clippy_lints/src/functions/mod.rs
src/tools/clippy/clippy_lints/src/functions/result.rs
src/tools/clippy/clippy_lints/src/future_not_send.rs
src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
src/tools/clippy/clippy_lints/src/instant_subtraction.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/manual_bits.rs
src/tools/clippy/clippy_lints/src/manual_clamp.rs
src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
src/tools/clippy/clippy_lints/src/manual_let_else.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
src/tools/clippy/clippy_lints/src/manual_retain.rs
src/tools/clippy/clippy_lints/src/manual_strip.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/try_err.rs
src/tools/clippy/clippy_lints/src/mem_replace.rs
src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
src/tools/clippy/clippy_lints/src/methods/err_expect.rs
src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
src/tools/clippy/clippy_lints/src/methods/map_clone.rs
src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/no_effect.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
src/tools/clippy/clippy_lints/src/redundant_field_names.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
src/tools/clippy/clippy_lints/src/unused_rounding.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
src/tools/clippy/clippy_utils/src/attrs.rs
src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/clippy.toml
src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.fixed
src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.rs
src/tools/clippy/tests/ui-toml/allow_mixed_uninlined_format_args/uninlined_format_args.stderr
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/almost_complete_letter_range.fixed
src/tools/clippy/tests/ui/almost_complete_letter_range.rs
src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
src/tools/clippy/tests/ui/cast_lossless_bool.fixed
src/tools/clippy/tests/ui/cast_lossless_bool.rs
src/tools/clippy/tests/ui/cast_lossless_bool.stderr
src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
src/tools/clippy/tests/ui/cfg_attr_rustfmt.stderr
src/tools/clippy/tests/ui/checked_conversions.fixed
src/tools/clippy/tests/ui/checked_conversions.rs
src/tools/clippy/tests/ui/checked_conversions.stderr
src/tools/clippy/tests/ui/cloned_instead_of_copied.fixed
src/tools/clippy/tests/ui/cloned_instead_of_copied.rs
src/tools/clippy/tests/ui/cloned_instead_of_copied.stderr
src/tools/clippy/tests/ui/err_expect.fixed
src/tools/clippy/tests/ui/err_expect.rs
src/tools/clippy/tests/ui/err_expect.stderr
src/tools/clippy/tests/ui/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/eta.stderr
src/tools/clippy/tests/ui/explicit_auto_deref.fixed
src/tools/clippy/tests/ui/explicit_auto_deref.rs
src/tools/clippy/tests/ui/filter_map_next_fixable.fixed
src/tools/clippy/tests/ui/filter_map_next_fixable.rs
src/tools/clippy/tests/ui/filter_map_next_fixable.stderr
src/tools/clippy/tests/ui/from_over_into.fixed
src/tools/clippy/tests/ui/from_over_into.rs
src/tools/clippy/tests/ui/from_over_into.stderr
src/tools/clippy/tests/ui/if_then_some_else_none.rs
src/tools/clippy/tests/ui/if_then_some_else_none.stderr
src/tools/clippy/tests/ui/manual_clamp.rs
src/tools/clippy/tests/ui/manual_clamp.stderr
src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
src/tools/clippy/tests/ui/manual_is_ascii_check.rs
src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
src/tools/clippy/tests/ui/manual_let_else.rs
src/tools/clippy/tests/ui/manual_let_else.stderr
src/tools/clippy/tests/ui/manual_rem_euclid.fixed
src/tools/clippy/tests/ui/manual_rem_euclid.rs
src/tools/clippy/tests/ui/manual_rem_euclid.stderr
src/tools/clippy/tests/ui/manual_retain.fixed
src/tools/clippy/tests/ui/manual_retain.rs
src/tools/clippy/tests/ui/manual_retain.stderr
src/tools/clippy/tests/ui/manual_split_once.fixed
src/tools/clippy/tests/ui/manual_split_once.rs
src/tools/clippy/tests/ui/manual_split_once.stderr
src/tools/clippy/tests/ui/manual_str_repeat.fixed
src/tools/clippy/tests/ui/manual_str_repeat.rs
src/tools/clippy/tests/ui/manual_str_repeat.stderr
src/tools/clippy/tests/ui/manual_strip.rs
src/tools/clippy/tests/ui/manual_strip.stderr
src/tools/clippy/tests/ui/map_unwrap_or.rs
src/tools/clippy/tests/ui/map_unwrap_or.stderr
src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
src/tools/clippy/tests/ui/mem_replace.fixed
src/tools/clippy/tests/ui/mem_replace.rs
src/tools/clippy/tests/ui/mem_replace.stderr
src/tools/clippy/tests/ui/min_rust_version_attr.rs
src/tools/clippy/tests/ui/min_rust_version_attr.stderr
src/tools/clippy/tests/ui/min_rust_version_invalid_attr.stderr
src/tools/clippy/tests/ui/misnamed_getters.rs
src/tools/clippy/tests/ui/misnamed_getters.stderr
src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/needless_question_mark.fixed
src/tools/clippy/tests/ui/needless_question_mark.rs
src/tools/clippy/tests/ui/needless_question_mark.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/needless_return.stderr
src/tools/clippy/tests/ui/needless_splitn.fixed
src/tools/clippy/tests/ui/needless_splitn.rs
src/tools/clippy/tests/ui/needless_splitn.stderr
src/tools/clippy/tests/ui/option_as_ref_deref.fixed
src/tools/clippy/tests/ui/option_as_ref_deref.rs
src/tools/clippy/tests/ui/option_as_ref_deref.stderr
src/tools/clippy/tests/ui/ptr_as_ptr.fixed
src/tools/clippy/tests/ui/ptr_as_ptr.rs
src/tools/clippy/tests/ui/ptr_as_ptr.stderr
src/tools/clippy/tests/ui/range_contains.fixed
src/tools/clippy/tests/ui/range_contains.rs
src/tools/clippy/tests/ui/range_contains.stderr
src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
src/tools/clippy/tests/ui/redundant_field_names.fixed
src/tools/clippy/tests/ui/redundant_field_names.rs
src/tools/clippy/tests/ui/redundant_field_names.stderr
src/tools/clippy/tests/ui/redundant_static_lifetimes.fixed
src/tools/clippy/tests/ui/redundant_static_lifetimes.rs
src/tools/clippy/tests/ui/redundant_static_lifetimes.stderr
src/tools/clippy/tests/ui/result_large_err.rs
src/tools/clippy/tests/ui/seek_from_current.fixed
src/tools/clippy/tests/ui/seek_from_current.rs
src/tools/clippy/tests/ui/seek_from_current.stderr
src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.fixed
src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.rs
src/tools/clippy/tests/ui/seek_to_start_instead_of_rewind.stderr
src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/uninlined_format_args.fixed
src/tools/clippy/tests/ui/uninlined_format_args.rs
src/tools/clippy/tests/ui/uninlined_format_args.stderr
src/tools/clippy/tests/ui/unnecessary_cast.fixed
src/tools/clippy/tests/ui/unnecessary_cast.rs
src/tools/clippy/tests/ui/unnecessary_cast.stderr
src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
src/tools/clippy/tests/ui/unnecessary_operation.fixed
src/tools/clippy/tests/ui/unnecessary_operation.rs
src/tools/clippy/tests/ui/unnecessary_safety_comment.rs
src/tools/clippy/tests/ui/unnecessary_safety_comment.stderr
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
src/tools/clippy/tests/ui/unnecessary_unsafety_doc.rs
src/tools/clippy/tests/ui/unnecessary_unsafety_doc.stderr
src/tools/clippy/tests/ui/unnested_or_patterns.fixed
src/tools/clippy/tests/ui/unnested_or_patterns.rs
src/tools/clippy/tests/ui/unnested_or_patterns.stderr
src/tools/clippy/tests/ui/unused_rounding.fixed
src/tools/clippy/tests/ui/unused_rounding.rs
src/tools/clippy/tests/ui/unused_rounding.stderr
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/use_self.stderr
src/tools/clippy/triagebot.toml

index 6f1f73c1fd2fe385e06badeb46983a8860864c44,0000000000000000000000000000000000000000..23912bb3ed6b16aea9a4ce27ad1cd5a1a2f50661
mode 100644,000000..100644
--- /dev/null
@@@ -1,4520 -1,0 +1,4522 @@@
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / Beta / In Rust Nightly
 +
 +[b52fb523...master](https://github.com/rust-lang/rust-clippy/compare/b52fb523...master)
 +
 +## Rust 1.65
 +
 +Current stable, released 2022-11-03
 +
 +[3c7e7dbc...b52fb523](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...b52fb523)
 +
 +### Important Changes
 +
 +* Clippy now has an `--explain <LINT>` command to show the lint description in the console
 +  [#8952](https://github.com/rust-lang/rust-clippy/pull/8952)
 +
 +### New Lints
 +
 +* [`unused_peekable`]
 +  [#9258](https://github.com/rust-lang/rust-clippy/pull/9258)
 +* [`collapsible_str_replace`]
 +  [#9269](https://github.com/rust-lang/rust-clippy/pull/9269)
 +* [`manual_string_new`]
 +  [#9295](https://github.com/rust-lang/rust-clippy/pull/9295)
 +* [`iter_on_empty_collections`]
 +  [#9187](https://github.com/rust-lang/rust-clippy/pull/9187)
 +* [`iter_on_single_items`]
 +  [#9187](https://github.com/rust-lang/rust-clippy/pull/9187)
 +* [`bool_to_int_with_if`]
 +  [#9412](https://github.com/rust-lang/rust-clippy/pull/9412)
 +* [`multi_assignments`]
 +  [#9379](https://github.com/rust-lang/rust-clippy/pull/9379)
 +* [`result_large_err`]
 +  [#9373](https://github.com/rust-lang/rust-clippy/pull/9373)
 +* [`partialeq_to_none`]
 +  [#9288](https://github.com/rust-lang/rust-clippy/pull/9288)
 +* [`suspicious_to_owned`]
 +  [#8984](https://github.com/rust-lang/rust-clippy/pull/8984)
 +* [`cast_slice_from_raw_parts`]
 +  [#9247](https://github.com/rust-lang/rust-clippy/pull/9247)
 +* [`manual_instant_elapsed`]
 +  [#9264](https://github.com/rust-lang/rust-clippy/pull/9264)
 +
 +### Moves and Deprecations
 +
 +* Moved [`significant_drop_in_scrutinee`] to `nursery` (now allow-by-default)
 +  [#9302](https://github.com/rust-lang/rust-clippy/pull/9302)
 +* Rename `logic_bug` to [`overly_complex_bool_expr`]
 +  [#9306](https://github.com/rust-lang/rust-clippy/pull/9306)
 +* Rename `arithmetic` to [`arithmetic_side_effects`]
 +  [#9443](https://github.com/rust-lang/rust-clippy/pull/9443)
 +* Moved [`only_used_in_recursion`] to complexity (now warn-by-default)
 +  [#8804](https://github.com/rust-lang/rust-clippy/pull/8804)
 +* Moved [`assertions_on_result_states`] to restriction (now allow-by-default)
 +  [#9273](https://github.com/rust-lang/rust-clippy/pull/9273)
 +* Renamed `blacklisted_name` to [`disallowed_names`]
 +  [#8974](https://github.com/rust-lang/rust-clippy/pull/8974)
 +
 +### Enhancements
 +
 +* [`option_if_let_else`]: Now also checks for match expressions
 +  [#8696](https://github.com/rust-lang/rust-clippy/pull/8696)
 +* [`explicit_auto_deref`]: Now lints on implicit returns in closures
 +  [#9126](https://github.com/rust-lang/rust-clippy/pull/9126)
 +* [`needless_borrow`]: Now considers trait implementations
 +  [#9136](https://github.com/rust-lang/rust-clippy/pull/9136)
 +* [`suboptimal_flops`], [`imprecise_flops`]: Now lint on constant expressions
 +  [#9404](https://github.com/rust-lang/rust-clippy/pull/9404)
 +* [`if_let_mutex`]: Now detects mutex behind references and warns about deadlocks
 +  [#9318](https://github.com/rust-lang/rust-clippy/pull/9318)
 +
 +### False Positive Fixes
 +
 +* [`unit_arg`] [`default_trait_access`] [`missing_docs_in_private_items`]: No longer
 +  trigger in code generated from proc-macros
 +  [#8694](https://github.com/rust-lang/rust-clippy/pull/8694)
 +* [`unwrap_used`]: Now lints uses of `unwrap_err`
 +  [#9338](https://github.com/rust-lang/rust-clippy/pull/9338)
 +* [`expect_used`]: Now lints uses of `expect_err`
 +  [#9338](https://github.com/rust-lang/rust-clippy/pull/9338)
 +* [`transmute_undefined_repr`]: Now longer lints if the first field is compatible
 +  with the other type
 +  [#9287](https://github.com/rust-lang/rust-clippy/pull/9287)
 +* [`unnecessary_to_owned`]: No longer lints, if type change cased errors in
 +  the caller function
 +  [#9424](https://github.com/rust-lang/rust-clippy/pull/9424)
 +* [`match_like_matches_macro`]: No longer lints, if there are comments inside the
 +  match expression
 +  [#9276](https://github.com/rust-lang/rust-clippy/pull/9276)
 +* [`partialeq_to_none`]: No longer trigger in code generated from macros
 +  [#9389](https://github.com/rust-lang/rust-clippy/pull/9389)
 +* [`arithmetic_side_effects`]: No longer lints expressions that only use literals
 +  [#9365](https://github.com/rust-lang/rust-clippy/pull/9365)
 +* [`explicit_auto_deref`]: Now ignores references on block expressions when the type
 +  is `Sized`, on `dyn Trait` returns and when the suggestion is non-trivial
 +  [#9126](https://github.com/rust-lang/rust-clippy/pull/9126)
 +* [`trait_duplication_in_bounds`]: Now better tracks bounds to avoid false positives
 +  [#9167](https://github.com/rust-lang/rust-clippy/pull/9167)
 +* [`format_in_format_args`]: Now suggests cases where the result is formatted again
 +  [#9349](https://github.com/rust-lang/rust-clippy/pull/9349)
 +* [`only_used_in_recursion`]: No longer lints on function without recursions and
 +  takes external functions into account
 +  [#8804](https://github.com/rust-lang/rust-clippy/pull/8804)
 +* [`missing_const_for_fn`]: No longer lints in proc-macros
 +  [#9308](https://github.com/rust-lang/rust-clippy/pull/9308)
 +* [`non_ascii_literal`]: Allow non-ascii comments in tests and make sure `#[allow]`
 +  attributes work in tests
 +  [#9327](https://github.com/rust-lang/rust-clippy/pull/9327)
 +* [`question_mark`]: No longer lint `if let`s with subpatterns
 +  [#9348](https://github.com/rust-lang/rust-clippy/pull/9348)
 +* [`needless_collect`]: No longer lints in loops
 +  [#8992](https://github.com/rust-lang/rust-clippy/pull/8992)
 +* [`mut_mutex_lock`]: No longer lints if the mutex is behind an immutable reference
 +  [#9418](https://github.com/rust-lang/rust-clippy/pull/9418)
 +* [`needless_return`]: Now ignores returns with arguments
 +  [#9381](https://github.com/rust-lang/rust-clippy/pull/9381)
 +* [`range_plus_one`], [`range_minus_one`]: Now ignores code with macros
 +  [#9446](https://github.com/rust-lang/rust-clippy/pull/9446)
 +* [`assertions_on_result_states`]: No longer lints on the unit type
 +  [#9273](https://github.com/rust-lang/rust-clippy/pull/9273)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unwrap_or_else_default`]: Now suggests `unwrap_or_default()` for empty strings
 +  [#9421](https://github.com/rust-lang/rust-clippy/pull/9421)
 +* [`if_then_some_else_none`]: Now also suggests `bool::then_some`
 +  [#9289](https://github.com/rust-lang/rust-clippy/pull/9289)
 +* [`redundant_closure_call`]: The suggestion now works for async closures
 +  [#9053](https://github.com/rust-lang/rust-clippy/pull/9053)
 +* [`suboptimal_flops`]: Now suggests parenthesis when they are required
 +  [#9394](https://github.com/rust-lang/rust-clippy/pull/9394)
 +* [`case_sensitive_file_extension_comparisons`]: Now suggests `map_or(..)` instead of `map(..).unwrap_or`
 +  [#9341](https://github.com/rust-lang/rust-clippy/pull/9341)
 +* Deprecated configuration values can now be updated automatically
 +  [#9252](https://github.com/rust-lang/rust-clippy/pull/9252)
 +* [`or_fun_call`]: Now suggest `Entry::or_default` for `Entry::or_insert(Default::default())`
 +  [#9342](https://github.com/rust-lang/rust-clippy/pull/9342)
 +* [`unwrap_used`]: Only suggests `expect` if [`expect_used`] is allowed
 +  [#9223](https://github.com/rust-lang/rust-clippy/pull/9223)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`useless_format`] for literals
 +  [#9406](https://github.com/rust-lang/rust-clippy/pull/9406)
 +* Fix infinite loop in [`vec_init_then_push`]
 +  [#9441](https://github.com/rust-lang/rust-clippy/pull/9441)
 +* Fix ICE when reading literals with weird proc-macro spans
 +  [#9303](https://github.com/rust-lang/rust-clippy/pull/9303)
 +
 +## Rust 1.64
 +
 +Released 2022-09-22
 +
 +[d7b5cbf0...3c7e7dbc](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...3c7e7dbc)
 +
 +### New Lints
 +
 +* [`arithmetic_side_effects`]
 +  [#9130](https://github.com/rust-lang/rust-clippy/pull/9130)
 +* [`invalid_utf8_in_unchecked`]
 +  [#9105](https://github.com/rust-lang/rust-clippy/pull/9105)
 +* [`assertions_on_result_states`]
 +  [#9225](https://github.com/rust-lang/rust-clippy/pull/9225)
 +* [`manual_find`]
 +  [#8649](https://github.com/rust-lang/rust-clippy/pull/8649)
 +* [`manual_retain`]
 +  [#8972](https://github.com/rust-lang/rust-clippy/pull/8972)
 +* [`default_instead_of_iter_empty`]
 +  [#8989](https://github.com/rust-lang/rust-clippy/pull/8989)
 +* [`manual_rem_euclid`]
 +  [#9031](https://github.com/rust-lang/rust-clippy/pull/9031)
 +* [`obfuscated_if_else`]
 +  [#9148](https://github.com/rust-lang/rust-clippy/pull/9148)
 +* [`std_instead_of_core`]
 +  [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
 +* [`std_instead_of_alloc`]
 +  [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
 +* [`alloc_instead_of_core`]
 +  [#9103](https://github.com/rust-lang/rust-clippy/pull/9103)
 +* [`explicit_auto_deref`]
 +  [#8355](https://github.com/rust-lang/rust-clippy/pull/8355)
 +
 +
 +### Moves and Deprecations
 +
 +* Moved [`format_push_string`] to `restriction` (now allow-by-default)
 +  [#9161](https://github.com/rust-lang/rust-clippy/pull/9161)
 +
 +### Enhancements
 +
 +* [`significant_drop_in_scrutinee`]: Now gives more context in the lint message
 +  [#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
 +* [`single_match`], [`single_match_else`]: Now catches more `Option` cases
 +  [#8985](https://github.com/rust-lang/rust-clippy/pull/8985)
 +* [`unused_async`]: Now works for async methods
 +  [#9025](https://github.com/rust-lang/rust-clippy/pull/9025)
 +* [`manual_filter_map`], [`manual_find_map`]: Now lint more expressions
 +  [#8958](https://github.com/rust-lang/rust-clippy/pull/8958)
 +* [`question_mark`]: Now works for simple `if let` expressions
 +  [#8356](https://github.com/rust-lang/rust-clippy/pull/8356)
 +* [`undocumented_unsafe_blocks`]: Now finds comments before the start of closures
 +  [#9117](https://github.com/rust-lang/rust-clippy/pull/9117)
 +* [`trait_duplication_in_bounds`]: Now catches duplicate bounds in where clauses
 +  [#8703](https://github.com/rust-lang/rust-clippy/pull/8703)
 +* [`shadow_reuse`], [`shadow_same`], [`shadow_unrelated`]: Now lint in const blocks
 +  [#9124](https://github.com/rust-lang/rust-clippy/pull/9124)
 +* [`slow_vector_initialization`]: Now detects cases with `vec.capacity()`
 +  [#8953](https://github.com/rust-lang/rust-clippy/pull/8953)
 +* [`unused_self`]: Now respects the `avoid-breaking-exported-api` config option
 +  [#9199](https://github.com/rust-lang/rust-clippy/pull/9199)
 +* [`box_collection`]: Now supports all std collections
 +  [#9170](https://github.com/rust-lang/rust-clippy/pull/9170)
 +
 +### False Positive Fixes
 +
 +* [`significant_drop_in_scrutinee`]: Now ignores calls to `IntoIterator::into_iter`
 +  [#9140](https://github.com/rust-lang/rust-clippy/pull/9140)
 +* [`while_let_loop`]: Now ignores cases when the significant drop order would change
 +  [#8981](https://github.com/rust-lang/rust-clippy/pull/8981)
 +* [`branches_sharing_code`]: Now ignores cases where moved variables have a significant
 +  drop or variable modifications can affect the conditions
 +  [#9138](https://github.com/rust-lang/rust-clippy/pull/9138)
 +* [`let_underscore_lock`]: Now ignores bindings that aren't locked
 +  [#8990](https://github.com/rust-lang/rust-clippy/pull/8990)
 +* [`trivially_copy_pass_by_ref`]: Now tracks lifetimes and ignores cases where unsafe
 +  pointers are used
 +  [#8639](https://github.com/rust-lang/rust-clippy/pull/8639)
 +* [`let_unit_value`]: No longer ignores `#[allow]` attributes on the value
 +  [#9082](https://github.com/rust-lang/rust-clippy/pull/9082)
 +* [`declare_interior_mutable_const`]: Now ignores the `thread_local!` macro
 +  [#9015](https://github.com/rust-lang/rust-clippy/pull/9015)
 +* [`if_same_then_else`]: Now ignores branches with `todo!` and `unimplemented!`
 +  [#9006](https://github.com/rust-lang/rust-clippy/pull/9006)
 +* [`enum_variant_names`]: Now ignores names with `_` prefixes
 +  [#9032](https://github.com/rust-lang/rust-clippy/pull/9032)
 +* [`let_unit_value`]: Now ignores cases, where the unit type is manually specified
 +  [#9056](https://github.com/rust-lang/rust-clippy/pull/9056)
 +* [`match_same_arms`]: Now ignores branches with `todo!`
 +  [#9207](https://github.com/rust-lang/rust-clippy/pull/9207)
 +* [`assign_op_pattern`]: Ignores cases that break borrowing rules
 +  [#9214](https://github.com/rust-lang/rust-clippy/pull/9214)
 +* [`extra_unused_lifetimes`]: No longer triggers in derive macros
 +  [#9037](https://github.com/rust-lang/rust-clippy/pull/9037)
 +* [`mismatching_type_param_order`]: Now ignores complicated generic parameters
 +  [#9146](https://github.com/rust-lang/rust-clippy/pull/9146)
 +* [`equatable_if_let`]: No longer lints in macros
 +  [#9074](https://github.com/rust-lang/rust-clippy/pull/9074)
 +* [`new_without_default`]: Now ignores generics and lifetime parameters on `fn new`
 +  [#9115](https://github.com/rust-lang/rust-clippy/pull/9115)
 +* [`needless_borrow`]: Now ignores cases that result in the execution of different traits
 +  [#9096](https://github.com/rust-lang/rust-clippy/pull/9096)
 +* [`declare_interior_mutable_const`]: No longer triggers in thread-local initializers
 +  [#9246](https://github.com/rust-lang/rust-clippy/pull/9246)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`type_repetition_in_bounds`]: The suggestion now works with maybe bounds
 +  [#9132](https://github.com/rust-lang/rust-clippy/pull/9132)
 +* [`transmute_ptr_to_ref`]: Now suggests `pointer::cast` when possible
 +  [#8939](https://github.com/rust-lang/rust-clippy/pull/8939)
 +* [`useless_format`]: Now suggests the correct variable name
 +  [#9237](https://github.com/rust-lang/rust-clippy/pull/9237)
 +* [`or_fun_call`]: The lint emission will now only span over the `unwrap_or` call
 +  [#9144](https://github.com/rust-lang/rust-clippy/pull/9144)
 +* [`neg_multiply`]: Now suggests adding parentheses around suggestion if needed
 +  [#9026](https://github.com/rust-lang/rust-clippy/pull/9026)
 +* [`unnecessary_lazy_evaluations`]: Now suggest for `bool::then_some` for lazy evaluation
 +  [#9099](https://github.com/rust-lang/rust-clippy/pull/9099)
 +* [`manual_flatten`]: Improved message for long code snippets
 +  [#9156](https://github.com/rust-lang/rust-clippy/pull/9156)
 +* [`explicit_counter_loop`]: The suggestion is now machine applicable
 +  [#9149](https://github.com/rust-lang/rust-clippy/pull/9149)
 +* [`needless_borrow`]: Now keeps parentheses around fields, when needed
 +  [#9210](https://github.com/rust-lang/rust-clippy/pull/9210)
 +* [`while_let_on_iterator`]: The suggestion now works in `FnOnce` closures
 +  [#9134](https://github.com/rust-lang/rust-clippy/pull/9134)
 +
 +### ICE Fixes
 +
 +* Fix ICEs related to `#![feature(generic_const_exprs)]` usage
 +  [#9241](https://github.com/rust-lang/rust-clippy/pull/9241)
 +* Fix ICEs related to reference lints
 +  [#9093](https://github.com/rust-lang/rust-clippy/pull/9093)
 +* [`question_mark`]: Fix ICE on zero field tuple structs
 +  [#9244](https://github.com/rust-lang/rust-clippy/pull/9244)
 +
 +### Documentation Improvements
 +
 +* [`needless_option_take`]: Now includes a "What it does" and "Why is this bad?" section.
 +  [#9022](https://github.com/rust-lang/rust-clippy/pull/9022)
 +
 +### Others
 +
 +* Using `--cap-lints=allow` and only `--force-warn`ing some will now work with Clippy's driver
 +  [#9036](https://github.com/rust-lang/rust-clippy/pull/9036)
 +* Clippy now tries to read the `rust-version` from `Cargo.toml` to identify the
 +  minimum supported rust version
 +  [#8774](https://github.com/rust-lang/rust-clippy/pull/8774)
 +
 +## Rust 1.63
 +
 +Released 2022-08-11
 +
 +[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
 +
 +### New Lints
 +
 +* [`borrow_deref_ref`]
 +  [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
 +* [`doc_link_with_quotes`]
 +  [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
 +* [`no_effect_replace`]
 +  [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
 +* [`rc_clone_in_vec_init`]
 +  [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
 +* [`derive_partial_eq_without_eq`]
 +  [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
 +* [`mismatching_type_param_order`]
 +  [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
 +* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
 +* [`unused_rounding`]
 +  [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
 +* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
 +* [`swap_ptr_to_ref`]
 +  [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
 +* [`almost_complete_letter_range`]
 +  [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
 +* [`needless_parens_on_range_literals`]
 +  [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
 +* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
 +
 +### Moves and Deprecations
 +
 +* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
 +  `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
 +
 +### Enhancements
 +
 +* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
 +  [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
 +* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
 +  [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
 +* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
 +  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
 +* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
 +  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
 +* [`disallowed_methods`]: Now also lints indirect usages
 +  [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
 +* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
 +  [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
 +* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
 +  [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
 +* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
 +  [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
 +* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
 +  [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
 +* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
 +  [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
 +* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
 +  method chains inside `map`
 +  [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
 +* [`needless_return`]: Now also lints on macro expressions in return statements
 +  [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
 +* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
 +  should extend the default and not replace it
 +  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
 +* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
 +  config should extend the default and not replace it
 +  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
 +* [`never_loop`]: Now checks for `continue` in struct expression
 +  [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
 +
 +### False Positive Fixes
 +
 +* [`useless_transmute`]: No longer lints on types with erased regions
 +  [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
 +* [`vec_init_then_push`]: No longer lints when further extended
 +  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
 +* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
 +  [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
 +* [`redundant_allocation`]: No longer lints on fat pointers that would become
 +  thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
 +* [`derive_partial_eq_without_eq`]:
 +    * Handle differing predicates applied by `#[derive(PartialEq)]` and
 +      `#[derive(Eq)]`
 +      [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
 +    * No longer lints on non-public types and better handles generics
 +      [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
 +* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
 +  string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
 +* [`branches_sharing_code`]: No longer lints when using different binding names
 +  [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
 +* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
 +  desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
 +* [`checked_conversions`]: No longer lints in `const` contexts
 +  [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
 +* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
 +  `T::Item` doesn't implement `IntoIterator`
 +  [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
 +  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
 +* [`manual_range_contains`]: Fix suggestion for integers with different signs
 +  [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
 +* [`identity_op`]: Add parenthesis to suggestions where required
 +  [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
 +* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
 +  [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
 +* [`rc_clone_in_vec_init`]: Add suggestion
 +  [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
 +* The "unknown field" error messages for config files now wraps the field names
 +  [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
 +* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
 +  [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
 +* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
 +  references and not trivially clone-able
 +  [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
 +* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
 +  `iter_mut()` or `into_iter()`
 +  [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
 +  [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
 +* Fix ICEs on callable `static`/`const`s
 +  [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
 +* [`needless_late_init`]
 +  [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
 +* Fix ICE in shadow lints
 +  [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
 +
 +### Documentation Improvements
 +
 +* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
 +  [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
 +* Add a *copy lint name*-button to Clippy's lint list
 +  [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
 +* Display past names of renamed lints on Clippy's lint list
 +  [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
 +* Add the ability to show the lint output in the lint list
 +  [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
 +
 +## Rust 1.62
 +
 +Released 2022-06-30
 +
 +[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
 +
 +### New Lints
 +
 +* [`large_include_file`]
 +  [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
 +* [`cast_abs_to_unsigned`]
 +  [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
 +* [`err_expect`]
 +  [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
 +* [`unnecessary_owned_empty_strings`]
 +  [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
 +* [`empty_structs_with_brackets`]
 +  [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
 +* [`crate_in_macro_def`]
 +  [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
 +* [`needless_option_take`]
 +  [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
 +* [`bytes_count_to_len`]
 +  [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
 +* [`is_digit_ascii_radix`]
 +  [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
 +* [`await_holding_invalid_type`]
 +  [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
 +* [`trim_split_whitespace`]
 +  [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
 +* [`pub_use`]
 +  [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
 +* [`format_push_string`]
 +  [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
 +* [`empty_drop`]
 +  [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
 +* [`drop_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +* [`forget_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +
 +### Moves and Deprecations
 +
 +* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
 +  [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
 +* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
 +  [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
 +
 +### Enhancements
 +
 +* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`map_identity`]: Now checks for needless `map_err`
 +  [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
 +* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
 +  [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
 +* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
 +  [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
 +* [`identity_op`]: Now checks for modulo expressions
 +  [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
 +* [`panic`]: No longer lint in constant context
 +  [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
 +* [`manual_split_once`]: Now lints manual iteration of `splitn`
 +  [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
 +* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
 +  [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
 +* [`unsound_collection_transmute`]: Now has better size and alignment checks
 +  [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
 +* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
 +  [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
 +
 +### False Positive Fixes
 +
 +* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
 +  [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
 +* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
 +  changes the drop order significantly
 +  [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
 +* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
 +  [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
 +* [`init_numbered_fields`]: No longer lints type aliases
 +  [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
 +* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
 +  [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
 +* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
 +  [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
 +* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
 +  [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
 +* [`needless_match`]: Now checks for more complex types and ignores type coercion
 +  [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
 +* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
 +  [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
 +* [`indexing_slicing`]: Fix false positives with constant indices in
 +  [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
 +* [`iter_with_drain`]: Now ignores iterator references
 +  [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
 +* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
 +  [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
 +* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
 +  [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
 +* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
 +  [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
 +* [`mut_from_ref`]: Only lint in unsafe code
 +  [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
 +* [`redundant_pub_crate`]: Now allows macro exports
 +  [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
 +* [`needless_match`]: Ignores cases where the else block expression is different
 +  [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
 +* [`transmute_int_to_char`]: Now allows transmutations in `const` code
 +  [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
 +* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
 +  [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
 +* [`redundant_closure`]: Now ignores coerced closure
 +  [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
 +* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
 +  [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
 +* [`let_unit_value`]: Now ignores cases which are used for type inference
 +  [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`bytes_nth`]: Fix typos in the diagnostic message
 +  [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
 +* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
 +  [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
 +* [`single_element_loop`]: Improve lint for Edition 2021 arrays
 +  [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
 +* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
 +  [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
 +* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
 +  [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
 +* [`collapsible_else_if`]: Add whitespace in suggestion
 +  [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
 +* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
 +  [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
 +* [`map_clone`]: Improve message and suggestion based on the msrv
 +  [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
 +* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
 +  [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
 +
 +### ICE Fixes
 +
 +* [`only_used_in_recursion`]
 +  [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
 +* [`cast_slice_different_sizes`]
 +  [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
 +* [`iter_overeager_cloned`]
 +  [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
 +* [`undocumented_unsafe_blocks`]
 +  [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
 +
 +## Rust 1.61
 +
 +Released 2022-05-19
 +
 +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
 +
 +### New Lints
 +
 +* [`only_used_in_recursion`]
 +  [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
 +* [`cast_enum_truncation`]
 +  [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
 +* [`missing_spin_loop`]
 +  [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
 +* [`deref_by_slicing`]
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`needless_match`]
 +  [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
 +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
 +  [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
 +* [`print_in_format_impl`]
 +  [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
 +* [`unnecessary_find_map`]
 +  [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
 +* [`or_then_unwrap`]
 +  [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
 +* [`unnecessary_join`]
 +  [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
 +* [`iter_with_drain`]
 +  [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
 +* [`cast_enum_constructor`]
 +  [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
 +* [`cast_slice_different_sizes`]
 +  [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
 +
 +### Moves and Deprecations
 +
 +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
 +  [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
 +* Moved [`try_err`] to `restriction`
 +  [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
 +* Move [`iter_with_drain`] to `nursery`
 +  [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
 +* Renamed `to_string_in_display` to [`recursive_format_impl`]
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### Enhancements
 +
 +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
 +  [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
 +* [`ptr_as_ptr`]: Now works inside macros
 +  [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
 +* [`use_self`]: Now works for variants in match expressions
 +  [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
 +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
 +  [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
 +* [`recursive_format_impl`]: Now checks for format calls on `self`
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### False Positive Fixes
 +
 +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
 +  [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
 +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
 +  generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
 +  [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
 +  [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
 +  [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
 +  [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
 +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
 +  lint `match` expressions with `cfg`ed arms
 +  [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
 +* [`single_component_path_imports`]: No longer lint on macros
 +  [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
 +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
 +  [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
 +* [`needless_borrow`]: No longer lints for method calls
 +  [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
 +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
 +  [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
 +* [`default_trait_access`]: Now allows `Default::default` in update expressions
 +  [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_slicing`]: Fixed suggestion for a method calls
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`map_flatten`]: Long suggestions will now be split up into two help messages
 +  [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
 +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
 +  [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
 +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
 +  [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
 +* [`search_is_some`]: More suggestions are now `MachineApplicable`
 +  [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
 +
 +### Documentation Improvements
 +
 +* [`new_without_default`]: Document `pub` requirement for the struct and fields
 +  [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
 +
 +## Rust 1.60
 +
 +Released 2022-04-07
 +
 +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
 +
 +### New Lints
 +
 +* [`single_char_lifetime_names`]
 +  [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
 +* [`iter_overeager_cloned`]
 +  [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
 +* [`transmute_undefined_repr`]
 +  [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
 +* [`default_union_representation`]
 +  [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
 +* [`manual_bits`]
 +  [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
 +* [`borrow_as_ptr`]
 +  [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
 +
 +### Moves and Deprecations
 +
 +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
 +  [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
 +* Rename `ref_in_deref` to [`needless_borrow`]
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
 +  [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
 +
 +### Enhancements
 +
 +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
 +  [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
 +* [`unused_io_amount`]: Now supports async read and write traits
 +  [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
 +* [`while_let_on_iterator`]: Improved detection to catch more cases
 +  [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
 +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
 +  [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
 +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
 +  [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
 +* [`map_clone`]: The suggestion takes `msrv` into account
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`disallowed_methods`]: Now works for methods on primitive types
 +  [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
 +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
 +  [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
 +* [`needless_question_mark`]: Now works for async functions
 +  [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
 +* [`iter_not_returning_iterator`]: Now handles type projections
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
 +  [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
 +* [`single_match`]: Now works for `match` statements with tuples
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +
 +### False Positive Fixes
 +
 +* [`erasing_op`]: No longer triggers if the output type changes
 +  [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
 +* [`if_same_then_else`]: No longer triggers for `if let` statements
 +  [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
 +* [`manual_memcpy`]: No longer lints on `VecDeque`
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`trait_duplication_in_bounds`]: Now takes path segments into account
 +  [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
 +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
 +  [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
 +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
 +  [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
 +* [`ptr_arg`]: No longer lint for mutable references in traits
 +  [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
 +* [`implicit_clone`]: No longer lints for double references
 +  [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
 +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
 +  [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
 +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
 +  [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
 +* [`enum_variant_names`]: No longer triggers for empty variant names
 +  [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
 +* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
 +  [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
 +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
 +  [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
 +* [`useless_format`]: Now works for implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* [`chars_next_cmp`]: Correctly escapes the suggestion
 +  [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
 +* [`explicit_write`]: Add suggestions for `write!`s with format arguments
 +  [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
 +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`or_fun_call`]: Improved suggestion display for long arguments
 +  [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
 +* [`unnecessary_cast`]: Now correctly includes the sign
 +  [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
 +* [`cmp_owned`]: No longer flips the comparison order
 +  [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
 +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
 +  [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
 +
 +### ICE Fixes
 +
 +* [`manual_split_once`]
 +  [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
 +
 +### Documentation Improvements
 +
 +* [`map_flatten`]: Add documentation for the `Option` type
 +  [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
 +* Document that Clippy's driver might use a different code generation than rustc
 +  [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
 +* Clippy's lint list will now automatically focus the search box
 +  [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
 +
 +### Others
 +
 +* Clippy now warns if we find multiple Clippy config files exist
 +  [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
 +
 +## Rust 1.59
 +
 +Released 2022-02-24
 +
 +[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
 +
 +### New Lints
 +
 +* [`index_refutable_slice`]
 +  [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
 +* [`needless_splitn`]
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* [`unnecessary_to_owned`]
 +  [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
 +* [`needless_late_init`]
 +  [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
 +* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
 +* [`return_self_not_must_use`]
 +  [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
 +* [`init_numbered_fields`]
 +  [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
 +
 +### Moves and Deprecations
 +
 +* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
 +  allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
 +* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
 +  [`disallowed_methods`]
 +  [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
 +* Move [`map_flatten`] to `complexity` (now warn-by-default)
 +  [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
 +
 +### Enhancements
 +
 +* [`match_overlapping_arm`]: Fix false negative where after included ranges,
 +  overlapping ranges weren't linted anymore
 +  [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
 +* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
 +  [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
 +* [`cast_lossless`]: Now also lints for `bool` to integer casts
 +  [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
 +* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
 +  [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
 +* [`needless_borrow`]
 +  [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
 +    * Lint when a borrow is auto-dereffed more than once
 +    * Lint in the trailing expression of a block for a match arm
 +* [`strlen_on_c_strings`]
 +  [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
 +    * Lint when used without a fully-qualified path
 +    * Suggest removing the surrounding unsafe block when possible
 +* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
 +  [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
 +* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
 +  `rsplit_once`, `replace`, and `replacen`
 +  [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
 +* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
 +  `Vec::new`, `HashSet::new`, and `HashMap::new`
 +  [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
 +* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
 +  [`shadow_unrelated`]
 +  [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
 +
 +### False Positive Fixes
 +
 +* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
 +  cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
 +  [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
 +* [`manual_split_once`]: No longer suggests code changing the original behavior
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
 +  implementing `FnOnce`
 +  [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
 +* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
 +  triggered on `let-else` statements
 +  [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
 +* [`if_then_some_else_none`]: No longer lints if there is an early return
 +  [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
 +* [`needless_collect`]: No longer suggests removal of `collect` when removal
 +  would create code requiring mutably borrowing a value multiple times
 +  [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
 +* [`shadow_same`]: Fix false positive for `async` function's params
 +  [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
 +* [`suboptimal_flops`]: No longer triggers in constant functions
 +  [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
 +* [`type_complexity`]: No longer lints on associated types in traits
 +  [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
 +* [`question_mark`]: No longer lints if returned object is not local
 +  [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
 +* [`option_if_let_else`]: No longer lint on complex sub-patterns
 +  [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
 +* [`blocks_in_if_conditions`]: No longer lints on empty closures
 +  [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
 +* [`enum_variant_names`]: No longer lint when first prefix is only a substring
 +  of a camel-case word
 +  [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
 +* [`identity_op`]: Only lint on integral operands
 +  [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
 +  [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
 +* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
 +  and adapts the suggestion accordingly
 +  [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
 +* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
 +  [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
 +* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
 +  types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
 +* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
 +  [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
 +* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
 +  [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
 +* [`option_if_let_else`]: No longer expands macros in the suggestion
 +  [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
 +* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
 +  [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
 +* [`doc_markdown`]: No longer uses inline hints to improve readability of
 +  suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
 +* [`needless_question_mark`]: Now better explains the suggestion
 +  [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
 +* [`single_char_pattern`]: Escape backslash `\` in suggestion
 +  [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
 +* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
 +  [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
 +* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
 +  apply this lints suggestion
 +  [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
 +* [`neg_multiply`]: Now produces a suggestion
 +  [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
 +* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
 +  in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
 +* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
 +  `to_radians` and `to_degrees`
 +  [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
 +
 +### ICE Fixes
 +
 +* [`undocumented_unsafe_blocks`]
 +  [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
 +  [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
 +* [`unnecessary_cast`]
 +  [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
 +
 +### Documentation Improvements
 +
 +* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
 +  can be changed crate-wide
 +  [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
 +* Added a note to the `README` that config changes don't apply to already
 +  compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
 +  the version a lint was added. :tada:
 +  [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
 +* New and improved issue templates
 +  [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
 +* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
 +  file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
 +
 +## Rust 1.58
 +
 +Released 2022-01-13
 +
 +[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
 +
 +### Rust 1.58.1
 +
 +* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
 +  [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
 +* [`useless_format`]: Handle implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### New lints
 +
 +* [`transmute_num_to_bytes`]
 +  [#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
 +* [`match_str_case_mismatch`]
 +  [#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
 +* [`format_in_format_args`], [`to_string_in_format_args`]
 +  [#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
 +* [`uninit_vec`]
 +  [#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
 +* [`fn_to_numeric_cast_any`]
 +  [#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
 +* [`undocumented_unsafe_blocks`]
 +  [#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
 +* [`trailing_empty_array`]
 +  [#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
 +* [`string_slice`]
 +  [#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
 +
 +### Moves or deprecations of lints
 +
 +* Move [`non_send_fields_in_send_ty`] to `suspicious`
 +  [#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
 +* Move [`non_ascii_literal`] to `restriction`
 +  [#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
 +
 +### Changes that expand what code existing lints cover
 +
 +* [`question_mark`] now covers `Result`
 +  [#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
 +* Make [`useless_format`] recognize bare `format!("")`
 +  [#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
 +* Lint on underscored variables with no side effects in [`no_effect`]
 +  [#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
 +* Expand [`match_ref_pats`] to check for multiple reference patterns
 +  [#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
 +
 +### False positive fixes
 +
 +* Fix false positive of [`implicit_saturating_sub`] with `else` clause
 +  [#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
 +* Fix [`question_mark`] when there is call in conditional predicate
 +  [#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
 +* [`mut_mut`] no longer lints when type is defined in external macros
 +  [#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
 +* Avoid [`eq_op`] in test functions
 +  [#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
 +* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
 +  method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
 +* [`match_str_case_mismatch`] no longer lints on uncased characters
 +  [#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
 +* [`ptr_arg`] no longer lints references to type aliases
 +  [#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
 +* [`missing_safety_doc`] now also accepts "implementation safety" headers
 +  [#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
 +* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
 +  attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
 +* [`if_not_else`] now ignores else-if statements
 +  [#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
 +* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
 +  [#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
 +* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
 +  involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
 +* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
 +  [#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
 +* Fix false positive in [`match_overlapping_arm`]
 +  [#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
 +* Prevent [`needless_lifetimes`] false positive in `async` function definition
 +  [#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
 +
 +### Suggestion fixes/improvements
 +
 +* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
 +  [#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
 +* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
 +  lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
 +* [`equatable_if_let`] no longer expands macros in the suggestion
 +  [#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
 +* Make [`shadow_reuse`] suggestion less verbose
 +  [#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
 +
 +### ICE fixes
 +
 +* Fix ICE in [`enum_variant_names`]
 +  [#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
 +* Fix ICE in [`undocumented_unsafe_blocks`]
 +  [#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
 +
 +### Documentation improvements
 +
 +* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
 +  [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
 +  [#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
 +* Fix typo in example for [`match_result_ok`]
 +  [#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
 +
 +### Others
 +
 +* Allow giving reasons for [`disallowed_types`]
 +  [#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
 +* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
 +  2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
 +* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
 +  loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
 +* Added a new configuration `literal-suffix-style` to enforce a certain style
 +  writing [`unseparated_literal_suffix`]
 +  [#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
 +
 +## Rust 1.57
 +
 +Released 2021-12-02
 +
 +[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
 +
 +### New Lints
 +
 +* [`negative_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`redundant_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`mod_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`self_named_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`manual_split_once`]
 +  [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
 +* [`derivable_impls`]
 +  [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
 +* [`needless_option_as_deref`]
 +  [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
 +* [`iter_not_returning_iterator`]
 +  [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
 +* [`same_name_method`]
 +  [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
 +* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
 +* [`non_send_fields_in_send_ty`]
 +  [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
 +* [`equatable_if_let`]
 +  [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
 +
 +### Moves and Deprecations
 +
 +* Move [`shadow_unrelated`] to `restriction`
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* Move [`option_if_let_else`] to `nursery`
 +  [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
 +* Move [`branches_sharing_code`] to `nursery`
 +  [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
 +* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
 +  `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
 +* Move [`many_single_char_names`] to `pedantic`
 +  [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
 +* Move [`float_cmp`] to `pedantic`
 +  [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
 +* Rename `box_vec` to [`box_collection`] and lint on more general cases
 +  [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
 +* Uplift `invalid_atomic_ordering` to rustc
 +  [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
 +
 +### Enhancements
 +
 +* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
 +  limited to certain patterns
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* The `avoid-breaking-exported-api` configuration now also works for
 +  [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
 +  [`option_option`], [`linkedlist`], [`rc_mutex`]
 +  [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
 +* [`unnecessary_unwrap`]: Now also checks for `expect`s
 +  [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
 +* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
 +  lint message
 +  [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
 +* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
 +  [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
 +* [`approx_constant`]: Add `TAU`
 +  [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
 +* [`needless_borrow`]: Now also lints on needless mutable borrows
 +  [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
 +* [`missing_safety_doc`]: Now also lints on unsafe traits
 +  [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
 +
 +### False Positive Fixes
 +
 +* [`manual_map`]: No longer lints when the option is borrowed in the match and
 +  also consumed in the arm
 +  [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
 +* [`filter_next`]: No longer lints if `filter` method is not the
 +  `Iterator::filter` method
 +  [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
 +* [`manual_flatten`]: No longer lints if expression is used after `if let`
 +  [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
 +* [`option_if_let_else`]: Multiple fixes
 +  [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
 +    * `break` and `continue` statements local to the would-be closure are
 +      allowed
 +    * Don't lint in const contexts
 +    * Don't lint when yield expressions are used
 +    * Don't lint when the captures made by the would-be closure conflict with
 +      the other branch
 +    * Don't lint when a field of a local is used when the type could be
 +      potentially moved from
 +    * In some cases, don't lint when scrutinee expression conflicts with the
 +      captures of the would-be closure
 +* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
 +  wide pointers with thin pointers
 +  [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
 +* [`bool_assert_comparison`]: No longer lints on types that do not implement the
 +  `Not` trait with `Output = bool`
 +  [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
 +* [`mut_range_bound`]: No longer lints on range bound mutations, that are
 +  immediately followed by a `break;`
 +  [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
 +* [`mutable_key_type`]: Improve accuracy and document remaining false positives
 +  and false negatives
 +  [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
 +* [`redundant_closure`]: Rewrite the lint to fix various false positives and
 +  false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
 +* [`large_enum_variant`]: No longer wrongly identifies the second largest
 +  variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
 +* [`needless_return`]: No longer lints on let-else expressions
 +  [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
 +* [`suspicious_else_formatting`]: No longer lints in proc-macros
 +  [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
 +* [`excessive_precision`]: No longer lints when in some cases the float was
 +  already written in the shortest form
 +  [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
 +* [`doc_markdown`]: No longer lints on intra-doc links
 +  [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
 +  function call in an indexing operation
 +  [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
 +* [`manual_split_once`]: Produce semantically equivalent suggestion when
 +  `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
 +* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
 +  [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
 +* [`manual_assert`]: No better handles complex conditions
 +  [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
 +* Correctly handle signs in exponents in numeric literals lints
 +  [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
 +* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
 +  [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
 +* Drop exponent from suggestion if it is 0 in numeric literals lints
 +  [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
 +
 +### ICE Fixes
 +
 +* [`implicit_hasher`]
 +  [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
 +
 +### Others
 +
 +* Clippy now uses the 2021
 +  [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
 +  [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
 +
 +## Rust 1.56
 +
 +Released 2021-10-21
 +
 +[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
 +
 +### New Lints
 +
 +* [`unwrap_or_else_default`]
 +  [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
 +
 +### Enhancements
 +
 +* [`needless_continue`]: Now also lints in `loop { continue; }` case
 +  [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
 +* [`disallowed_types`]: Now also primitive types can be disallowed
 +  [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
 +* [`manual_swap`]: Now also lints on xor swaps
 +  [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
 +* [`map_flatten`]: Now also lints on the `Result` type
 +  [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
 +* [`no_effect`]: Now also lints on inclusive ranges
 +  [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
 +
 +### False Positive Fixes
 +
 +* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
 +  [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
 +* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
 +  [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
 +* [`similar_names`]: No longer complains about `iter` and `item` being too
 +  similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
 +  [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
 +* [`new_without_default`]: No longer shows the full qualified type path when
 +  suggesting adding a `Default` implementation
 +  [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
 +* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
 +  [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
 +* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
 +  references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
 +* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
 +  [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
 +* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
 +  applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
 +
 +### Documentation Improvements
 +
 +* Clippy now uses a lint to generate its lint documentation. [Lints all the way
 +  down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
 +  [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
 +* Reworked Clippy's website:
 +  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
 +  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
 +  * Added applicability information about lints
 +  * Added a link to jump into the implementation
 +  * Improved loading times
 +  * Adapted some styling
 +* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
 +  [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
 +* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
 +  example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
 +
 +## Rust 1.55
 +
 +Released 2021-09-09
 +
 +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 +
 +### Important Changes
 +
 +* Stabilized `cargo clippy --fix` :tada:
 +  [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
 +
 +### New Lints
 +
 +* [`rc_mutex`]
 +  [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
 +* [`nonstandard_macro_braces`]
 +  [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
 +* [`strlen_on_c_strings`]
 +  [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
 +* [`self_named_constructors`]
 +  [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 +* [`disallowed_script_idents`]
 +  [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
 +* [`disallowed_types`]
 +  [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 +* [`missing_enforced_import_renames`]
 +  [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
 +* [`extend_with_drain`]
 +  [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
 +
 +### Moves and Deprecations
 +
 +* Moved [`from_iter_instead_of_collect`] to `pedantic`
 +  [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
 +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
 +  [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
 +  * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
 +  * Moved [`empty_loop`] to `suspicious`
 +  * Moved [`eval_order_dependence`] to `suspicious`
 +  * Moved [`float_equality_without_abs`] to `suspicious`
 +  * Moved [`for_loops_over_fallibles`] to `suspicious`
 +  * Moved [`misrefactored_assign_op`] to `suspicious`
 +  * Moved [`mut_range_bound`] to `suspicious`
 +  * Moved [`mutable_key_type`] to `suspicious`
 +  * Moved [`suspicious_arithmetic_impl`] to `suspicious`
 +  * Moved [`suspicious_assignment_formatting`] to `suspicious`
 +  * Moved [`suspicious_else_formatting`] to `suspicious`
 +  * Moved [`suspicious_map`] to `suspicious`
 +  * Moved [`suspicious_op_assign_impl`] to `suspicious`
 +  * Moved [`suspicious_unary_op_formatting`] to `suspicious`
 +
 +### Enhancements
 +
 +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
 +  [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
 +* [`doc_markdown`]:
 +  * Now detects unbalanced ticks
 +    [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
 +  * Add `FreeBSD` to the default configuration as an allowed identifier
 +    [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
 +  or hidden variants
 +  [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
 +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
 +  [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
 +* [`disallowed_names`]: Now allows disallowed names in test code
 +  [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 +* [`redundant_closure`]: Suggests `&mut` for `FnMut`
 +  [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
 +* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
 +  no longer require fully qualified paths
 +  [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +## Rust 1.54
 +
 +Released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_methods`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* `ref_in_deref` Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
 +* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`disallowed_names`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
 +[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
 +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
 +[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`as_ptr_cast_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_ptr_cast_mut
 +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
 +[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`bool_to_int_with_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_to_int_with_if
 +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 +[`box_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_default
 +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
 +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 +[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_nan_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_nan_to_int
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
 +[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
 +[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
 +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
 +[`disallowed_macros`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_macros
 +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
 +[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 +[`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
 +[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
 +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_raw_with_void_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_raw_with_void_ptr
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 +[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
 +[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
 +[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
 +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_future`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_future
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 +[`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
 +[`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
 +[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
 +[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
 +[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
 +[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`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
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
 +[`missing_trait_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_trait_methods
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 +[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
 +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
 +[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 +[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 +[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
 +[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
 +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
 +[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
 +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
 +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
 +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
 +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
 +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 +[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
 +[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
 +[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
 +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 +[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
 +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
 +[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
 +[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 +[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 +[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
 +[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
 +[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`seek_from_current`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current
 +[`seek_to_start_instead_of_rewind`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_to_start_instead_of_rewind
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
 +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
 +[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
 +[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`suspicious_xor_used_as_pow`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_xor_used_as_pow
 +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`unchecked_duration_subtraction`]: https://rust-lang.github.io/rust-clippy/master/index.html#unchecked_duration_subtraction
 +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
 +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
 +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
 +[`uninlined_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`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
 +[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 +[`unused_format_specs`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_format_specs
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 +[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
 +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index f74de7de42b8b0d20d5c59b2d71cace4f776c7e6,0000000000000000000000000000000000000000..81254ba8b8b8f7faf163e4be75172a847cfceb4a
mode 100644,000000..100644
--- /dev/null
@@@ -1,257 -1,0 +1,257 @@@
- 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.
 +# Clippy
 +
 +[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto)
 +[![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
 +
 +A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 +
 +[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 +
 +Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 +You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
 +
 +| Category              | Description                                                                         | Default level |
 +| --------------------- | ----------------------------------------------------------------------------------- | ------------- |
 +| `clippy::all`         | all lints that are on by default (correctness, suspicious, style, complexity, perf) | **warn/deny** |
 +| `clippy::correctness` | code that is outright wrong or useless                                              | **deny**      |
 +| `clippy::suspicious`  | code that is most likely wrong or useless                                           | **warn**      |
 +| `clippy::style`       | code that should be written in a more idiomatic way                                 | **warn**      |
 +| `clippy::complexity`  | code that does something simple but in a complex way                                | **warn**      |
 +| `clippy::perf`        | code that can be written to run faster                                              | **warn**      |
 +| `clippy::pedantic`    | lints which are rather strict or have occasional false positives                    | allow         |
 +| `clippy::nursery`     | new lints that are still under development                                          | allow         |
 +| `clippy::cargo`       | lints for the cargo manifest                                                        | allow         |
 +
 +More to come, please [file an issue](https://github.com/rust-lang/rust-clippy/issues) if you have ideas!
 +
 +The [lint list](https://rust-lang.github.io/rust-clippy/master/index.html) also contains "restriction lints", which are
 +for things which are usually not considered "bad", but may be useful to turn on in specific cases. These should be used
 +very selectively, if at all.
 +
 +Table of contents:
 +
 +*   [Usage instructions](#usage)
 +*   [Configuration](#configuration)
 +*   [Contributing](#contributing)
 +*   [License](#license)
 +
 +## Usage
 +
 +Below are instructions on how to use Clippy as a cargo subcommand,
 +in projects that do not use cargo, or in Travis CI.
 +
 +### As a cargo subcommand (`cargo clippy`)
 +
 +One way to use Clippy is by installing Clippy through rustup as a cargo
 +subcommand.
 +
 +#### Step 1: Install Rustup
 +
 +You can install [Rustup](https://rustup.rs/) on supported platforms. This will help
 +us install Clippy and its dependencies.
 +
 +If you already have Rustup installed, update to ensure you have the latest
 +Rustup and compiler:
 +
 +```terminal
 +rustup update
 +```
 +
 +#### Step 2: Install Clippy
 +
 +Once you have rustup and the latest stable release (at least Rust 1.29) installed, run the following command:
 +
 +```terminal
 +rustup component add clippy
 +```
 +If it says that it can't find the `clippy` component, please run `rustup self update`.
 +
 +#### Step 3: Run Clippy
 +
 +Now you can run Clippy by invoking the following command:
 +
 +```terminal
 +cargo clippy
 +```
 +
 +#### Automatically applying Clippy suggestions
 +
 +Clippy can automatically apply some lint suggestions, just like the compiler.
 +
 +```terminal
 +cargo clippy --fix
 +```
 +
 +#### Workspaces
 +
 +All the usual workspace options should work with Clippy. For example the following command
 +will run Clippy on the `example` crate:
 +
 +```terminal
 +cargo clippy -p example
 +```
 +
 +As with `cargo check`, this includes dependencies that are members of the workspace, like path dependencies.
 +If you want to run Clippy **only** on the given crate, use the `--no-deps` option like this:
 +
 +```terminal
 +cargo clippy -p example -- --no-deps
 +```
 +
 +### Using `clippy-driver`
 +
 +Clippy can also be used in projects that do not use cargo. To do so, run `clippy-driver`
 +with the same arguments you use for `rustc`. For example:
 +
 +```terminal
 +clippy-driver --edition 2018 -Cpanic=abort foo.rs
 +```
 +
 +Note that `clippy-driver` is designed for running Clippy only and should not be used as a general
 +replacement for `rustc`. `clippy-driver` may produce artifacts that are not optimized as expected,
 +for example.
 +
 +### Travis CI
 +
 +You can add Clippy to Travis CI in the same way you use it locally:
 +
 +```yml
 +language: rust
 +rust:
 +  - stable
 +  - beta
 +before_script:
 +  - rustup component add clippy
 +script:
 +  - cargo clippy
 +  # if you want the build job to fail when encountering warnings, use
 +  - cargo clippy -- -D warnings
 +  # in order to also check tests and non-default crate features, use
 +  - cargo clippy --all-targets --all-features -- -D warnings
 +  - cargo test
 +  # etc.
 +```
 +
 +Note that adding `-D warnings` will cause your build to fail if **any** warnings are found in your code.
 +That includes warnings found by rustc (e.g. `dead_code`, etc.). If you want to avoid this and only cause
 +an error for Clippy warnings, use `#![deny(clippy::all)]` in your code or `-D clippy::all` on the command
 +line. (You can swap `clippy::all` with the specific lint category you are targeting.)
 +
 +## Configuration
 +
 +### Allowing/denying lints
 +
 +You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
 +
 +*   the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`).
 +    Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html).
 +
 +*   all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
 +    `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive
 +    lints prone to false positives.
 +
 +*   only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
 +
 +*   `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
 +
 +Note: `allow` means to suppress the lint for your code. With `warn` the lint
 +will only emit a warning, while with `deny` the lint will emit an error, when
 +triggering for your code. An error causes clippy to exit with an error code, so
 +is useful in scripts like CI/CD.
 +
 +If you do not want to include your lint levels in your code, you can globally
 +enable/disable lints by passing extra flags to Clippy during the run:
 +
 +To allow `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -A clippy::lint_name
 +```
 +
 +And to warn on `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -W clippy::lint_name
 +```
 +
 +This also works with lint groups. For example, you
 +can run Clippy with warnings for all lints enabled:
 +```terminal
 +cargo clippy -- -W clippy::pedantic
 +```
 +
 +If you care only about a single lint, you can allow all others and then explicitly warn on
 +the lint(s) you are interested in:
 +```terminal
 +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
 +```
 +
 +### Configure the behavior of some lints
 +
 +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a basic `variable =
 +value` mapping e.g.
 +
 +```toml
 +avoid-breaking-exported-api = false
 +disallowed-names = ["toto", "tata", "titi"]
 +cognitive-complexity-threshold = 30
 +```
 +
- The MSRV can also be specified as an inner attribute, like below.
++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**
 +>
 +> `clippy.toml` or `.clippy.toml` cannot be used to allow/deny lints.
 +
 +To deactivate the “for further information visit *lint-link*” message you can
 +define the `CLIPPY_DISABLE_DOCS_LINKS` environment variable.
 +
 +### Specifying the minimum supported Rust version
 +
 +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by
 +specifying the minimum supported Rust version (MSRV) in the clippy configuration file.
 +
 +```toml
 +msrv = "1.30.0"
 +```
 +
 +Alternatively, the [`rust-version` field](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
 +in the `Cargo.toml` can be used.
 +
 +```toml
 +# Cargo.toml
 +rust-version = "1.30"
 +```
 +
++The MSRV can also be specified as an attribute, like below.
 +
 +```rust
 +#![feature(custom_inner_attributes)]
 +#![clippy::msrv = "1.30.0"]
 +
 +fn main() {
 +  ...
 +}
 +```
 +
 +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
 +is equivalent to `msrv = 1.30.0`.
 +
 +Note: `custom_inner_attributes` is an unstable feature, so it has to be enabled explicitly.
 +
 +Lints that recognize this configuration option can be found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
 +
 +## Contributing
 +
 +If you want to contribute to Clippy, you can find more information in [CONTRIBUTING.md](https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md).
 +
 +## License
 +
 +Copyright 2014-2022 The Rust Project Developers
 +
 +Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 +[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
 +<LICENSE-MIT or [https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your
 +option. Files in the project may not be
 +copied, modified, or distributed except according to those terms.
index 0b945faf9b78effe2d1dbd0fc5d6a269c87c50a2,0000000000000000000000000000000000000000..1f0b8db28a152e19c48ccd93f46285036a18e9ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,24 @@@
 +# Summary
 +
 +[Introduction](README.md)
 +
 +- [Installation](installation.md)
 +- [Usage](usage.md)
 +- [Configuration](configuration.md)
 +- [Clippy's Lints](lints.md)
 +- [Continuous Integration](continuous_integration/README.md)
 +    - [GitHub Actions](continuous_integration/github_actions.md)
 +    - [Travis CI](continuous_integration/travis.md)
 +- [Development](development/README.md)
 +    - [Basics](development/basics.md)
 +    - [Adding Lints](development/adding_lints.md)
 +    - [Common Tools](development/common_tools_writing_lints.md)
 +    - [Infrastructure](development/infrastructure/README.md)
 +        - [Syncing changes between Clippy and rust-lang/rust](development/infrastructure/sync.md)
 +        - [Backporting Changes](development/infrastructure/backport.md)
 +        - [Updating the Changelog](development/infrastructure/changelog_update.md)
 +        - [Release a New Version](development/infrastructure/release.md)
 +        - [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)
index 77f1d2e8797a36c2200c5563e0b05085bc3c4b9e,0000000000000000000000000000000000000000..430ff8b739ae861785c528b749728a199f6ee72c
mode 100644,000000..100644
--- /dev/null
@@@ -1,92 -1,0 +1,92 @@@
- 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.
 +# Configuring Clippy
 +
 +> **Note:** The configuration file is unstable and may be deprecated in the future.
 +
 +Some lints can be configured in a TOML file named `clippy.toml` or `.clippy.toml`. It contains a
 +basic `variable = value` mapping eg.
 +
 +```toml
 +avoid-breaking-exported-api = false
 +disallowed-names = ["toto", "tata", "titi"]
 +cognitive-complexity-threshold = 30
 +```
 +
- The MSRV can also be specified as an inner attribute, like below.
++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.
 +
 +### Allowing/denying lints
 +
 +You can add options to your code to `allow`/`warn`/`deny` Clippy lints:
 +
 +* the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`)
 +
 +* all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`,
 +  `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive lints prone to false
 +  positives.
 +
 +* only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.)
 +
 +* `allow`/`warn`/`deny` can be limited to a single function or module using `#[allow(...)]`, etc.
 +
 +Note: `allow` means to suppress the lint for your code. With `warn` the lint will only emit a warning, while with `deny`
 +the lint will emit an error, when triggering for your code. An error causes clippy to exit with an error code, so is
 +useful in scripts like CI/CD.
 +
 +If you do not want to include your lint levels in your code, you can globally enable/disable lints by passing extra
 +flags to Clippy during the run:
 +
 +To allow `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -A clippy::lint_name
 +```
 +
 +And to warn on `lint_name`, run
 +
 +```terminal
 +cargo clippy -- -W clippy::lint_name
 +```
 +
 +This also works with lint groups. For example you can run Clippy with warnings for all lints enabled:
 +
 +```terminal
 +cargo clippy -- -W clippy::pedantic
 +```
 +
 +If you care only about a single lint, you can allow all others and then explicitly warn on the lint(s) you are
 +interested in:
 +
 +```terminal
 +cargo clippy -- -A clippy::all -W clippy::useless_format -W clippy::...
 +```
 +
 +### Specifying the minimum supported Rust version
 +
 +Projects that intend to support old versions of Rust can disable lints pertaining to newer features by specifying the
 +minimum supported Rust version (MSRV) in the clippy configuration file.
 +
 +```toml
 +msrv = "1.30.0"
 +```
 +
++The MSRV can also be specified as an attribute, like below.
 +
 +```rust
 +#![feature(custom_inner_attributes)]
 +#![clippy::msrv = "1.30.0"]
 +
 +fn main() {
 +    ...
 +}
 +```
 +
 +You can also omit the patch version when specifying the MSRV, so `msrv = 1.30`
 +is equivalent to `msrv = 1.30.0`.
 +
 +Note: `custom_inner_attributes` is an unstable feature so it has to be enabled explicitly.
 +
 +Lints that recognize this configuration option can be
 +found [here](https://rust-lang.github.io/rust-clippy/master/index.html#msrv)
index 3c3f368a529b15755ca687375d1becf2a635a8a0,0000000000000000000000000000000000000000..8b4eee8c9d94d3b3f6ddaaa72507a59de8149d70
mode 100644,000000..100644
--- /dev/null
@@@ -1,759 -1,0 +1,755 @@@
-     msrv: Option<RustcVersion>,
 +# Adding a new lint
 +
 +You are probably here because you want to add a new lint to Clippy. If this is
 +the first time you're contributing to Clippy, this document guides you through
 +creating an example lint from scratch.
 +
 +To get started, we will create a lint that detects functions called `foo`,
 +because that's clearly a non-descriptive name.
 +
 +- [Adding a new lint](#adding-a-new-lint)
 +  - [Setup](#setup)
 +  - [Getting Started](#getting-started)
 +    - [Defining Our Lint](#defining-our-lint)
 +      - [Standalone](#standalone)
 +      - [Specific Type](#specific-type)
 +      - [Tests Location](#tests-location)
 +  - [Testing](#testing)
 +    - [Cargo lints](#cargo-lints)
 +  - [Rustfix tests](#rustfix-tests)
 +  - [Testing manually](#testing-manually)
 +  - [Lint declaration](#lint-declaration)
 +  - [Lint registration](#lint-registration)
 +  - [Lint passes](#lint-passes)
 +  - [Emitting a lint](#emitting-a-lint)
 +  - [Adding the lint logic](#adding-the-lint-logic)
 +  - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
 +  - [Author lint](#author-lint)
 +  - [Print HIR lint](#print-hir-lint)
 +  - [Documentation](#documentation)
 +  - [Running rustfmt](#running-rustfmt)
 +  - [Debugging](#debugging)
 +  - [PR Checklist](#pr-checklist)
 +  - [Adding configuration to a lint](#adding-configuration-to-a-lint)
 +  - [Cheat Sheet](#cheat-sheet)
 +
 +## Setup
 +
 +See the [Basics](basics.md#get-the-code) documentation.
 +
 +## Getting Started
 +
 +There is a bit of boilerplate code that needs to be set up when creating a new
 +lint. Fortunately, you can use the Clippy dev tools to handle this for you. We
 +are naming our new lint `foo_functions` (lints are generally written in snake
 +case), and we don't need type information, so it will have an early pass type
 +(more on this later). If you're unsure if the name you chose fits the lint,
 +take a look at our [lint naming guidelines][lint_naming].
 +
 +## Defining Our Lint
 +To get started, there are two ways to define our lint.
 +
 +### Standalone
 +Command: `cargo dev new_lint --name=foo_functions --pass=early --category=pedantic`
 +(category will default to nursery if not provided)
 +
 +This command will create a new file: `clippy_lints/src/foo_functions.rs`, as well
 +as [register the lint](#lint-registration).
 +
 +### Specific Type
 +Command: `cargo dev new_lint --name=foo_functions --type=functions --category=pedantic`
 +
 +This command will create a new file: `clippy_lints/src/{type}/foo_functions.rs`.
 +
 +Notice how this command has a `--type` flag instead of `--pass`. Unlike a standalone
 +definition, this lint won't be registered in the traditional sense. Instead, you will
 +call your lint from within the type's lint pass, found in `clippy_lints/src/{type}/mod.rs`.
 +
 +A "type" is just the name of a directory in `clippy_lints/src`, like `functions` in
 +the example command. These are groupings of lints with common behaviors, so if your
 +lint falls into one, it would be best to add it to that type.
 +
 +### Tests Location
 +Both commands will create a file: `tests/ui/foo_functions.rs`. For cargo lints,
 +two project hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
 +
 +Next, we'll open up these files and add our lint!
 +
 +## Testing
 +
 +Let's write some tests first that we can execute while we iterate on our lint.
 +
 +Clippy uses UI tests for testing. UI tests check that the output of Clippy is
 +exactly as expected. Each test is just a plain Rust file that contains the code
 +we want to check. The output of Clippy is compared against a `.stderr` file.
 +Note that you don't have to create this file yourself, we'll get to generating
 +the `.stderr` files further down.
 +
 +We start by opening the test file created at `tests/ui/foo_functions.rs`.
 +
 +Update the file with some examples to get started:
 +
 +```rust
 +#![allow(unused)]
 +#![warn(clippy::foo_functions)]
 +
 +// Impl methods
 +struct A;
 +impl A {
 +    pub fn fo(&self) {}
 +    pub fn foo(&self) {}
 +    pub fn food(&self) {}
 +}
 +
 +// Default trait methods
 +trait B {
 +    fn fo(&self) {}
 +    fn foo(&self) {}
 +    fn food(&self) {}
 +}
 +
 +// Plain functions
 +fn fo() {}
 +fn foo() {}
 +fn food() {}
 +
 +fn main() {
 +    // We also don't want to lint method calls
 +    foo();
 +    let a = A;
 +    a.foo();
 +}
 +```
 +
 +Now we can run the test with `TESTNAME=foo_functions cargo uitest`, currently
 +this test is meaningless though.
 +
 +While we are working on implementing our lint, we can keep running the UI test.
 +That allows us to check if the output is turning into what we want.
 +
 +Once we are satisfied with the output, we need to run `cargo dev bless` to
 +update the `.stderr` file for our lint. Please note that, we should run
 +`TESTNAME=foo_functions cargo uitest` every time before running `cargo dev
 +bless`. Running `TESTNAME=foo_functions cargo uitest` should pass then. When we
 +commit our lint, we need to commit the generated `.stderr` files, too. In
 +general, you should only commit files changed by `cargo dev bless` for the
 +specific lint you are creating/editing. Note that if the generated files are
 +empty, they should be removed.
 +
 +> _Note:_ you can run multiple test files by specifying a comma separated list:
 +> `TESTNAME=foo_functions,test2,test3`.
 +
 +### Cargo lints
 +
 +For cargo lints, the process of testing differs in that we are interested in the
 +`Cargo.toml` manifest file. We also need a minimal crate associated with that
 +manifest.
 +
 +If our new lint is named e.g. `foo_categories`, after running `cargo dev
 +new_lint` we will find by default two new crates, each with its manifest file:
 +
 +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the
 +  new lint to raise an error.
 +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger
 +  the lint.
 +
 +If you need more cases, you can copy one of those crates (under
 +`foo_categories`) and rename it.
 +
 +The process of generating the `.stderr` file is the same, and prepending the
 +`TESTNAME` variable to `cargo uitest` works too.
 +
 +## Rustfix tests
 +
 +If the lint you are working on is making use of structured suggestions, the test
 +file should include a `// run-rustfix` comment at the top. This will
 +additionally run [rustfix] for that test. Rustfix will apply the suggestions
 +from the lint to the code of the test file and compare that to the contents of a
 +`.fixed` file.
 +
 +Use `cargo dev bless` to automatically generate the `.fixed` file after running
 +the tests.
 +
 +[rustfix]: https://github.com/rust-lang/rustfix
 +
 +## Testing manually
 +
 +Manually testing against an example file can be useful if you have added some
 +`println!`s and the test suite output becomes unreadable. To try Clippy with
 +your local modifications, run
 +
 +```
 +cargo dev lint input.rs
 +```
 +
 +from the working copy root. With tests in place, let's have a look at
 +implementing our lint now.
 +
 +## Lint declaration
 +
 +Let's start by opening the new file created in the `clippy_lints` crate at
 +`clippy_lints/src/foo_functions.rs`. That's the crate where all the lint code
 +is. This file has already imported some initial things we will need:
 +
 +```rust
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_ast::ast::*;
 +```
 +
 +The next step is to update the lint declaration. Lints are declared using the
 +[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
 +the auto-generated lint declaration to have a real description, something like
 +this:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +* The section of lines prefixed with `///` constitutes the lint documentation
 +  section. This is the default documentation style and will be displayed [like
 +  this][example_lint_page]. To render and open this documentation locally in a
 +  browser, run `cargo dev serve`.
 +* The `#[clippy::version]` attribute will be rendered as part of the lint
 +  documentation. The value should be set to the current Rust version that the
 +  lint is developed in, it can be retrieved by running `rustc -vV` in the
 +  rust-clippy directory. The version is listed under *release*. (Use the version
 +  without the `-nightly`) suffix.
 +* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the [lint naming
 +  guidelines][lint_naming] here when naming your lint. In short, the name should
 +  state the thing that is being checked for and read well when used with
 +  `allow`/`warn`/`deny`.
 +* `pedantic` sets the lint level to `Allow`. The exact mapping can be found
 +  [here][category_level_mapping]
 +* The last part should be a text that explains what exactly is wrong with the
 +  code
 +
 +The rest of this file contains an empty implementation for our lint pass, which
 +in this case is `EarlyLintPass` and should look like this:
 +
 +```rust
 +// clippy_lints/src/foo_functions.rs
 +
 +// .. imports and lint declaration ..
 +
 +declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
 +
 +impl EarlyLintPass for FooFunctions {}
 +```
 +
 +[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
 +[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
 +
 +## Lint registration
 +
 +When using `cargo dev new_lint`, the lint is automatically registered and
 +nothing more has to be done.
 +
 +When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
 +pass may have to be registered manually in the `register_plugins` function in
 +`clippy_lints/src/lib.rs`:
 +
 +```rust
 +store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
 +```
 +
 +As one may expect, there is a corresponding `register_late_pass` method
 +available as well. Without a call to one of `register_early_pass` or
 +`register_late_pass`, the lint pass in question will not be run.
 +
 +One reason that `cargo dev update_lints` does not automate this step is that
 +multiple lints can use the same lint pass, so registering the lint pass may
 +already be done when adding a new lint. Another reason that this step is not
 +automated is that the order that the passes are registered determines the order
 +the passes actually run, which in turn affects the order that any emitted lints
 +are output in.
 +
 +## Lint passes
 +
 +Writing a lint that only checks for the name of a function means that we only
 +have to deal with the AST and don't have to deal with the type system at all.
 +This is good, because it makes writing this particular lint less complicated.
 +
 +We have to make this decision with every new Clippy lint. It boils down to using
 +either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
 +
 +In short, the `LateLintPass` has access to type information while the
 +`EarlyLintPass` doesn't. If you don't need access to type information, use the
 +`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
 +hasn't really been a concern with Clippy so far.
 +
 +Since we don't need type information for checking the function name, we used
 +`--pass=early` when running the new lint automation and all the imports were
 +added accordingly.
 +
 +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
 +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
 +
 +## Emitting a lint
 +
 +With UI tests and the lint declaration in place, we can start working on the
 +implementation of the lint logic.
 +
 +Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        // TODO: Emit lint here
 +    }
 +}
 +```
 +
 +We implement the [`check_fn`][check_fn] method from the
 +[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
 +information about the function that is currently being checked. More on that in
 +the next section. Let's worry about the details later and emit our lint for
 +*every* function definition first.
 +
 +Depending on how complex we want our lint message to be, we can choose from a
 +variety of lint emission functions. They can all be found in
 +[`clippy_utils/src/diagnostics.rs`][diagnostics].
 +
 +`span_lint_and_help` seems most appropriate in this case. It allows us to
 +provide an extra help message and we can't really suggest a better name
 +automatically. This is how it looks:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        span_lint_and_help(
 +            cx,
 +            FOO_FUNCTIONS,
 +            span,
 +            "function named `foo`",
 +            None,
 +            "consider using a more meaningful name"
 +        );
 +    }
 +}
 +```
 +
 +Running our UI test should now produce output that contains the lint message.
 +
 +According to [the rustc-dev-guide], the text should be matter of fact and avoid
 +capitalization and periods, unless multiple sentences are needed. When code or
 +an identifier must appear in a message or label, it should be surrounded with
 +single grave accents \`.
 +
 +[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
 +[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
 +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 +
 +## Adding the lint logic
 +
 +Writing the logic for your lint will most likely be different from our example,
 +so this section is kept rather short.
 +
 +Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
 +that has the [`FnKind::Fn`] variant. It provides access to the name of the
 +function/method via an [`Ident`][ident].
 +
 +With that we can expand our `check_fn` method to:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        if is_foo_fn(fn_kind) {
 +            span_lint_and_help(
 +                cx,
 +                FOO_FUNCTIONS,
 +                span,
 +                "function named `foo`",
 +                None,
 +                "consider using a more meaningful name"
 +            );
 +        }
 +    }
 +}
 +```
 +
 +We separate the lint conditional from the lint emissions because it makes the
 +code a bit easier to read. In some cases this separation would also allow to
 +write some unit tests (as opposed to only UI tests) for the separate function.
 +
 +In our example, `is_foo_fn` looks like:
 +
 +```rust
 +// use statements, impl EarlyLintPass, check_fn, ..
 +
 +fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => {
 +            // check if `fn` name is `foo`
 +            ident.name.as_str() == "foo"
 +        }
 +        // ignore closures
 +        FnKind::Closure(..) => false
 +    }
 +}
 +```
 +
 +Now we should also run the full test suite with `cargo test`. At this point
 +running `cargo test` should produce the expected output. Remember to run `cargo
 +dev bless` to update the `.stderr` file.
 +
 +`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
 +implementation is not violating any Clippy lints itself.
 +
 +That should be it for the lint implementation. Running `cargo test` should now
 +pass.
 +
 +[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
 +[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
 +[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
 +
 +## Specifying the lint's minimum supported Rust version (MSRV)
 +
 +Sometimes a lint makes suggestions that require a certain version of Rust. For
 +example, the `manual_strip` lint suggests using `str::strip_prefix` and
 +`str::strip_suffix` which is only available after Rust 1.45. In such cases, you
 +need to ensure that the MSRV configured for the project is >= the MSRV of the
 +required Rust feature. If multiple features are required, just use the one with
 +a lower MSRV.
 +
 +First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`].
 +This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 +
 +```rust
 +msrv_aliases! {
 +    ..
 +    1,45,0 { STR_STRIP_PREFIX }
 +}
 +```
 +
 +In order to access the project-configured MSRV, you need to have an `msrv` field
 +in the LintPass struct, and a constructor to initialize the field. The `msrv`
 +value is passed to the constructor in `clippy_lints/lib.rs`.
 +
 +```rust
 +pub struct ManualStrip {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualStrip {
 +    #[must_use]
- using the `meets_msrv` utility function.
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +```
 +
 +The project's MSRV can then be matched against the feature MSRV in the LintPass
- if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
++using the `Msrv::meets` method.
 +
 +``` rust
- The project's MSRV can also be specified as an inner attribute, which overrides
++if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
 +    return;
 +}
 +```
 +
- #![feature(custom_inner_attributes)]
++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`.
 +
 +```rust
 +impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        ...
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +```
 +
 +Once the `msrv` is added to the lint, a relevant test case should be added to
 +the lint's test file, `tests/ui/manual_strip.rs` in this example. It should
 +have a case for the version below the MSRV and one with the same contents but
 +for the MSRV version itself.
 +
 +```rust
-     #![clippy::msrv = "1.44"]
 +...
 +
++#[clippy::msrv = "1.44"]
 +fn msrv_1_44() {
-     #![clippy::msrv = "1.45"]
 +    /* something that would trigger the lint */
 +}
 +
++#[clippy::msrv = "1.45"]
 +fn msrv_1_45() {
 +    /* something that would trigger the lint */
 +}
 +```
 +
 +As a last step, the lint should be added to the lint documentation. This is done
 +in `clippy_lints/src/utils/conf.rs`:
 +
 +```rust
 +define_Conf! {
 +    /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    ...
 +}
 +```
 +
 +[`clippy_utils::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/msrvs/index.html
 +
 +## Author lint
 +
 +If you have trouble implementing your lint, there is also the internal `author`
 +lint to generate Clippy code that detects the offending pattern. It does not
 +work for all of the Rust syntax, but can give a good starting point.
 +
 +The quickest way to use it, is the [Rust playground:
 +play.rust-lang.org][author_example]. Put the code you want to lint into the
 +editor and add the `#[clippy::author]` attribute above the item. Then run Clippy
 +via `Tools -> Clippy` and you should see the generated code in the output below.
 +
 +[Here][author_example] is an example on the playground.
 +
 +If the command was executed successfully, you can copy the code over to where
 +you are implementing your lint.
 +
 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
 +
 +## Print HIR lint
 +
 +To implement a lint, it's helpful to first understand the internal
 +representation that rustc uses. Clippy has the `#[clippy::dump]` attribute that
 +prints the [_High-Level Intermediate Representation (HIR)_] of the item,
 +statement, or expression that the attribute is attached to. To attach the
 +attribute to expressions you often need to enable
 +`#![feature(stmt_expr_attributes)]`.
 +
 +[Here][print_hir_example] you can find an example, just select _Tools_ and run
 +_Clippy_.
 +
 +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
 +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
 +
 +## Documentation
 +
 +The final thing before submitting our PR is to add some documentation to our
 +lint declaration.
 +
 +Please document your lint with a doc comment akin to the following:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for ... (describe what the lint matches).
 +    ///
 +    /// ### Why is this bad?
 +    /// Supply the reason for linting the code.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust,ignore
 +    /// // A short example of code that triggers the lint
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // A short example of improved code that doesn't trigger the lint
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +Once your lint is merged, this documentation will show up in the [lint
 +list][lint_list].
 +
 +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
 +
 +## Running rustfmt
 +
 +[Rustfmt] is a tool for formatting Rust code according to style guidelines. Your
 +code has to be formatted by `rustfmt` before a PR can be merged. Clippy uses
 +nightly `rustfmt` in the CI.
 +
 +It can be installed via `rustup`:
 +
 +```bash
 +rustup component add rustfmt --toolchain=nightly
 +```
 +
 +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 +installed for the nightly toolchain.
 +
 +[Rustfmt]: https://github.com/rust-lang/rustfmt
 +
 +## Debugging
 +
 +If you want to debug parts of your lint implementation, you can use the [`dbg!`]
 +macro anywhere in your code. Running the tests should then include the debug
 +output in the `stdout` part.
 +
 +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
 +
 +## PR Checklist
 +
 +Before submitting your PR make sure you followed all of the basic requirements:
 +
 +<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
 +
 +- \[ ] Followed [lint naming conventions][lint_naming]
 +- \[ ] Added passing UI tests (including committed `.stderr` file)
 +- \[ ] `cargo test` passes locally
 +- \[ ] Executed `cargo dev update_lints`
 +- \[ ] Added lint documentation
 +- \[ ] Run `cargo dev fmt`
 +
 +## Adding configuration to a lint
 +
 +Clippy supports the configuration of lints values using a `clippy.toml` file in
 +the workspace directory. Adding a configuration to a lint can be useful for
 +thresholds or to constrain some behavior that can be seen as a false positive
 +for some users. Adding a configuration is done in the following steps:
 +
 +1. Adding a new configuration entry to [`clippy_lints::utils::conf`] like this:
 +
 +   ```rust
 +   /// Lint: LINT_NAME.
 +   ///
 +   /// <The configuration field doc comment>
 +   (configuration_ident: Type = DefaultValue),
 +   ```
 +
 +   The doc comment is automatically added to the documentation of the listed
 +   lints. The default value will be formatted using the `Debug` implementation
 +   of the type.
 +2. Adding the configuration value to the lint impl struct:
 +    1. This first requires the definition of a lint impl struct. Lint impl
 +       structs are usually generated with the `declare_lint_pass!` macro. This
 +       struct needs to be defined manually to add some kind of metadata to it:
 +       ```rust
 +       // Generated struct definition
 +       declare_lint_pass!(StructName => [
 +           LINT_NAME
 +       ]);
 +
 +       // New manual definition struct
 +       #[derive(Copy, Clone)]
 +       pub struct StructName {}
 +
 +       impl_lint_pass!(StructName => [
 +           LINT_NAME
 +       ]);
 +       ```
 +
 +    2. Next add the configuration value and a corresponding creation method like
 +       this:
 +       ```rust
 +       #[derive(Copy, Clone)]
 +       pub struct StructName {
 +           configuration_ident: Type,
 +       }
 +
 +       // ...
 +
 +       impl StructName {
 +           pub fn new(configuration_ident: Type) -> Self {
 +               Self {
 +                   configuration_ident,
 +               }
 +           }
 +       }
 +       ```
 +3. Passing the configuration value to the lint impl struct:
 +
 +   First find the struct construction in the [`clippy_lints` lib file]. The
 +   configuration value is now cloned or copied into a local value that is then
 +   passed to the impl struct like this:
 +
 +   ```rust
 +   // Default generated registration:
 +   store.register_*_pass(|| box module::StructName);
 +
 +   // New registration with configuration value
 +   let configuration_ident = conf.configuration_ident.clone();
 +   store.register_*_pass(move || box module::StructName::new(configuration_ident));
 +   ```
 +
 +   Congratulations the work is almost done. The configuration value can now be
 +   accessed in the linting code via `self.configuration_ident`.
 +
 +4. Adding tests:
 +    1. The default configured value can be tested like any normal lint in
 +       [`tests/ui`].
 +    2. The configuration itself will be tested separately in [`tests/ui-toml`].
 +       Simply add a new subfolder with a fitting name. This folder contains a
 +       `clippy.toml` file with the configuration value and a rust file that
 +       should be linted by Clippy. The test can otherwise be written as usual.
 +
 +[`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs
 +[`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
 +[`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui
 +[`tests/ui-toml`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui-toml
 +
 +## Cheat Sheet
 +
 +Here are some pointers to things you are likely going to need for every lint:
 +
 +* [Clippy utils][utils] - Various helper functions. Maybe the function you need
 +  is already in here ([`is_type_diagnostic_item`], [`implements_trait`],
 +  [`snippet`], etc)
 +* [Clippy diagnostics][diagnostics]
 +* [Let chains][let-chains]
 +* [`from_expansion`][from_expansion] and
 +  [`in_external_macro`][in_external_macro]
 +* [`Span`][span]
 +* [`Applicability`][applicability]
 +* [Common tools for writing lints](common_tools_writing_lints.md) helps with
 +  common operations
 +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler
 +  concepts
 +* [The nightly rustc docs][nightly_docs] which has been linked to throughout
 +  this guide
 +
 +For `EarlyLintPass` lints:
 +
 +* [`EarlyLintPass`][early_lint_pass]
 +* [`rustc_ast::ast`][ast]
 +
 +For `LateLintPass` lints:
 +
 +* [`LateLintPass`][late_lint_pass]
 +* [`Ty::TyKind`][ty]
 +
 +While most of Clippy's lint utils are documented, most of rustc's internals lack
 +documentation currently. This is unfortunate, but in most cases you can probably
 +get away with copying things from existing similar lints. If you are stuck,
 +don't hesitate to ask on [Zulip] or in the issue/PR.
 +
 +[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
 +[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
 +[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
 +[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
 +[let-chains]: https://github.com/rust-lang/rust/pull/94927
 +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
 +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
 +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
 +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
 +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
 +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
 +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c5587c4bf9089eff4283f327d0029d079462df4f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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.
index 9e15f1504fa91f6e6f2c883380f52d5355862c7a,0000000000000000000000000000000000000000..ec7f1dd0d846cbe8a0f87ad50c43e733ebfec59a
mode 100644,000000..100644
--- /dev/null
@@@ -1,559 -1,0 +1,557 @@@
-             "store.register_{lint_pass}_pass(move |{ctor_arg}| Box::new({module_name}::{camel_name}::new(msrv)));\n    ",
 +use crate::clippy_project_root;
 +use indoc::{formatdoc, writedoc};
 +use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::io::{self, ErrorKind};
 +use std::path::{Path, PathBuf};
 +
 +struct LintData<'a> {
 +    pass: &'a str,
 +    name: &'a str,
 +    category: &'a str,
 +    ty: Option<&'a str>,
 +    project_root: PathBuf,
 +}
 +
 +trait Context {
 +    fn context<C: AsRef<str>>(self, text: C) -> Self;
 +}
 +
 +impl<T> Context for io::Result<T> {
 +    fn context<C: AsRef<str>>(self, text: C) -> Self {
 +        match self {
 +            Ok(t) => Ok(t),
 +            Err(e) => {
 +                let message = format!("{}: {e}", text.as_ref());
 +                Err(io::Error::new(ErrorKind::Other, message))
 +            },
 +        }
 +    }
 +}
 +
 +/// Creates the files required to implement and test a new lint and runs `update_lints`.
 +///
 +/// # Errors
 +///
 +/// This function errors out if the files couldn't be created or written to.
 +pub fn create(
 +    pass: Option<&String>,
 +    lint_name: Option<&String>,
 +    category: Option<&str>,
 +    mut ty: Option<&str>,
 +    msrv: bool,
 +) -> io::Result<()> {
 +    if category == Some("cargo") && ty.is_none() {
 +        // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
 +        ty = Some("cargo");
 +    }
 +
 +    let lint = LintData {
 +        pass: pass.map_or("", String::as_str),
 +        name: lint_name.expect("`name` argument is validated by clap"),
 +        category: category.expect("`category` argument is validated by clap"),
 +        ty,
 +        project_root: clippy_project_root(),
 +    };
 +
 +    create_lint(&lint, msrv).context("Unable to create lint implementation")?;
 +    create_test(&lint).context("Unable to create a test for the new lint")?;
 +
 +    if lint.ty.is_none() {
 +        add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
 +    }
 +
 +    Ok(())
 +}
 +
 +fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 +    if let Some(ty) = lint.ty {
 +        create_lint_for_ty(lint, enable_msrv, ty)
 +    } else {
 +        let lint_contents = get_lint_file_contents(lint, enable_msrv);
 +        let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
 +        write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?;
 +        println!("Generated lint file: `{lint_path}`");
 +
 +        Ok(())
 +    }
 +}
 +
 +fn create_test(lint: &LintData<'_>) -> io::Result<()> {
 +    fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
 +        let mut path = location.into().join(case);
 +        fs::create_dir(&path)?;
 +        write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
 +
 +        path.push("src");
 +        fs::create_dir(&path)?;
 +        let header = format!("// compile-flags: --crate-name={lint_name}");
 +        write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
 +
 +        Ok(())
 +    }
 +
 +    if lint.category == "cargo" {
 +        let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
 +        let test_dir = lint.project_root.join(&relative_test_dir);
 +        fs::create_dir(&test_dir)?;
 +
 +        create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
 +        create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?;
 +
 +        println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`");
 +    } else {
 +        let test_path = format!("tests/ui/{}.rs", lint.name);
 +        let test_contents = get_test_file_contents(lint.name, None);
 +        write_file(lint.project_root.join(&test_path), test_contents)?;
 +
 +        println!("Generated test file: `{test_path}`");
 +    }
 +
 +    Ok(())
 +}
 +
 +fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 +    let path = "clippy_lints/src/lib.rs";
 +    let mut lib_rs = fs::read_to_string(path).context("reading")?;
 +
 +    let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
 +
 +    let new_lint = if enable_msrv {
 +        format!(
-             use clippy_utils::msrvs;
++            "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,
 +            camel_name = to_camel_case(lint.name),
 +        )
 +    } else {
 +        format!(
 +            "store.register_{lint_pass}_pass(|{ctor_arg}| Box::new({module_name}::{camel_name}));\n    ",
 +            lint_pass = lint.pass,
 +            ctor_arg = if lint.pass == "late" { "_" } else { "" },
 +            module_name = lint.name,
 +            camel_name = to_camel_case(lint.name),
 +        )
 +    };
 +
 +    lib_rs.insert_str(comment_start, &new_lint);
 +
 +    fs::write(path, lib_rs).context("writing")
 +}
 +
 +fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
 +    fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
 +        OpenOptions::new()
 +            .write(true)
 +            .create_new(true)
 +            .open(path)?
 +            .write_all(contents)
 +    }
 +
 +    inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display()))
 +}
 +
 +fn to_camel_case(name: &str) -> String {
 +    name.split('_')
 +        .map(|s| {
 +            if s.is_empty() {
 +                String::new()
 +            } else {
 +                [&s[0..1].to_uppercase(), &s[1..]].concat()
 +            }
 +        })
 +        .collect()
 +}
 +
 +pub(crate) fn get_stabilization_version() -> String {
 +    fn parse_manifest(contents: &str) -> Option<String> {
 +        let version = contents
 +            .lines()
 +            .filter_map(|l| l.split_once('='))
 +            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
 +        let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
 +            return None;
 +        };
 +        let (minor, patch) = version.split_once('.')?;
 +        Some(format!(
 +            "{}.{}.0",
 +            minor.parse::<u32>().ok()?,
 +            patch.parse::<u32>().ok()?
 +        ))
 +    }
 +    let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
 +    parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
 +}
 +
 +fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
 +    let mut contents = formatdoc!(
 +        r#"
 +        #![allow(unused)]
 +        #![warn(clippy::{lint_name})]
 +
 +        fn main() {{
 +            // test code goes here
 +        }}
 +    "#
 +    );
 +
 +    if let Some(header) = header_commands {
 +        contents = format!("{header}\n{contents}");
 +    }
 +
 +    contents
 +}
 +
 +fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
 +    formatdoc!(
 +        r#"
 +        # {hint}
 +
 +        [package]
 +        name = "{lint_name}"
 +        version = "0.1.0"
 +        publish = false
 +
 +        [workspace]
 +    "#
 +    )
 +}
 +
 +fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
 +    let mut result = String::new();
 +
 +    let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
 +        "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
 +        "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
 +        _ => {
 +            unreachable!("`pass_type` should only ever be `early` or `late`!");
 +        },
 +    };
 +
 +    let lint_name = lint.name;
 +    let category = lint.category;
 +    let name_camel = to_camel_case(lint.name);
 +    let name_upper = lint_name.to_uppercase();
 +
 +    result.push_str(&if enable_msrv {
 +        formatdoc!(
 +            r#"
-             use rustc_semver::RustcVersion;
++            use clippy_utils::msrvs::{{self, Msrv}};
 +            {pass_import}
 +            use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
-                 msrv: Option<RustcVersion>,
 +            use rustc_session::{{declare_tool_lint, impl_lint_pass}};
 +
 +        "#
 +        )
 +    } else {
 +        formatdoc!(
 +            r#"
 +            {pass_import}
 +            use rustc_lint::{{{context_import}, {pass_type}}};
 +            use rustc_session::{{declare_lint_pass, declare_tool_lint}};
 +
 +        "#
 +        )
 +    });
 +
 +    let _ = write!(result, "{}", get_lint_declaration(&name_upper, category));
 +
 +    result.push_str(&if enable_msrv {
 +        formatdoc!(
 +            r#"
 +            pub struct {name_camel} {{
-                 pub fn new(msrv: Option<RustcVersion>) -> Self {{
++                msrv: Msrv,
 +            }}
 +
 +            impl {name_camel} {{
 +                #[must_use]
-                 use clippy_utils::{{meets_msrv, msrvs}};
++                pub fn new(msrv: Msrv) -> Self {{
 +                    Self {{ msrv }}
 +                }}
 +            }}
 +
 +            impl_lint_pass!({name_camel} => [{name_upper}]);
 +
 +            impl {pass_type}{pass_lifetimes} for {name_camel} {{
 +                extract_msrv_attr!({context_import});
 +            }}
 +
 +            // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
 +            // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
 +            // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
 +        "#
 +        )
 +    } else {
 +        formatdoc!(
 +            r#"
 +            declare_lint_pass!({name_camel} => [{name_upper}]);
 +
 +            impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
 +        "#
 +        )
 +    });
 +
 +    result
 +}
 +
 +fn get_lint_declaration(name_upper: &str, category: &str) -> String {
 +    formatdoc!(
 +        r#"
 +            declare_clippy_lint! {{
 +                /// ### What it does
 +                ///
 +                /// ### Why is this bad?
 +                ///
 +                /// ### Example
 +                /// ```rust
 +                /// // example code where clippy issues a warning
 +                /// ```
 +                /// Use instead:
 +                /// ```rust
 +                /// // example code which does not raise clippy warning
 +                /// ```
 +                #[clippy::version = "{}"]
 +                pub {name_upper},
 +                {category},
 +                "default lint description"
 +            }}
 +        "#,
 +        get_stabilization_version(),
 +    )
 +}
 +
 +fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::Result<()> {
 +    match ty {
 +        "cargo" => assert_eq!(
 +            lint.category, "cargo",
 +            "Lints of type `cargo` must have the `cargo` category"
 +        ),
 +        _ if lint.category == "cargo" => panic!("Lints of category `cargo` must have the `cargo` type"),
 +        _ => {},
 +    }
 +
 +    let ty_dir = lint.project_root.join(format!("clippy_lints/src/{ty}"));
 +    assert!(
 +        ty_dir.exists() && ty_dir.is_dir(),
 +        "Directory `{}` does not exist!",
 +        ty_dir.display()
 +    );
 +
 +    let lint_file_path = ty_dir.join(format!("{}.rs", lint.name));
 +    assert!(
 +        !lint_file_path.exists(),
 +        "File `{}` already exists",
 +        lint_file_path.display()
 +    );
 +
 +    let mod_file_path = ty_dir.join("mod.rs");
 +    let context_import = setup_mod_file(&mod_file_path, lint)?;
 +
 +    let name_upper = lint.name.to_uppercase();
 +    let mut lint_file_contents = String::new();
 +
 +    if enable_msrv {
 +        let _ = writedoc!(
 +            lint_file_contents,
 +            r#"
-                 use rustc_semver::RustcVersion;
++                use clippy_utils::msrvs::{{self, Msrv}};
 +                use rustc_lint::{{{context_import}, LintContext}};
-                 pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
-                     if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
 +
 +                use super::{name_upper};
 +
 +                // TODO: Adjust the parameters as necessary
++                pub(super) fn check(cx: &{context_import}, msrv: &Msrv) {{
++                    if !msrv.meets(todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
 +                        return;
 +                    }}
 +                    todo!();
 +                }}
 +           "#,
 +            context_import = context_import,
 +            name_upper = name_upper,
 +        );
 +    } else {
 +        let _ = writedoc!(
 +            lint_file_contents,
 +            r#"
 +                use rustc_lint::{{{context_import}, LintContext}};
 +
 +                use super::{name_upper};
 +
 +                // TODO: Adjust the parameters as necessary
 +                pub(super) fn check(cx: &{context_import}) {{
 +                    todo!();
 +                }}
 +           "#,
 +            context_import = context_import,
 +            name_upper = name_upper,
 +        );
 +    }
 +
 +    write_file(lint_file_path.as_path(), lint_file_contents)?;
 +    println!("Generated lint file: `clippy_lints/src/{ty}/{}.rs`", lint.name);
 +    println!(
 +        "Be sure to add a call to `{}::check` in `clippy_lints/src/{ty}/mod.rs`!",
 +        lint.name
 +    );
 +
 +    Ok(())
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> {
 +    use super::update_lints::{match_tokens, LintDeclSearchResult};
 +    use rustc_lexer::TokenKind;
 +
 +    let lint_name_upper = lint.name.to_uppercase();
 +
 +    let mut file_contents = fs::read_to_string(path)?;
 +    assert!(
 +        !file_contents.contains(&lint_name_upper),
 +        "Lint `{}` already defined in `{}`",
 +        lint.name,
 +        path.display()
 +    );
 +
 +    let mut offset = 0usize;
 +    let mut last_decl_curly_offset = None;
 +    let mut lint_context = None;
 +
 +    let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &file_contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
 +    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
 +        let mut iter = iter
 +            .by_ref()
 +            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
 +
 +        match content {
 +            "declare_clippy_lint" => {
 +                // matches `!{`
 +                match_tokens!(iter, Bang OpenBrace);
 +                if let Some(LintDeclSearchResult { range, .. }) =
 +                    iter.find(|result| result.token_kind == TokenKind::CloseBrace)
 +                {
 +                    last_decl_curly_offset = Some(range.end);
 +                }
 +            },
 +            "impl" => {
 +                let mut token = iter.next();
 +                match token {
 +                    // matches <'foo>
 +                    Some(LintDeclSearchResult {
 +                        token_kind: TokenKind::Lt,
 +                        ..
 +                    }) => {
 +                        match_tokens!(iter, Lifetime { .. } Gt);
 +                        token = iter.next();
 +                    },
 +                    None => break,
 +                    _ => {},
 +                }
 +
 +                if let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::Ident,
 +                    content,
 +                    ..
 +                }) = token
 +                {
 +                    // Get the appropriate lint context struct
 +                    lint_context = match content {
 +                        "LateLintPass" => Some("LateContext"),
 +                        "EarlyLintPass" => Some("EarlyContext"),
 +                        _ => continue,
 +                    };
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    drop(iter);
 +
 +    let last_decl_curly_offset =
 +        last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display()));
 +    let lint_context =
 +        lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display()));
 +
 +    // Add the lint declaration to `mod.rs`
 +    file_contents.replace_range(
 +        // Remove the trailing newline, which should always be present
 +        last_decl_curly_offset..=last_decl_curly_offset,
 +        &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)),
 +    );
 +
 +    // Add the lint to `impl_lint_pass`/`declare_lint_pass`
 +    let impl_lint_pass_start = file_contents.find("impl_lint_pass!").unwrap_or_else(|| {
 +        file_contents
 +            .find("declare_lint_pass!")
 +            .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`/`declare_lint_pass`"))
 +    });
 +
 +    let mut arr_start = file_contents[impl_lint_pass_start..].find('[').unwrap_or_else(|| {
 +        panic!("malformed `impl_lint_pass`/`declare_lint_pass`");
 +    });
 +
 +    arr_start += impl_lint_pass_start;
 +
 +    let mut arr_end = file_contents[arr_start..]
 +        .find(']')
 +        .expect("failed to find `impl_lint_pass` terminator");
 +
 +    arr_end += arr_start;
 +
 +    let mut arr_content = file_contents[arr_start + 1..arr_end].to_string();
 +    arr_content.retain(|c| !c.is_whitespace());
 +
 +    let mut new_arr_content = String::new();
 +    for ident in arr_content
 +        .split(',')
 +        .chain(std::iter::once(&*lint_name_upper))
 +        .filter(|s| !s.is_empty())
 +    {
 +        let _ = write!(new_arr_content, "\n    {ident},");
 +    }
 +    new_arr_content.push('\n');
 +
 +    file_contents.replace_range(arr_start + 1..arr_end, &new_arr_content);
 +
 +    // Just add the mod declaration at the top, it'll be fixed by rustfmt
 +    file_contents.insert_str(0, &format!("mod {};\n", &lint.name));
 +
 +    let mut file = OpenOptions::new()
 +        .write(true)
 +        .truncate(true)
 +        .open(path)
 +        .context(format!("trying to open: `{}`", path.display()))?;
 +    file.write_all(file_contents.as_bytes())
 +        .context(format!("writing to file: `{}`", path.display()))?;
 +
 +    Ok(lint_context)
 +}
 +
 +#[test]
 +fn test_camel_case() {
 +    let s = "a_lint";
 +    let s2 = to_camel_case(s);
 +    assert_eq!(s2, "ALint");
 +
 +    let name = "a_really_long_new_lint";
 +    let name2 = to_camel_case(name);
 +    assert_eq!(name2, "AReallyLongNewLint");
 +
 +    let name3 = "lint__name";
 +    let name4 = to_camel_case(name3);
 +    assert_eq!(name4, "LintName");
 +}
index df92579a85df280a7846addaad0aa94045ed4fc1,0000000000000000000000000000000000000000..52beaf504a4e0deb6ff0f17e5bf387d463e05d9f
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,110 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
 +    /// don't because they're a half open range.
 +    ///
 +    /// ### Why is this bad?
 +    /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 'a'..'z';
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = 'a'..='z';
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub ALMOST_COMPLETE_LETTER_RANGE,
 +    suspicious,
 +    "almost complete letter range"
 +}
 +impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
 +
 +pub struct AlmostCompleteLetterRange {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +impl AlmostCompleteLetterRange {
-                 && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +impl EarlyLintPass for AlmostCompleteLetterRange {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
 +        if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
 +            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)
-             let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
++                && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
 +            {
 +                Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
 +            } else {
 +                None
 +            };
 +            check_range(cx, e.span, start, end, sugg);
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
 +        if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
 +            && matches!(kind.node, RangeEnd::Excluded)
 +        {
++            let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
 +                "..="
 +            } else {
 +                "..."
 +            };
 +            check_range(cx, p.span, start, end, Some((kind.span, sugg)));
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
 +    if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind
 +        && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind
 +        && matches!(
 +            (
 +                LitKind::from_token_lit(start_token_lit),
 +                LitKind::from_token_lit(end_token_lit),
 +            ),
 +            (
 +                Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
 +                Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
 +            )
 +            | (
 +                Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
 +                Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
 +            )
 +        )
 +        && !in_external_macro(cx.sess(), span)
 +    {
 +        span_lint_and_then(
 +            cx,
 +            ALMOST_COMPLETE_LETTER_RANGE,
 +            span,
 +            "almost complete ascii letter range",
 +            |diag| {
 +                if let Some((span, sugg)) = sugg {
 +                    diag.span_suggestion(
 +                        span,
 +                        "use an inclusive range",
 +                        sugg,
 +                        Applicability::MaybeIncorrect,
 +                    );
 +                }
 +            }
 +        );
 +    }
 +}
index 724490fb49592f64c5b770de45124e741dcb08c0,0000000000000000000000000000000000000000..ccf82f132f4e42dd1af0b6a533c678dd757e2337
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +use clippy_utils::diagnostics::span_lint_and_help;
-     msrv: Option<RustcVersion>,
++use clippy_utils::msrvs::{self, Msrv};
 +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol;
 +use std::f64::consts as f64;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for floating point literals that approximate
 +    /// constants which are defined in
 +    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
 +    /// or
 +    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
 +    /// respectively, suggesting to use the predefined constant.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually, the definition in the standard library is more
 +    /// precise than what people come up with. If you find that your definition is
 +    /// actually more precise, please [file a Rust
 +    /// issue](https://github.com/rust-lang/rust/issues).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 3.14;
 +    /// let y = 1_f64 / x;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = std::f32::consts::PI;
 +    /// let y = std::f64::consts::FRAC_1_PI;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub APPROX_CONSTANT,
 +    correctness,
 +    "the approximate of a known float constant (in `std::fXX::consts`)"
 +}
 +
 +// Tuples are of the form (constant, name, min_digits, msrv)
 +const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
 +    (f64::E, "E", 4, None),
 +    (f64::FRAC_1_PI, "FRAC_1_PI", 4, None),
 +    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None),
 +    (f64::FRAC_2_PI, "FRAC_2_PI", 5, None),
 +    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None),
 +    (f64::FRAC_PI_2, "FRAC_PI_2", 5, None),
 +    (f64::FRAC_PI_3, "FRAC_PI_3", 5, None),
 +    (f64::FRAC_PI_4, "FRAC_PI_4", 5, None),
 +    (f64::FRAC_PI_6, "FRAC_PI_6", 5, None),
 +    (f64::FRAC_PI_8, "FRAC_PI_8", 5, None),
 +    (f64::LN_2, "LN_2", 5, None),
 +    (f64::LN_10, "LN_10", 5, None),
 +    (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)),
 +    (f64::LOG2_E, "LOG2_E", 5, None),
 +    (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)),
 +    (f64::LOG10_E, "LOG10_E", 5, None),
 +    (f64::PI, "PI", 3, None),
 +    (f64::SQRT_2, "SQRT_2", 5, None),
 +    (f64::TAU, "TAU", 3, Some(msrvs::TAU)),
 +];
 +
 +pub struct ApproxConstant {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ApproxConstant {
 +    #[must_use]
-                 if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +
 +    fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
 +        match *lit {
 +            LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
 +                FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"),
 +                FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"),
 +            },
 +            LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"),
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
 +        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| self.msrv.meets(msrv)) {
 +                    span_lint_and_help(
 +                        cx,
 +                        APPROX_CONSTANT,
 +                        e.span,
 +                        &format!("approximate value of `{module}::consts::{}` found", &name),
 +                        None,
 +                        "consider using the constant directly",
 +                    );
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Lit(lit) = &e.kind {
 +            self.check_lit(cx, &lit.node, e);
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Returns `false` if the number of significant figures in `value` are
 +/// less than `min_digits`; otherwise, returns true if `value` is equal
 +/// to `constant`, rounded to the number of digits present in `value`.
 +#[must_use]
 +fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
 +    if value.len() <= min_digits {
 +        false
 +    } else if constant.to_string().starts_with(value) {
 +        // The value is a truncated constant
 +        true
 +    } else {
 +        let round_const = format!("{constant:.*}", value.len() - 2);
 +        value == round_const
 +    }
 +}
index 018f10f258867872816e22b5f9604ee0cbc57a0f,0000000000000000000000000000000000000000..0710ac0bb0a72468bb8144351560972dcf53a55a
mode 100644,000000..100644
--- /dev/null
@@@ -1,763 -1,0 +1,761 @@@
- use clippy_utils::msrvs;
 +//! checks for attributes
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, macro_backtrace};
- use clippy_utils::{extract_msrv_attr, meets_msrv};
++use clippy_utils::msrvs::{self, Msrv};
 +use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
- use rustc_semver::RustcVersion;
 +use if_chain::if_chain;
 +use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
-     pub msrv: Option<RustcVersion>,
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, DUMMY_SP};
 +use semver::Version;
 +
 +static UNIX_SYSTEMS: &[&str] = &[
 +    "android",
 +    "dragonfly",
 +    "emscripten",
 +    "freebsd",
 +    "fuchsia",
 +    "haiku",
 +    "illumos",
 +    "ios",
 +    "l4re",
 +    "linux",
 +    "macos",
 +    "netbsd",
 +    "openbsd",
 +    "redox",
 +    "solaris",
 +    "vxworks",
 +];
 +
 +// NOTE: windows is excluded from the list because it's also a valid target family.
 +static NON_UNIX_SYSTEMS: &[&str] = &["hermit", "none", "wasi"];
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items annotated with `#[inline(always)]`,
 +    /// unless the annotated function is empty or simply panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there are valid uses of this annotation (and once
 +    /// you know when to use it, by all means `allow` this lint), it's a common
 +    /// newbie-mistake to pepper one's code with it.
 +    ///
 +    /// As a rule of thumb, before slapping `#[inline(always)]` on a function,
 +    /// measure if that additional function call really affects your runtime profile
 +    /// sufficiently to make up for the increase in compile time.
 +    ///
 +    /// ### Known problems
 +    /// False positives, big time. This lint is meant to be
 +    /// deactivated by everyone doing serious performance work. This means having
 +    /// done the measurement.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[inline(always)]
 +    /// fn not_quite_hot_code(..) { ... }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INLINE_ALWAYS,
 +    pedantic,
 +    "use of `#[inline(always)]`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `extern crate` and `use` items annotated with
 +    /// lint attributes.
 +    ///
 +    /// This lint permits lint attributes for lints emitted on the items themself.
 +    /// For `use` items these lints are:
 +    /// * deprecated
 +    /// * unreachable_pub
 +    /// * unused_imports
 +    /// * clippy::enum_glob_use
 +    /// * clippy::macro_use_imports
 +    /// * clippy::wildcard_imports
 +    ///
 +    /// For `extern crate` items these lints are:
 +    /// * `unused_imports` on items with `#[macro_use]`
 +    ///
 +    /// ### Why is this bad?
 +    /// Lint attributes have no effect on crate imports. Most
 +    /// likely a `!` was forgotten.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[deny(dead_code)]
 +    /// extern crate foo;
 +    /// #[forbid(dead_code)]
 +    /// use foo::bar;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[allow(unused_imports)]
 +    /// use foo::baz;
 +    /// #[allow(unused_imports)]
 +    /// #[macro_use]
 +    /// extern crate baz;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ATTRIBUTE,
 +    correctness,
 +    "use of lint attributes on `extern crate` items"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[deprecated]` annotations with a `since`
 +    /// field that is not a valid semantic version.
 +    ///
 +    /// ### Why is this bad?
 +    /// For checking the version of the deprecation, it must be
 +    /// a valid semver. Failing that, the contained information is useless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[deprecated(since = "forever")]
 +    /// fn something_else() { /* ... */ }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DEPRECATED_SEMVER,
 +    correctness,
 +    "use of `#[deprecated(since = \"x\")]` where x is not semver"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for empty lines after outer attributes
 +    ///
 +    /// ### Why is this bad?
 +    /// Most likely the attribute was meant to be an inner attribute using a '!'.
 +    /// If it was meant to be an outer attribute, then the following item
 +    /// should not be separated by empty lines.
 +    ///
 +    /// ### Known problems
 +    /// Can cause false positives.
 +    ///
 +    /// From the clippy side it's difficult to detect empty lines between an attributes and the
 +    /// following item because empty lines and comments are not part of the AST. The parsing
 +    /// currently works for basic cases but is not perfect.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[allow(dead_code)]
 +    ///
 +    /// fn not_quite_good_code() { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Good (as inner attribute)
 +    /// #![allow(dead_code)]
 +    ///
 +    /// fn this_is_fine() { }
 +    ///
 +    /// // or
 +    ///
 +    /// // Good (as outer attribute)
 +    /// #[allow(dead_code)]
 +    /// fn this_is_fine_too() { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EMPTY_LINE_AFTER_OUTER_ATTR,
 +    nursery,
 +    "empty line after outer attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category.
 +    ///
 +    /// ### Why is this bad?
 +    /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust.
 +    /// These lints should only be enabled on a lint-by-lint basis and with careful consideration.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #![deny(clippy::restriction)]
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #![deny(clippy::as_conversions)]
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    suspicious,
 +    "enabling the complete restriction group"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it
 +    /// with `#[rustfmt::skip]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690))
 +    /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes.
 +    ///
 +    /// ### Known problems
 +    /// This lint doesn't detect crate level inner attributes, because they get
 +    /// processed before the PreExpansionPass lints get executed. See
 +    /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[cfg_attr(rustfmt, rustfmt_skip)]
 +    /// fn main() { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #[rustfmt::skip]
 +    /// fn main() { }
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub DEPRECATED_CFG_ATTR,
 +    complexity,
 +    "usage of `cfg_attr(rustfmt)` instead of tool attributes"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cfg attributes having operating systems used in target family position.
 +    ///
 +    /// ### Why is this bad?
 +    /// The configuration option will not be recognised and the related item will not be included
 +    /// by the conditional compilation engine.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[cfg(linux)]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # mod hidden {
 +    /// #[cfg(target_os = "linux")]
 +    /// fn conditional() { }
 +    /// # }
 +    ///
 +    /// // or
 +    ///
 +    /// #[cfg(unix)]
 +    /// fn conditional() { }
 +    /// ```
 +    /// Check the [Rust Reference](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os) for more details.
 +    #[clippy::version = "1.45.0"]
 +    pub MISMATCHED_TARGET_OS,
 +    correctness,
 +    "usage of `cfg(operating_system)` instead of `cfg(target_os = \"operating_system\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for attributes that allow lints without a reason.
 +    ///
 +    /// (This requires the `lint_reasons` feature)
 +    ///
 +    /// ### Why is this bad?
 +    /// Allowing a lint should always have a reason. This reason should be documented to
 +    /// ensure that others understand the reasoning
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint)]
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    restriction,
 +    "ensures that all `allow` and `expect` attributes have a reason"
 +}
 +
 +declare_lint_pass!(Attributes => [
 +    ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    INLINE_ALWAYS,
 +    DEPRECATED_SEMVER,
 +    USELESS_ATTRIBUTE,
 +    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Attributes {
 +    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
 +        for (name, level) in &cx.sess().opts.lint_opts {
 +            if name == "clippy::restriction" && *level > Level::Allow {
 +                span_lint_and_then(
 +                    cx,
 +                    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +                    DUMMY_SP,
 +                    "`clippy::restriction` is not meant to be enabled as a group",
 +                    |diag| {
 +                        diag.note(format!(
 +                            "because of the command line `--{} clippy::restriction`",
 +                            level.as_str()
 +                        ));
 +                        diag.help("enable the restriction lints you need individually");
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
 +        if let Some(items) = &attr.meta_item_list() {
 +            if let Some(ident) = attr.ident() {
 +                if is_lint_level(ident.name) {
 +                    check_clippy_lint_names(cx, ident.name, items);
 +                }
 +                if matches!(ident.name, sym::allow | sym::expect) {
 +                    check_lint_reason(cx, ident.name, items, attr);
 +                }
 +                if items.is_empty() || !attr.has_name(sym::deprecated) {
 +                    return;
 +                }
 +                for item in items {
 +                    if_chain! {
 +                        if let NestedMetaItem::MetaItem(mi) = &item;
 +                        if let MetaItemKind::NameValue(lit) = &mi.kind;
 +                        if mi.has_name(sym::since);
 +                        then {
 +                            check_semver(cx, item.span(), lit);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        if is_relevant_item(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, attrs);
 +        }
 +        match item.kind {
 +            ItemKind::ExternCrate(..) | ItemKind::Use(..) => {
 +                let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
 +
 +                for attr in attrs {
 +                    if in_external_macro(cx.sess(), attr.span) {
 +                        return;
 +                    }
 +                    if let Some(lint_list) = &attr.meta_item_list() {
 +                        if attr.ident().map_or(false, |ident| is_lint_level(ident.name)) {
 +                            for lint in lint_list {
 +                                match item.kind {
 +                                    ItemKind::Use(..) => {
 +                                        if is_word(lint, sym::unused_imports)
 +                                            || is_word(lint, sym::deprecated)
 +                                            || is_word(lint, sym!(unreachable_pub))
 +                                            || is_word(lint, sym!(unused))
 +                                            || extract_clippy_lint(lint).map_or(false, |s| {
 +                                                matches!(
 +                                                    s.as_str(),
 +                                                    "wildcard_imports"
 +                                                        | "enum_glob_use"
 +                                                        | "redundant_pub_crate"
 +                                                        | "macro_use_imports"
 +                                                        | "unsafe_removed_from_name"
 +                                                        | "module_name_repetitions"
 +                                                        | "single_component_path_imports"
 +                                                )
 +                                            })
 +                                        {
 +                                            return;
 +                                        }
 +                                    },
 +                                    ItemKind::ExternCrate(..) => {
 +                                        if is_word(lint, sym::unused_imports) && skip_unused_imports {
 +                                            return;
 +                                        }
 +                                        if is_word(lint, sym!(unused_extern_crates)) {
 +                                            return;
 +                                        }
 +                                    },
 +                                    _ => {},
 +                                }
 +                            }
 +                            let line_span = first_line_of_span(cx, attr.span);
 +
 +                            if let Some(mut sugg) = snippet_opt(cx, line_span) {
 +                                if sugg.contains("#[") {
 +                                    span_lint_and_then(
 +                                        cx,
 +                                        USELESS_ATTRIBUTE,
 +                                        line_span,
 +                                        "useless lint attribute",
 +                                        |diag| {
 +                                            sugg = sugg.replacen("#[", "#![", 1);
 +                                            diag.span_suggestion(
 +                                                line_span,
 +                                                "if you just forgot a `!`, use",
 +                                                sugg,
 +                                                Applicability::MaybeIncorrect,
 +                                            );
 +                                        },
 +                                    );
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if is_relevant_impl(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if is_relevant_trait(cx, item) {
 +            check_attrs(cx, item.span, item.ident.name, cx.tcx.hir().attrs(item.hir_id()));
 +        }
 +    }
 +}
 +
 +/// Returns the lint name if it is clippy lint.
 +fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
 +    if_chain! {
 +        if let Some(meta_item) = lint.meta_item();
 +        if meta_item.path.segments.len() > 1;
 +        if let tool_name = meta_item.path.segments[0].ident;
 +        if tool_name.name == sym::clippy;
 +        then {
 +            let lint_name = meta_item.path.segments.last().unwrap().ident.name;
 +            return Some(lint_name);
 +        }
 +    }
 +    None
 +}
 +
 +fn check_clippy_lint_names(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
 +    for lint in items {
 +        if let Some(lint_name) = extract_clippy_lint(lint) {
 +            if lint_name.as_str() == "restriction" && name != sym::allow {
 +                span_lint_and_help(
 +                    cx,
 +                    BLANKET_CLIPPY_RESTRICTION_LINTS,
 +                    lint.span(),
 +                    "`clippy::restriction` is not meant to be enabled as a group",
 +                    None,
 +                    "enable the restriction lints you need individually",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem], attr: &'_ Attribute) {
 +    // Check for the feature
 +    if !cx.tcx.sess.features_untracked().lint_reasons {
 +        return;
 +    }
 +
 +    // Check if the reason is present
 +    if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
 +        && let MetaItemKind::NameValue(_) = &item.kind
 +        && item.path == sym::reason
 +    {
 +        return;
 +    }
 +
 +    // Check if the attribute is in an external macro and therefore out of the developer's control
 +    if in_external_macro(cx.sess(), attr.span) {
 +        return;
 +    }
 +
 +    span_lint_and_help(
 +        cx,
 +        ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +        attr.span,
 +        &format!("`{}` attribute without specifying a reason", name.as_str()),
 +        None,
 +        "try adding a reason at the end with `, reason = \"..\"`",
 +    );
 +}
 +
 +fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if let ItemKind::Fn(_, _, eid) = item.kind {
 +        is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
 +    } else {
 +        true
 +    }
 +}
 +
 +fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
 +    match item.kind {
 +        ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value),
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
 +    match item.kind {
 +        TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
 +        TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
 +            is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, block: &Block<'_>) -> bool {
 +    block.stmts.first().map_or(
 +        block
 +            .expr
 +            .as_ref()
 +            .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
 +        |stmt| match &stmt.kind {
 +            StmtKind::Local(_) => true,
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
 +            StmtKind::Item(_) => false,
 +        },
 +    )
 +}
 +
 +fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
 +    if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
 +        is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
 +    }) {
 +        return false;
 +    }
 +    match &expr.kind {
 +        ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block),
 +        ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e),
 +        ExprKind::Ret(None) | ExprKind::Break(_, None) => false,
 +        _ => true,
 +    }
 +}
 +
 +fn check_attrs(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) {
 +    if span.from_expansion() {
 +        return;
 +    }
 +
 +    for attr in attrs {
 +        if let Some(values) = attr.meta_item_list() {
 +            if values.len() != 1 || !attr.has_name(sym::inline) {
 +                continue;
 +            }
 +            if is_word(&values[0], sym::always) {
 +                span_lint(
 +                    cx,
 +                    INLINE_ALWAYS,
 +                    attr.span,
 +                    &format!("you have declared `#[inline(always)]` on `{name}`. This is usually a bad idea"),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_semver(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
 +    if let LitKind::Str(is, _) = lit.kind {
 +        if Version::parse(is.as_str()).is_ok() {
 +            return;
 +        }
 +    }
 +    span_lint(
 +        cx,
 +        DEPRECATED_SEMVER,
 +        span,
 +        "the since field must contain a semver-compliant version",
 +    );
 +}
 +
 +fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
 +    if let NestedMetaItem::MetaItem(mi) = &nmi {
 +        mi.is_word() && mi.has_name(expected)
 +    } else {
 +        false
 +    }
 +}
 +
 +pub struct EarlyAttributes {
-         check_deprecated_cfg_attr(cx, attr, self.msrv);
++    pub msrv: Msrv,
 +}
 +
 +impl_lint_pass!(EarlyAttributes => [
 +    DEPRECATED_CFG_ATTR,
 +    MISMATCHED_TARGET_OS,
 +    EMPTY_LINE_AFTER_OUTER_ATTR,
 +]);
 +
 +impl EarlyLintPass for EarlyAttributes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +        check_empty_line_after_outer_attr(cx, item);
 +    }
 +
 +    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
- fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
++        check_deprecated_cfg_attr(cx, attr, &self.msrv);
 +        check_mismatched_target_os(cx, attr);
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
 +    let mut iter = item.attrs.iter().peekable();
 +    while let Some(attr) = iter.next() {
 +        if matches!(attr.kind, AttrKind::Normal(..))
 +            && attr.style == AttrStyle::Outer
 +            && is_present_in_source(cx, attr.span)
 +        {
 +            let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent());
 +            let end_of_attr_to_next_attr_or_item = Span::new(
 +                attr.span.hi(),
 +                iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()),
 +                item.span.ctxt(),
 +                item.span.parent(),
 +            );
 +
 +            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
 +                let lines = snippet.split('\n').collect::<Vec<_>>();
 +                let lines = without_block_comments(lines);
 +
 +                if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 {
 +                    span_lint(
 +                        cx,
 +                        EMPTY_LINE_AFTER_OUTER_ATTR,
 +                        begin_of_attr_to_item,
 +                        "found an empty line after an outer attribute. \
 +                        Perhaps you forgot to add a `!` to make it an inner attribute?",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
-         if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES);
++fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
 +    if_chain! {
++        if msrv.meets(msrvs::TOOL_ATTRIBUTES);
 +        // check cfg_attr
 +        if attr.has_name(sym::cfg_attr);
 +        if let Some(items) = attr.meta_item_list();
 +        if items.len() == 2;
 +        // check for `rustfmt`
 +        if let Some(feature_item) = items[0].meta_item();
 +        if feature_item.has_name(sym::rustfmt);
 +        // check for `rustfmt_skip` and `rustfmt::skip`
 +        if let Some(skip_item) = &items[1].meta_item();
 +        if skip_item.has_name(sym!(rustfmt_skip))
 +            || skip_item
 +                .path
 +                .segments
 +                .last()
 +                .expect("empty path in attribute")
 +                .ident
 +                .name
 +                == sym::skip;
 +        // Only lint outer attributes, because custom inner attributes are unstable
 +        // Tracking issue: https://github.com/rust-lang/rust/issues/54726
 +        if attr.style == AttrStyle::Outer;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DEPRECATED_CFG_ATTR,
 +                attr.span,
 +                "`cfg_attr` is deprecated for rustfmt and got replaced by tool attributes",
 +                "use",
 +                "#[rustfmt::skip]".to_string(),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
 +    fn find_os(name: &str) -> Option<&'static str> {
 +        UNIX_SYSTEMS
 +            .iter()
 +            .chain(NON_UNIX_SYSTEMS.iter())
 +            .find(|&&os| os == name)
 +            .copied()
 +    }
 +
 +    fn is_unix(name: &str) -> bool {
 +        UNIX_SYSTEMS.iter().any(|&os| os == name)
 +    }
 +
 +    fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
 +        let mut mismatched = Vec::new();
 +
 +        for item in items {
 +            if let NestedMetaItem::MetaItem(meta) = item {
 +                match &meta.kind {
 +                    MetaItemKind::List(list) => {
 +                        mismatched.extend(find_mismatched_target_os(list));
 +                    },
 +                    MetaItemKind::Word => {
 +                        if_chain! {
 +                            if let Some(ident) = meta.ident();
 +                            if let Some(os) = find_os(ident.name.as_str());
 +                            then {
 +                                mismatched.push((os, ident.span));
 +                            }
 +                        }
 +                    },
 +                    MetaItemKind::NameValue(..) => {},
 +                }
 +            }
 +        }
 +
 +        mismatched
 +    }
 +
 +    if_chain! {
 +        if attr.has_name(sym::cfg);
 +        if let Some(list) = attr.meta_item_list();
 +        let mismatched = find_mismatched_target_os(&list);
 +        if !mismatched.is_empty();
 +        then {
 +            let mess = "operating system used in target family position";
 +
 +            span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
 +                // Avoid showing the unix suggestion multiple times in case
 +                // we have more than one mismatch for unix-like systems
 +                let mut unix_suggested = false;
 +
 +                for (os, span) in mismatched {
 +                    let sugg = format!("target_os = \"{os}\"");
 +                    diag.span_suggestion(span, "try", sugg, Applicability::MaybeIncorrect);
 +
 +                    if !unix_suggested && is_unix(os) {
 +                        diag.help("did you mean `unix`?");
 +                        unix_suggested = true;
 +                    }
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +fn is_lint_level(symbol: Symbol) -> bool {
 +    matches!(symbol, sym::allow | sym::expect | sym::warn | sym::deny | sym::forbid)
 +}
index 3f1edabe6c5040621bb2903b0df2e2ea07843270,0000000000000000000000000000000000000000..442262983337686fab42a2e0482d41cba26cdf8f
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,43 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::msrvs::{self, Msrv};
 +use clippy_utils::sugg::Sugg;
- use rustc_semver::RustcVersion;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
-     msrv: Option<RustcVersion>,
 +
 +use super::CAST_ABS_TO_UNSIGNED;
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    cast_expr: &Expr<'_>,
 +    cast_from: Ty<'_>,
 +    cast_to: Ty<'_>,
-     if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
++    msrv: &Msrv,
 +) {
++    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
 +        && method_path.ident.name.as_str() == "abs"
 +    {
 +        let span = if from.bit_width() == to.bit_width() {
 +            expr.span
 +        } else {
 +            // if the result of `.unsigned_abs` would be a different type, keep the cast
 +            // e.g. `i64 -> usize`, `i16 -> u8`
 +            cast_expr.span
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            CAST_ABS_TO_UNSIGNED,
 +            span,
 +            &format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
 +            "replace with",
 +            format!("{}.unsigned_abs()", Sugg::hir(cx, receiver, "..").maybe_par()),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 13c403234dad5e8b44a41dfe87b99a5f24d25b4b,0000000000000000000000000000000000000000..cf07e050ccce99e6966ae2d9b2ec55a32df8067e
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,100 @@@
- use clippy_utils::{in_constant, meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, FloatTy, Ty};
-     msrv: Option<RustcVersion>,
 +
 +use super::{utils, CAST_LOSSLESS};
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    cast_op: &Expr<'_>,
 +    cast_from: Ty<'_>,
 +    cast_to: Ty<'_>,
- fn should_lint(
-     cx: &LateContext<'_>,
-     expr: &Expr<'_>,
-     cast_from: Ty<'_>,
-     cast_to: Ty<'_>,
-     msrv: Option<RustcVersion>,
- ) -> bool {
++    msrv: &Msrv,
 +) {
 +    if !should_lint(cx, expr, cast_from, cast_to, msrv) {
 +        return;
 +    }
 +
 +    // The suggestion is to use a function call, so if the original expression
 +    // has parens on the outside, they are no longer needed.
 +    let mut applicability = Applicability::MachineApplicable;
 +    let opt = snippet_opt(cx, cast_op.span);
 +    let sugg = opt.as_ref().map_or_else(
 +        || {
 +            applicability = Applicability::HasPlaceholders;
 +            ".."
 +        },
 +        |snip| {
 +            if should_strip_parens(cast_op, snip) {
 +                &snip[1..snip.len() - 1]
 +            } else {
 +                snip.as_str()
 +            }
 +        },
 +    );
 +
 +    let message = if cast_from.is_bool() {
 +        format!("casting `{cast_from:}` to `{cast_to:}` is more cleanly stated with `{cast_to:}::from(_)`")
 +    } else {
 +        format!("casting `{cast_from}` to `{cast_to}` may become silently lossy if you later change the type")
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        CAST_LOSSLESS,
 +        expr.span,
 +        &message,
 +        "try",
 +        format!("{cast_to}::from({sugg})"),
 +        applicability,
 +    );
 +}
 +
-         (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true,
++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;
 +    }
 +
 +    match (cast_from.is_integral(), cast_to.is_integral()) {
 +        (true, true) => {
 +            let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
 +            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
 +            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 +            !is_isize_or_usize(cast_from)
 +                && !is_isize_or_usize(cast_to)
 +                && from_nbits < to_nbits
 +                && !cast_signed_to_unsigned
 +        },
 +
 +        (true, false) => {
 +            let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
 +            let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
 +                32
 +            } else {
 +                64
 +            };
 +            !is_isize_or_usize(cast_from) && from_nbits < to_nbits
 +        },
++        (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))
 +        },
 +    }
 +}
 +
 +fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool {
 +    if let ExprKind::Binary(_, _, _) = cast_expr.kind {
 +        if snip.starts_with('(') && snip.ends_with(')') {
 +            return true;
 +        }
 +    }
 +    false
 +}
index adbcfd3189b76d0fad22532f0e767fe7e63a7932,0000000000000000000000000000000000000000..a6376484914ba11dad3b13e0ef9fe649a9f8493c
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,157 @@@
-             let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
-                 matches!(
-                     ty,
-                     IntegerType::Pointer(_),
-                 )
-             });
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::expr_or_init;
 +use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, FloatTy, Ty};
 +use rustc_target::abi::IntegerType;
 +
 +use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
 +
 +fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
 +    if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
 +        Some(c)
 +    } else {
 +        None
 +    }
 +}
 +
 +fn get_constant_bits(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u64> {
 +    constant_int(cx, expr).map(|c| u64::from(128 - c.leading_zeros()))
 +}
 +
 +fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: bool) -> u64 {
 +    match expr_or_init(cx, expr).kind {
 +        ExprKind::Cast(inner, _) => apply_reductions(cx, nbits, inner, signed),
 +        ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
 +        ExprKind::Binary(op, left, right) => match op.node {
 +            BinOpKind::Div => {
 +                apply_reductions(cx, nbits, left, signed).saturating_sub(if signed {
 +                    // let's be conservative here
 +                    0
 +                } else {
 +                    // by dividing by 1, we remove 0 bits, etc.
 +                    get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
 +                })
 +            },
 +            BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
 +                .unwrap_or(u64::max_value())
 +                .min(apply_reductions(cx, nbits, left, signed)),
 +            BinOpKind::Shr => apply_reductions(cx, nbits, left, signed)
 +                .saturating_sub(constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))),
 +            _ => nbits,
 +        },
 +        ExprKind::MethodCall(method, left, [right], _) => {
 +            if signed {
 +                return nbits;
 +            }
 +            let max_bits = if method.ident.as_str() == "min" {
 +                get_constant_bits(cx, right)
 +            } else {
 +                None
 +            };
 +            apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
 +        },
 +        ExprKind::MethodCall(method, _, [lo, hi], _) => {
 +            if method.ident.as_str() == "clamp" {
 +                //FIXME: make this a diagnostic item
 +                if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
 +                    return lo_bits.max(hi_bits);
 +                }
 +            }
 +            nbits
 +        },
 +        ExprKind::MethodCall(method, _value, [], _) => {
 +            if method.ident.name.as_str() == "signum" {
 +                0 // do not lint if cast comes from a `signum` function
 +            } else {
 +                nbits
 +            }
 +        },
 +        _ => nbits,
 +    }
 +}
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
 +    let msg = match (cast_from.kind(), cast_to.is_integral()) {
 +        (ty::Int(_) | ty::Uint(_), true) => {
 +            let from_nbits = apply_reductions(
 +                cx,
 +                utils::int_ty_to_nbits(cast_from, cx.tcx),
 +                cast_expr,
 +                cast_from.is_signed(),
 +            );
 +            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 +
 +            let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
 +                (true, true) | (false, false) => (to_nbits < from_nbits, ""),
 +                (true, false) => (
 +                    to_nbits <= 32,
 +                    if to_nbits == 32 {
 +                        " on targets with 64-bit wide pointers"
 +                    } else {
 +                        ""
 +                    },
 +                ),
 +                (false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"),
 +            };
 +
 +            if !should_lint {
 +                return;
 +            }
 +
 +            format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
 +        },
 +
 +        (ty::Adt(def, _), true) if def.is_enum() => {
 +            let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
 +                && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
 +            {
 +                let i = def.variant_index_with_ctor_id(id);
 +                let variant = def.variant(i);
 +                let nbits = utils::enum_value_nbits(get_discriminant_value(cx.tcx, *def, i));
 +                (nbits, Some(variant))
 +            } else {
 +                (utils::enum_ty_to_nbits(*def, cx.tcx), None)
 +            };
 +            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 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 => "",
 +                (false, true) if from_nbits > 64 => "",
 +                (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
 +                _ => return,
 +            };
 +
 +            if let Some(variant) = variant {
 +                span_lint(
 +                    cx,
 +                    CAST_ENUM_TRUNCATION,
 +                    expr.span,
 +                    &format!(
 +                        "casting `{cast_from}::{}` to `{cast_to}` will truncate the value{suffix}",
 +                        variant.name,
 +                    ),
 +                );
 +                return;
 +            }
 +            format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
 +        },
 +
 +        (ty::Float(_), true) => {
 +            format!("casting `{cast_from}` to `{cast_to}` may truncate the value")
 +        },
 +
 +        (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
 +            "casting `f64` to `f32` may truncate the value".to_string()
 +        },
 +
 +        _ => return,
 +    };
 +
 +    span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
 +}
index d31d10d22b92b8997a77631d71d86ddc21dcdd38,0000000000000000000000000000000000000000..e862f13e69fc7059011014ad82f3310e8666ea73
mode 100644,000000..100644
--- /dev/null
@@@ -1,143 -1,0 +1,143 @@@
- 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;
 +    }
 +
 +    // if this cast is the child of another cast expression then don't emit something for it, the full
 +    // chain will be analyzed
 +    if is_child_of_cast(cx, expr) {
 +        return;
 +    }
 +
 +    if let Some(CastChainInfo {
 +        left_cast,
 +        start_ty,
 +        end_ty,
 +    }) = expr_cast_chain_tys(cx, expr)
 +    {
 +        if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(start_ty.ty), cx.layout_of(end_ty.ty)) {
 +            let from_size = from_layout.size.bytes();
 +            let to_size = to_layout.size.bytes();
 +            if from_size != to_size && from_size != 0 && to_size != 0 {
 +                span_lint_and_then(
 +                    cx,
 +                    CAST_SLICE_DIFFERENT_SIZES,
 +                    expr.span,
 +                    &format!(
 +                        "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
 +                        start_ty.ty, end_ty.ty,
 +                    ),
 +                    |diag| {
 +                        let ptr_snippet = source::snippet(cx, left_cast.span, "..");
 +
 +                        let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
 +                            Mutability::Mut => ("_mut", "mut"),
 +                            Mutability::Not => ("", "const"),
 +                        };
 +                        let sugg = format!(
 +                            "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
 +                            // get just the ty from the TypeAndMut so that the printed type isn't something like `mut
 +                            // T`, extract just the `T`
 +                            end_ty.ty
 +                        );
 +
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
 +                            sugg,
 +                            rustc_errors::Applicability::HasPlaceholders,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let map = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(parent_id) = map.find_parent_node(expr.hir_id);
 +        if let Some(parent) = map.find(parent_id);
 +        then {
 +            let expr = match parent {
 +                Node::Block(block) => {
 +                    if let Some(parent_expr) = block.expr {
 +                        parent_expr
 +                    } else {
 +                        return false;
 +                    }
 +                },
 +                Node::Expr(expr) => expr,
 +                _ => return false,
 +            };
 +
 +            matches!(expr.kind, ExprKind::Cast(..))
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
 +/// the type is one of those slices
 +fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
 +    match ty.kind() {
 +        ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() {
 +            ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
 +            _ => None,
 +        },
 +        _ => None,
 +    }
 +}
 +
 +struct CastChainInfo<'tcx> {
 +    /// The left most part of the cast chain, or in other words, the first cast in the chain
 +    /// Used for diagnostics
 +    left_cast: &'tcx Expr<'tcx>,
 +    /// The starting type of the cast chain
 +    start_ty: TypeAndMut<'tcx>,
 +    /// The final type of the cast chain
 +    end_ty: TypeAndMut<'tcx>,
 +}
 +
 +/// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final
 +/// ptr U if the expression is composed of casts.
 +/// Returns None if the expr is not a Cast
 +fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<CastChainInfo<'tcx>> {
 +    if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
 +        let cast_to = cx.typeck_results().expr_ty(expr);
 +        let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
 +
 +        // If the expression that makes up the source of this cast is itself a cast, recursively
 +        // call `expr_cast_chain_tys` and update the end type with the final target type.
 +        // Otherwise, this cast is not immediately nested, just construct the info for this cast
 +        if let Some(prev_info) = expr_cast_chain_tys(cx, cast_expr) {
 +            Some(CastChainInfo {
 +                end_ty: to_slice_ty,
 +                ..prev_info
 +            })
 +        } else {
 +            let cast_from = cx.typeck_results().expr_ty(cast_expr);
 +            let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
 +            Some(CastChainInfo {
 +                left_cast: cast_expr,
 +                start_ty: from_slice_ty,
 +                end_ty: to_slice_ty,
 +            })
 +        }
 +    } else {
 +        None
 +    }
 +}
index 284ef165998a7f46cbfaa7b9060cb81dc42fe9a7,0000000000000000000000000000000000000000..627b795d6edd8f5ea04603bfead49042878ff7c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,57 @@@
- use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::msrvs::{self, Msrv};
 +use clippy_utils::source::snippet_with_applicability;
- use rustc_semver::RustcVersion;
++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};
- pub(super) fn check(
-     cx: &LateContext<'_>,
-     expr: &Expr<'_>,
-     cast_expr: &Expr<'_>,
-     cast_to: Ty<'_>,
-     msrv: Option<RustcVersion>,
- ) {
 +
 +use super::CAST_SLICE_FROM_RAW_PARTS;
 +
 +enum RawPartsKind {
 +    Immutable,
 +    Mutable,
 +}
 +
 +fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
 +    if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
 +        Some(RawPartsKind::Immutable)
 +    } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
 +        Some(RawPartsKind::Mutable)
 +    } else {
 +        None
 +    }
 +}
 +
-         if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
 +    if_chain! {
++        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;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
 +        then {
 +            let func = match rpk {
 +                RawPartsKind::Immutable => "from_raw_parts",
 +                RawPartsKind::Mutable => "from_raw_parts_mut"
 +            };
 +            let span = expr.span;
 +            let mut applicability = Applicability::MachineApplicable;
 +            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
 +            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
 +            span_lint_and_sugg(
 +                cx,
 +                CAST_SLICE_FROM_RAW_PARTS,
 +                span,
 +                &format!("casting the result of `{func}` to {cast_to}"),
 +                "replace with",
 +                format!("core::ptr::slice_{func}({ptr}, {len})"),
 +                applicability
 +            );
 +        }
 +    }
 +}
index 7148b5e6ebf45e09c2da8693aaf6184410ec6bbc,0000000000000000000000000000000000000000..c6d505c4a181fc2e3b52c2be3b1d713f7123eec2
mode 100644,000000..100644
--- /dev/null
@@@ -1,742 -1,0 +1,742 @@@
- use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
 +mod as_ptr_cast_mut;
 +mod as_underscore;
 +mod borrow_as_ptr;
 +mod cast_abs_to_unsigned;
 +mod cast_enum_constructor;
 +mod cast_lossless;
 +mod cast_nan_to_int;
 +mod cast_possible_truncation;
 +mod cast_possible_wrap;
 +mod cast_precision_loss;
 +mod cast_ptr_alignment;
 +mod cast_ref_to_mut;
 +mod cast_sign_loss;
 +mod cast_slice_different_sizes;
 +mod cast_slice_from_raw_parts;
 +mod char_lit_as_u8;
 +mod fn_to_numeric_cast;
 +mod fn_to_numeric_cast_any;
 +mod fn_to_numeric_cast_with_truncation;
 +mod ptr_as_ptr;
 +mod unnecessary_cast;
 +mod utils;
 +
- use rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from any numerical to a float type where
 +    /// the receiving type cannot store all values from the original type without
 +    /// rounding errors. This possible rounding is to be expected, so this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
 +    /// or any 64-bit integer to `f64`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad at all. But in some applications it can be
 +    /// helpful to know where precision loss can take place. This lint can help find
 +    /// those places in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = u64::MAX;
 +    /// x as f64;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PRECISION_LOSS,
 +    pedantic,
 +    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from a signed to an unsigned numerical
 +    /// type. In this case, negative values wrap around to large positive values,
 +    /// which can be quite surprising in practice. However, as the cast works as
 +    /// defined, this lint is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// Possibly surprising results. You can activate this lint
 +    /// as a one-time check to see where numerical wrapping can arise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let y: i8 = -1;
 +    /// y as u128; // will return 18446744073709551615
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_SIGN_LOSS,
 +    pedantic,
 +    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// truncate large values. This is expected behavior, so the cast is `Allow` by
 +    /// default.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some problem domains, it is good practice to avoid
 +    /// truncation. This lint can be activated to help assess where additional
 +    /// checks could be beneficial.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u8(x: u64) -> u8 {
 +    ///     x as u8
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_TRUNCATION,
 +    pedantic,
 +    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an unsigned type to a signed type of
 +    /// the same size. Performing such a cast is a 'no-op' for the compiler,
 +    /// i.e., nothing is changed at the bit level, and the binary representation of
 +    /// the value is reinterpreted. This can cause wrapping if the value is too big
 +    /// for the target signed type. However, the cast works as defined, so this lint
 +    /// is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// While such a cast is not bad in itself, the results can
 +    /// be surprising when this is not the intended behavior, as demonstrated by the
 +    /// example below.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// u32::MAX as i32; // will yield a value of `-1`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_WRAP,
 +    pedantic,
 +    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// be replaced by safe conversion functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust's `as` keyword will perform many kinds of
 +    /// conversions, including silently lossy conversions. Conversion functions such
 +    /// as `i32::from` will only perform lossless conversions. Using the conversion
 +    /// functions prevents conversions from turning into silent lossy conversions if
 +    /// the types of the input expressions ever change, and make it easier for
 +    /// people reading the code to know that the conversion is lossless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     x as u64
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `::from` would look like this:
 +    ///
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     u64::from(x)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_LOSSLESS,
 +    pedantic,
 +    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts to the same type, casts of int literals to integer types
 +    /// and casts of float literals to float types.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's just unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 2i32 as i32;
 +    /// let _ = 0.5 as f32;
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// let _ = 2_i32;
 +    /// let _ = 0.5_f32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_CAST,
 +    complexity,
 +    "cast to the same type, e.g., `x as i32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts, using `as` or `pointer::cast`,
 +    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing the resulting pointer may be undefined
 +    /// behavior.
 +    ///
 +    /// ### Known problems
 +    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
 +    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
 +    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (&1u8 as *const u8) as *const u16;
 +    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
 +    ///
 +    /// (&1u8 as *const u8).cast::<u16>();
 +    /// (&mut 1u8 as *mut u8).cast::<u16>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PTR_ALIGNMENT,
 +    pedantic,
 +    "cast from a pointer to a more-strictly-aligned pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of function pointers to something other than usize
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to anything other than usize/isize is not portable across
 +    /// architectures, because you end up losing bits if the target type is too small or end up with a
 +    /// bunch of extra bits that waste space and add more instructions to the final binary than
 +    /// strictly necessary for the problem
 +    ///
 +    /// Casting to isize also doesn't make sense since there are no signed addresses.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun() -> i32 { 1 }
 +    /// let _ = fun as i64;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun() -> i32 { 1 }
 +    /// let _ = fun as usize;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST,
 +    style,
 +    "casting a function pointer to a numeric type other than usize"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to a numeric type not wide enough to
 +    /// store address.
 +    ///
 +    /// ### Why is this bad?
 +    /// Such a cast discards some bits of the function's address. If this is intended, it would be more
 +    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
 +    /// a comment) to perform the truncation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as i32;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Cast to usize first, then comment with the reason for the truncation
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let fn_ptr = fn1 as usize;
 +    /// let fn_ptr_truncated = fn_ptr as i32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    style,
 +    "casting a function pointer to a numeric type not wide enough to store the address"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to any integer type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to an integer can have surprising results and can occur
 +    /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
 +    /// low-level with function pointers then you can opt-out of casting functions to integers in
 +    /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
 +    /// pointer casts in your code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // fn1 is cast as `usize`
 +    /// fn fn1() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as usize;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // maybe you intended to call the function?
 +    /// fn fn2() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn2() as usize;
 +    ///
 +    /// // or
 +    ///
 +    /// // maybe you intended to cast it to a function type?
 +    /// fn fn3() -> u16 {
 +    ///     1
 +    /// }
 +    /// let _ = fn3 as fn() -> u16;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FN_TO_NUMERIC_CAST_ANY,
 +    restriction,
 +    "casting a function pointer to any integer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of `&T` to `&mut T` anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// It’s basically guaranteed to be undefined behavior.
 +    /// `UnsafeCell` is the only way to obtain aliasable data that is considered
 +    /// mutable.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn x(r: &i32) {
 +    ///     unsafe {
 +    ///         *(r as *const _ as *mut _) += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Instead consider using interior mutability types.
 +    ///
 +    /// ```rust
 +    /// use std::cell::UnsafeCell;
 +    ///
 +    /// fn x(r: &UnsafeCell<i32>) {
 +    ///     unsafe {
 +    ///         *r.get() += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub CAST_REF_TO_MUT,
 +    correctness,
 +    "a cast of reference to a mutable pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where a character literal is cast
 +    /// to `u8` and suggests using a byte literal instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, casting values to smaller types is
 +    /// error-prone and should be avoided where possible. In the particular case of
 +    /// converting a character literal to u8, it is easy to avoid by just using a
 +    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
 +    /// than `'a' as u8`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 'x' as u8
 +    /// ```
 +    ///
 +    /// A better version, using the byte literal:
 +    ///
 +    /// ```rust,ignore
 +    /// b'x'
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHAR_LIT_AS_U8,
 +    complexity,
 +    "casting a character literal to `u8` truncates"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers without changing its mutability,
 +    /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
 +    /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr as *const i32;
 +    /// let _ = mut_ptr as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr.cast::<i32>();
 +    /// let _ = mut_ptr.cast::<i32>();
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub PTR_AS_PTR,
 +    pedantic,
 +    "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum type to an integral type which will definitely truncate the
 +    /// value.
 +    ///
 +    /// ### Why is this bad?
 +    /// The resulting integral value will not match the value of the variant it came from.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X = 256 };
 +    /// let _ = E::X as u8;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_ENUM_TRUNCATION,
 +    suspicious,
 +    "casts from an enum type to an integral type which will truncate the value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers to slices with differently sized elements.
 +    ///
 +    /// ### Why is this bad?
 +    /// The produced raw pointer to a slice does not update its length metadata. The produced
 +    /// pointer will point to a different number of bytes than the original pointer because the
 +    /// length metadata of a raw slice pointer is in elements rather than bytes.
 +    /// Producing a slice reference from the raw pointer will either create a slice with
 +    /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
 +    ///
 +    /// ### Example
 +    /// // Missing data
 +    /// ```rust
 +    /// let a = [1_i32, 2, 3, 4];
 +    /// let p = &a as *const [i32] as *const [u8];
 +    /// unsafe {
 +    ///     println!("{:?}", &*p);
 +    /// }
 +    /// ```
 +    /// // Undefined Behavior (note: also potential alignment issues)
 +    /// ```rust
 +    /// let a = [1_u8, 2, 3, 4];
 +    /// let p = &a as *const [u8] as *const [u32];
 +    /// unsafe {
 +    ///     println!("{:?}", &*p);
 +    /// }
 +    /// ```
 +    /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
 +    /// ```rust
 +    /// let a = [1_i32, 2, 3, 4];
 +    /// let old_ptr = &a as *const [i32];
 +    /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
 +    /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
 +    /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
 +    /// unsafe {
 +    ///     println!("{:?}", &*new_ptr);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_SLICE_DIFFERENT_SIZES,
 +    correctness,
 +    "casting using `as` between raw pointers to slices of types with different sizes"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum tuple constructor to an integer.
 +    ///
 +    /// ### Why is this bad?
 +    /// The cast is easily confused with casting a c-like enum value to an integer.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X(i32) };
 +    /// let _ = E::X as usize;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_ENUM_CONSTRUCTOR,
 +    suspicious,
 +    "casts from an enum tuple constructor to an integer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of the `abs()` method that cast the result to unsigned.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `unsigned_abs()` method avoids panic when called on the MIN value.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = -42;
 +    /// let y: u32 = x.abs() as u32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: i32 = -42;
 +    /// let y: u32 = x.unsigned_abs();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub CAST_ABS_TO_UNSIGNED,
 +    suspicious,
 +    "casting the result of `abs()` to an unsigned integer can panic"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for the usage of `as _` conversion using inferred type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The conversion might include lossy conversion and dangerous cast that might go
 +    /// undetected due to the type being inferred.
 +    ///
 +    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(n: usize) {}
 +    /// let n: u16 = 256;
 +    /// foo(n as _);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo(n: usize) {}
 +    /// let n: u16 = 256;
 +    /// foo(n as usize);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub AS_UNDERSCORE,
 +    restriction,
 +    "detects `as _` conversion"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `&expr as *const T` or
 +    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
 +    /// `ptr::addr_of_mut` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// This would improve readability and avoid creating a reference
 +    /// that points to an uninitialized value or unaligned place.
 +    /// Read the `ptr::addr_of` docs for more information.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let val = 1;
 +    /// let p = &val as *const i32;
 +    ///
 +    /// let mut val_mut = 1;
 +    /// let p_mut = &mut val_mut as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let val = 1;
 +    /// let p = std::ptr::addr_of!(val);
 +    ///
 +    /// let mut val_mut = 1;
 +    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub BORROW_AS_PTR,
 +    pedantic,
 +    "borrowing just to cast to a raw pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a raw slice being cast to a slice pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// This can result in multiple `&mut` references to the same location when only a pointer is
 +    /// required.
 +    /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
 +    /// the same [safety requirements] to be upheld.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
 +    /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
 +    /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
 +    /// ```
 +    /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
 +    #[clippy::version = "1.65.0"]
 +    pub CAST_SLICE_FROM_RAW_PARTS,
 +    suspicious,
 +    "casting a slice created from a pointer and length to a slice pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
 +    /// mutability is used, making it unlikely that having it as a mutable pointer is correct.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let string = String::with_capacity(1);
 +    /// let ptr = string.as_ptr() as *mut u8;
 +    /// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut string = String::with_capacity(1);
 +    /// let ptr = string.as_mut_ptr();
 +    /// unsafe { ptr.write(4) };
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub AS_PTR_CAST_MUT,
 +    nursery,
 +    "casting the result of the `&self`-taking `as_ptr` to a mutabe pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a known NaN float being cast to an integer
 +    ///
 +    /// ### Why is this bad?
 +    /// NaNs are cast into zero, so one could simply use this and make the
 +    /// code more readable. The lint could also hint at a programmer error.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _: (0.0_f32 / 0.0) as u64;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _: = 0_u64;
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub CAST_NAN_TO_INT,
 +    suspicious,
 +    "casting a known floating-point NaN into an integer"
 +}
 +
 +pub struct Casts {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl Casts {
 +    #[must_use]
-             ptr_as_ptr::check(cx, expr, self.msrv);
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Casts => [
 +    CAST_PRECISION_LOSS,
 +    CAST_SIGN_LOSS,
 +    CAST_POSSIBLE_TRUNCATION,
 +    CAST_POSSIBLE_WRAP,
 +    CAST_LOSSLESS,
 +    CAST_REF_TO_MUT,
 +    CAST_PTR_ALIGNMENT,
 +    CAST_SLICE_DIFFERENT_SIZES,
 +    UNNECESSARY_CAST,
 +    FN_TO_NUMERIC_CAST_ANY,
 +    FN_TO_NUMERIC_CAST,
 +    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    CHAR_LIT_AS_U8,
 +    PTR_AS_PTR,
 +    CAST_ENUM_TRUNCATION,
 +    CAST_ENUM_CONSTRUCTOR,
 +    CAST_ABS_TO_UNSIGNED,
 +    AS_UNDERSCORE,
 +    BORROW_AS_PTR,
 +    CAST_SLICE_FROM_RAW_PARTS,
 +    AS_PTR_CAST_MUT,
 +    CAST_NAN_TO_INT,
 +]);
 +
 +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) {
-             cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
++            ptr_as_ptr::check(cx, expr, &self.msrv);
 +        }
 +
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
 +            if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
 +                return;
 +            }
 +            let (cast_from, cast_to) = (
 +                cx.typeck_results().expr_ty(cast_expr),
 +                cx.typeck_results().expr_ty(expr),
 +            );
 +
 +            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
 +                return;
 +            }
-                     cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, 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);
 +            fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +
 +            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
 +                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +                if cast_from.is_numeric() {
 +                    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_lossless::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);
 +                }
-             if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
++                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);
 +
-         ptr_as_ptr::check(cx, expr, self.msrv);
-         cast_slice_different_sizes::check(cx, expr, self.msrv);
++            if self.msrv.meets(msrvs::BORROW_AS_PTR) {
 +                borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
 +            }
 +        }
 +
 +        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);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index b9509ca656f7a170117e115cf0a6cd47023eee80,0000000000000000000000000000000000000000..15ffb00da88bacd80d7bb5016cde42db203ba221
mode 100644,000000..100644
--- /dev/null
@@@ -1,49 -1,0 +1,48 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +use std::borrow::Cow;
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::msrvs::{self, Msrv};
 +use clippy_utils::sugg::Sugg;
- use rustc_semver::RustcVersion;
 +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};
- pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) {
-     if !meets_msrv(msrv, msrvs::POINTER_CAST) {
 +
 +use super::PTR_AS_PTR;
 +
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
++    if !msrv.meets(msrvs::POINTER_CAST) {
 +        return;
 +    }
 +
 +    if_chain! {
 +        if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind;
 +        let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr));
 +        if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind();
 +        if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind();
 +        if matches!((from_mutbl, to_mutbl),
 +            (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut));
 +        // The `U` in `pointer::cast` have to be `Sized`
 +        // as explained here: https://github.com/rust-lang/rust/issues/60602.
 +        if to_pointee_ty.is_sized(cx.tcx, cx.param_env);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability);
 +            let turbofish = match &cast_to_hir_ty.kind {
 +                    TyKind::Infer => Cow::Borrowed(""),
 +                    TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""),
 +                    _ => Cow::Owned(format!("::<{to_pointee_ty}>")),
 +                };
 +            span_lint_and_sugg(
 +                cx,
 +                PTR_AS_PTR,
 +                expr.span,
 +                "`as` casting between raw pointers without changing its mutability",
 +                "try `pointer::cast`, a safer alternative",
 +                format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()),
 +                applicability,
 +            );
 +        }
 +    }
 +}
index c8596987e4d719b158aa879d7f1bc2bf452d4c16,0000000000000000000000000000000000000000..7e23318076cf2e8b9aa6ec5247b39d2268c3ca38
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,164 @@@
- use clippy_utils::get_parent_expr;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-             cast_str,
 +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;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
 +
 +use super::UNNECESSARY_CAST;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'tcx>,
 +    cast_expr: &Expr<'tcx>,
 +    cast_from: Ty<'tcx>,
 +    cast_to: Ty<'tcx>,
 +) -> bool {
 +    // skip non-primitive type cast
 +    if_chain! {
 +        if let ExprKind::Cast(_, cast_to) = expr.kind;
 +        if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
 +        if let Res::PrimTy(_) = path.res;
 +        then {}
 +        else {
 +            return false
 +        }
 +    }
 +
 +    let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
 +
 +    if let Some(lit) = get_numeric_literal(cast_expr) {
 +        let literal_str = &cast_str;
 +
 +        if_chain! {
 +            if let LitKind::Int(n, _) = lit.node;
 +            if let Some(src) = snippet_opt(cx, cast_expr.span);
 +            if cast_to.is_floating_point();
 +            if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
 +            let from_nbits = 128 - n.leading_zeros();
 +            let to_nbits = fp_ty_mantissa_nbits(cast_to);
 +            if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
 +            then {
 +                lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
 +                return true
 +            }
 +        }
 +
 +        match lit.node {
 +            LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
 +                lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
 +                return false;
 +            },
 +            LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
 +                lint_unnecessary_cast(cx, expr, literal_str, cast_from, cast_to);
 +                return false;
 +            },
 +            LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
 +            | LitKind::Float(_, LitFloatType::Suffixed(_))
 +                if cast_from.kind() == cast_to.kind() =>
 +            {
 +                if let Some(src) = snippet_opt(cx, cast_expr.span) {
 +                    if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
 +                        lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
 +                        return true;
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    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",
++            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;
 +    }
 +
 +    false
 +}
 +
 +fn lint_unnecessary_cast(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    raw_literal_str: &str,
 +    cast_from: Ty<'_>,
 +    cast_to: Ty<'_>,
 +) {
 +    let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
 +    // first we remove all matches so `-(1)` become `-1`, and remove trailing dots, so `1.` become `1`
 +    let literal_str = raw_literal_str
 +        .replace(['(', ')'], "")
 +        .trim_end_matches('.')
 +        .to_string();
 +    // we know need to check if the parent is a method call, to add parenthesis accordingly (eg:
 +    // (-1).foo() instead of -1.foo())
 +    let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr)
 +        && let ExprKind::MethodCall(..) = parent_expr.kind
 +        && literal_str.starts_with('-')
 +        {
 +            format!("({literal_str}_{cast_to})")
 +
 +        } else {
 +            format!("{literal_str}_{cast_to}")
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        UNNECESSARY_CAST,
 +        expr.span,
 +        &format!("casting {literal_kind_name} literal to `{cast_to}` is unnecessary"),
 +        "try",
 +        sugg,
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
 +    match expr.kind {
 +        ExprKind::Lit(ref lit) => Some(lit),
 +        ExprKind::Unary(UnOp::Neg, e) => {
 +            if let ExprKind::Lit(ref lit) = e.kind {
 +                Some(lit)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns the mantissa bits wide of a fp type.
 +/// Will return 0 if the type is not a fp
 +fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
 +    match typ.kind() {
 +        ty::Float(FloatTy::F32) => 23,
 +        ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52,
 +        _ => 0,
 +    }
 +}
index 78e9921f036f3369bd3f5768bb96227c01430fcd,0000000000000000000000000000000000000000..9102a89e37726ab035c349a6768c25bddd0cb796
mode 100644,000000..100644
--- /dev/null
@@@ -1,341 -1,0 +1,341 @@@
- use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq};
 +//! 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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit bounds checking when casting.
 +    ///
 +    /// ### Why is this bad?
 +    /// Reduces the readability of statements & is error prone.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo: u32 = 5;
 +    /// foo <= i32::MAX as u32;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = 1;
 +    /// # #[allow(unused)]
 +    /// i32::try_from(foo).is_ok();
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub CHECKED_CONVERSIONS,
 +    pedantic,
 +    "`try_from` could replace manual bounds checking when casting"
 +}
 +
 +pub struct CheckedConversions {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl CheckedConversions {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
++        if !self.msrv.meets(msrvs::TRY_FROM) {
 +            return;
 +        }
 +
 +        let result = if_chain! {
 +            if !in_constant(cx, item.hir_id);
 +            if !in_external_macro(cx.sess(), item.span);
 +            if let ExprKind::Binary(op, left, right) = &item.kind;
 +
 +            then {
 +                match op.node {
 +                    BinOpKind::Ge | BinOpKind::Le => single_check(item),
 +                    BinOpKind::And => double_check(cx, left, right),
 +                    _ => None,
 +                }
 +            } else {
 +                None
 +            }
 +        };
 +
 +        if let Some(cv) = result {
 +            if let Some(to_type) = cv.to_type {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
 +                span_lint_and_sugg(
 +                    cx,
 +                    CHECKED_CONVERSIONS,
 +                    item.span,
 +                    "checked cast can be simplified",
 +                    "try",
 +                    format!("{to_type}::try_from({snippet}).is_ok()"),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Searches for a single check from unsigned to _ is done
 +/// todo: check for case signed -> larger unsigned == only x >= 0
 +fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
 +    check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned)
 +}
 +
 +/// Searches for a combination of upper & lower bound checks
 +fn double_check<'a>(cx: &LateContext<'_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> {
 +    let upper_lower = |l, r| {
 +        let upper = check_upper_bound(l);
 +        let lower = check_lower_bound(r);
 +
 +        upper.zip(lower).and_then(|(l, r)| l.combine(r, cx))
 +    };
 +
 +    upper_lower(left, right).or_else(|| upper_lower(right, left))
 +}
 +
 +/// Contains the result of a tried conversion check
 +#[derive(Clone, Debug)]
 +struct Conversion<'a> {
 +    cvt: ConversionType,
 +    expr_to_cast: &'a Expr<'a>,
 +    to_type: Option<&'a str>,
 +}
 +
 +/// The kind of conversion that is checked
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +enum ConversionType {
 +    SignedToUnsigned,
 +    SignedToSigned,
 +    FromUnsigned,
 +}
 +
 +impl<'a> Conversion<'a> {
 +    /// Combine multiple conversions if the are compatible
 +    pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>> {
 +        if self.is_compatible(&other, cx) {
 +            // Prefer a Conversion that contains a type-constraint
 +            Some(if self.to_type.is_some() { self } else { other })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    /// Checks if two conversions are compatible
 +    /// same type of conversion, same 'castee' and same 'to type'
 +    pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_>) -> bool {
 +        (self.cvt == other.cvt)
 +            && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast))
 +            && (self.has_compatible_to_type(other))
 +    }
 +
 +    /// Checks if the to-type is the same (if there is a type constraint)
 +    fn has_compatible_to_type(&self, other: &Self) -> bool {
 +        match (self.to_type, other.to_type) {
 +            (Some(l), Some(r)) => l == r,
 +            _ => true,
 +        }
 +    }
 +
 +    /// Try to construct a new conversion if the conversion type is valid
 +    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option<Conversion<'a>> {
 +        ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
 +            cvt,
 +            expr_to_cast,
 +            to_type: Some(to_type),
 +        })
 +    }
 +
 +    /// Construct a new conversion without type constraint
 +    fn new_any(expr_to_cast: &'a Expr<'_>) -> Conversion<'a> {
 +        Conversion {
 +            cvt: ConversionType::SignedToUnsigned,
 +            expr_to_cast,
 +            to_type: None,
 +        }
 +    }
 +}
 +
 +impl ConversionType {
 +    /// Creates a conversion type if the type is allowed & conversion is valid
 +    #[must_use]
 +    fn try_new(from: &str, to: &str) -> Option<Self> {
 +        if UINTS.contains(&from) {
 +            Some(Self::FromUnsigned)
 +        } else if SINTS.contains(&from) {
 +            if UINTS.contains(&to) {
 +                Some(Self::SignedToUnsigned)
 +            } else if SINTS.contains(&to) {
 +                Some(Self::SignedToSigned)
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Check for `expr <= (to_type::MAX as from_type)`
 +fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
 +    if_chain! {
 +         if let ExprKind::Binary(ref op, left, right) = &expr.kind;
 +         if let Some((candidate, check)) = normalize_le_ge(op, left, right);
 +         if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX");
 +
 +         then {
 +             Conversion::try_new(candidate, from, to)
 +         } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Check for `expr >= 0|(to_type::MIN as from_type)`
 +fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
 +    fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> {
 +        (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check)))
 +    }
 +
 +    // First of we need a binary containing the expression & the cast
 +    if let ExprKind::Binary(ref op, left, right) = &expr.kind {
 +        normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Check for `expr >= 0`
 +fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
 +    is_integer_literal(check, 0).then(|| Conversion::new_any(candidate))
 +}
 +
 +/// Check for `expr >= (to_type::MIN as from_type)`
 +fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
 +    if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") {
 +        Conversion::try_new(candidate, from, to)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Tries to extract the from- and to-type from a cast expression
 +fn get_types_from_cast<'a>(
 +    expr: &'a Expr<'_>,
 +    types: &'a [&str],
 +    func: &'a str,
 +    assoc_const: &'a str,
 +) -> Option<(&'a str, &'a str)> {
 +    // `to_type::max_value() as from_type`
 +    // or `to_type::MAX as from_type`
 +    let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
 +        // to_type::max_value(), from_type
 +        if let ExprKind::Cast(limit, from_type) = &expr.kind;
 +        if let TyKind::Path(ref from_type_path) = &from_type.kind;
 +        if let Some(from_sym) = int_ty_to_sym(from_type_path);
 +
 +        then {
 +            Some((limit, from_sym))
 +        } else {
 +            None
 +        }
 +    };
 +
 +    // `from_type::from(to_type::max_value())`
 +    let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
 +        if_chain! {
 +            // `from_type::from, to_type::max_value()`
 +            if let ExprKind::Call(from_func, [limit]) = &expr.kind;
 +            // `from_type::from`
 +            if let ExprKind::Path(ref path) = &from_func.kind;
 +            if let Some(from_sym) = get_implementing_type(path, INTS, "from");
 +
 +            then {
 +                Some((limit, from_sym))
 +            } else {
 +                None
 +            }
 +        }
 +    });
 +
 +    if let Some((limit, from_type)) = limit_from {
 +        match limit.kind {
 +            // `from_type::from(_)`
 +            ExprKind::Call(path, _) => {
 +                if let ExprKind::Path(ref path) = path.kind {
 +                    // `to_type`
 +                    if let Some(to_type) = get_implementing_type(path, types, func) {
 +                        return Some((from_type, to_type));
 +                    }
 +                }
 +            },
 +            // `to_type::MAX`
 +            ExprKind::Path(ref path) => {
 +                if let Some(to_type) = get_implementing_type(path, types, assoc_const) {
 +                    return Some((from_type, to_type));
 +                }
 +            },
 +            _ => {},
 +        }
 +    };
 +    None
 +}
 +
 +/// Gets the type which implements the called function
 +fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
 +    if_chain! {
 +        if let QPath::TypeRelative(ty, path) = &path;
 +        if path.ident.name.as_str() == function;
 +        if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind;
 +        if let [int] = tp.segments;
 +        then {
 +            let name = int.ident.name.as_str();
 +            candidates.iter().find(|c| &name == *c).copied()
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Gets the type as a string, if it is a supported integer
 +fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
 +    if_chain! {
 +        if let QPath::Resolved(_, path) = *path;
 +        if let [ty] = path.segments;
 +        then {
 +            let name = ty.ident.name.as_str();
 +            INTS.iter().find(|c| &name == *c).copied()
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Will return the expressions as if they were expr1 <= expr2
 +fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
 +    match op.node {
 +        BinOpKind::Le => Some((left, right)),
 +        BinOpKind::Ge => Some((right, left)),
 +        _ => None,
 +    }
 +}
 +
 +// Constants
 +const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
 +const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
 +const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
index 90430b71a0ef99638596bfa9ea7ac840fe035867,0000000000000000000000000000000000000000..b38e09dc09f46e5d2053f8080b3e87e2f17844e4
mode 100644,000000..100644
--- /dev/null
@@@ -1,195 -1,0 +1,197 @@@
-         if expr.span.ctxt() == inner.span.ctxt();
 +//! Checks for if expressions that contain only an if expression.
 +//!
 +//! For example, the lint would catch:
 +//!
 +//! ```rust,ignore
 +//! if x {
 +//!     if y {
 +//!         println!("Hello world");
 +//!     }
 +//! }
 +//! ```
 +//!
 +//! This lint is **warn** by default
 +
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for nested `if` statements which can be collapsed
 +    /// by `&&`-combining their conditions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Each `if`-statement adds one level of nesting, which
 +    /// makes code look more complex than it really is.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let (x, y) = (true, true);
 +    /// if x {
 +    ///     if y {
 +    ///         // …
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let (x, y) = (true, true);
 +    /// if x && y {
 +    ///     // …
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub COLLAPSIBLE_IF,
 +    style,
 +    "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for collapsible `else { if ... }` expressions
 +    /// that can be collapsed to `else if ...`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Each `if`-statement adds one level of nesting, which
 +    /// makes code look more complex than it really is.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    ///
 +    /// if x {
 +    ///     …
 +    /// } else {
 +    ///     if y {
 +    ///         …
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Should be written:
 +    ///
 +    /// ```rust,ignore
 +    /// if x {
 +    ///     …
 +    /// } else if y {
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub COLLAPSIBLE_ELSE_IF,
 +    style,
 +    "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
 +}
 +
 +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
 +
 +impl EarlyLintPass for CollapsibleIf {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if !expr.span.from_expansion() {
 +            check_if(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +    if let ast::ExprKind::If(check, then, else_) = &expr.kind {
 +        if let Some(else_) = else_ {
 +            check_collapsible_maybe_if_let(cx, then.span, else_);
 +        } else if let ast::ExprKind::Let(..) = check.kind {
 +            // Prevent triggering on `if let a = b { if c { .. } }`.
 +        } else {
 +            check_collapsible_no_if_let(cx, expr, check, then);
 +        }
 +    }
 +}
 +
 +fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
 +    // We trim all opening braces and whitespaces and then check if the next string is a comment.
 +    let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
 +        .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
 +        .to_owned();
 +    trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
 +}
 +
 +fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
 +    if_chain! {
 +        if let ast::ExprKind::Block(ref block, _) = else_.kind;
 +        if !block_starts_with_comment(cx, block);
 +        if let Some(else_) = expr_block(block);
 +        if else_.attrs.is_empty();
 +        if !else_.span.from_expansion();
 +        if let ast::ExprKind::If(..) = else_.kind;
 +        then {
 +            // Prevent "elseif"
 +            // Check that the "else" is followed by whitespace
 +            let up_to_else = then_span.between(block.span);
 +            let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
 +
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                COLLAPSIBLE_ELSE_IF,
 +                block.span,
 +                "this `else { if .. }` block can be collapsed",
 +                "collapse nested if block",
 +                format!(
 +                    "{}{}",
 +                    if requires_space { " " } else { "" },
 +                    snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
 +    if_chain! {
 +        if !block_starts_with_comment(cx, then);
 +        if let Some(inner) = expr_block(then);
 +        if inner.attrs.is_empty();
 +        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(..));
-                 let lhs = Sugg::ast(cx, check, "..");
-                 let rhs = Sugg::ast(cx, check_inner, "..");
++        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| {
-                     Applicability::MachineApplicable, // snippet
++                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",
 +                    format!(
 +                        "if {} {}",
 +                        lhs.and(&rhs),
 +                        snippet_block(cx, content.span, "..", Some(expr.span)),
 +                    ),
++                    app, // snippet
 +                );
 +            });
 +        }
 +    }
 +}
 +
 +/// If the block contains only one expression, return it.
 +fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
 +    let mut it = block.stmts.iter();
 +
 +    if let (Some(stmt), None) = (it.next(), it.next()) {
 +        match stmt.kind {
 +            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
index 0d3fc43a6443b69f23e38a42eb3569411aad39b1,0000000000000000000000000000000000000000..e4d76f07d6b482b13fa7d5fbaf9cb8b6ce9923fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,628 -1,0 +1,630 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +pub(crate) static LINTS: &[&crate::LintInfo] = &[
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::clippy_lints_internal::CLIPPY_LINTS_INTERNAL_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::lint_without_lint_pass::LINT_WITHOUT_LINT_PASS_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
 +    #[cfg(feature = "internal")]
 +    crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
 +    crate::almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE_INFO,
 +    crate::approx_const::APPROX_CONSTANT_INFO,
 +    crate::as_conversions::AS_CONVERSIONS_INFO,
 +    crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO,
 +    crate::asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX_INFO,
 +    crate::assertions_on_constants::ASSERTIONS_ON_CONSTANTS_INFO,
 +    crate::assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES_INFO,
 +    crate::async_yields_async::ASYNC_YIELDS_ASYNC_INFO,
 +    crate::attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON_INFO,
 +    crate::attrs::BLANKET_CLIPPY_RESTRICTION_LINTS_INFO,
 +    crate::attrs::DEPRECATED_CFG_ATTR_INFO,
 +    crate::attrs::DEPRECATED_SEMVER_INFO,
 +    crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
 +    crate::attrs::INLINE_ALWAYS_INFO,
 +    crate::attrs::MISMATCHED_TARGET_OS_INFO,
 +    crate::attrs::USELESS_ATTRIBUTE_INFO,
 +    crate::await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE_INFO,
 +    crate::await_holding_invalid::AWAIT_HOLDING_LOCK_INFO,
 +    crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO,
 +    crate::blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS_INFO,
 +    crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO,
 +    crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO,
 +    crate::booleans::NONMINIMAL_BOOL_INFO,
 +    crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO,
 +    crate::borrow_deref_ref::BORROW_DEREF_REF_INFO,
 +    crate::box_default::BOX_DEFAULT_INFO,
 +    crate::cargo::CARGO_COMMON_METADATA_INFO,
 +    crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO,
 +    crate::cargo::NEGATIVE_FEATURE_NAMES_INFO,
 +    crate::cargo::REDUNDANT_FEATURE_NAMES_INFO,
 +    crate::cargo::WILDCARD_DEPENDENCIES_INFO,
 +    crate::casts::AS_PTR_CAST_MUT_INFO,
 +    crate::casts::AS_UNDERSCORE_INFO,
 +    crate::casts::BORROW_AS_PTR_INFO,
 +    crate::casts::CAST_ABS_TO_UNSIGNED_INFO,
 +    crate::casts::CAST_ENUM_CONSTRUCTOR_INFO,
 +    crate::casts::CAST_ENUM_TRUNCATION_INFO,
 +    crate::casts::CAST_LOSSLESS_INFO,
 +    crate::casts::CAST_NAN_TO_INT_INFO,
 +    crate::casts::CAST_POSSIBLE_TRUNCATION_INFO,
 +    crate::casts::CAST_POSSIBLE_WRAP_INFO,
 +    crate::casts::CAST_PRECISION_LOSS_INFO,
 +    crate::casts::CAST_PTR_ALIGNMENT_INFO,
 +    crate::casts::CAST_REF_TO_MUT_INFO,
 +    crate::casts::CAST_SIGN_LOSS_INFO,
 +    crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
 +    crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
 +    crate::casts::CHAR_LIT_AS_U8_INFO,
 +    crate::casts::FN_TO_NUMERIC_CAST_INFO,
 +    crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
 +    crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
 +    crate::casts::PTR_AS_PTR_INFO,
 +    crate::casts::UNNECESSARY_CAST_INFO,
 +    crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
 +    crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
 +    crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
 +    crate::collapsible_if::COLLAPSIBLE_IF_INFO,
 +    crate::comparison_chain::COMPARISON_CHAIN_INFO,
 +    crate::copies::BRANCHES_SHARING_CODE_INFO,
 +    crate::copies::IFS_SAME_COND_INFO,
 +    crate::copies::IF_SAME_THEN_ELSE_INFO,
 +    crate::copies::SAME_FUNCTIONS_IN_IF_CONDITION_INFO,
 +    crate::copy_iterator::COPY_ITERATOR_INFO,
 +    crate::crate_in_macro_def::CRATE_IN_MACRO_DEF_INFO,
 +    crate::create_dir::CREATE_DIR_INFO,
 +    crate::dbg_macro::DBG_MACRO_INFO,
 +    crate::default::DEFAULT_TRAIT_ACCESS_INFO,
 +    crate::default::FIELD_REASSIGN_WITH_DEFAULT_INFO,
 +    crate::default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY_INFO,
 +    crate::default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK_INFO,
 +    crate::default_union_representation::DEFAULT_UNION_REPRESENTATION_INFO,
 +    crate::dereference::EXPLICIT_AUTO_DEREF_INFO,
 +    crate::dereference::EXPLICIT_DEREF_METHODS_INFO,
 +    crate::dereference::NEEDLESS_BORROW_INFO,
 +    crate::dereference::REF_BINDING_TO_REFERENCE_INFO,
 +    crate::derivable_impls::DERIVABLE_IMPLS_INFO,
 +    crate::derive::DERIVE_HASH_XOR_EQ_INFO,
 +    crate::derive::DERIVE_ORD_XOR_PARTIAL_ORD_INFO,
 +    crate::derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ_INFO,
 +    crate::derive::EXPL_IMPL_CLONE_ON_COPY_INFO,
 +    crate::derive::UNSAFE_DERIVE_DESERIALIZE_INFO,
 +    crate::disallowed_macros::DISALLOWED_MACROS_INFO,
 +    crate::disallowed_methods::DISALLOWED_METHODS_INFO,
 +    crate::disallowed_names::DISALLOWED_NAMES_INFO,
 +    crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
 +    crate::disallowed_types::DISALLOWED_TYPES_INFO,
 +    crate::doc::DOC_LINK_WITH_QUOTES_INFO,
 +    crate::doc::DOC_MARKDOWN_INFO,
 +    crate::doc::MISSING_ERRORS_DOC_INFO,
 +    crate::doc::MISSING_PANICS_DOC_INFO,
 +    crate::doc::MISSING_SAFETY_DOC_INFO,
 +    crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
 +    crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
 +    crate::double_parens::DOUBLE_PARENS_INFO,
 +    crate::drop_forget_ref::DROP_COPY_INFO,
 +    crate::drop_forget_ref::DROP_NON_DROP_INFO,
 +    crate::drop_forget_ref::DROP_REF_INFO,
 +    crate::drop_forget_ref::FORGET_COPY_INFO,
 +    crate::drop_forget_ref::FORGET_NON_DROP_INFO,
 +    crate::drop_forget_ref::FORGET_REF_INFO,
 +    crate::drop_forget_ref::UNDROPPED_MANUALLY_DROPS_INFO,
 +    crate::duplicate_mod::DUPLICATE_MOD_INFO,
 +    crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
 +    crate::empty_drop::EMPTY_DROP_INFO,
 +    crate::empty_enum::EMPTY_ENUM_INFO,
 +    crate::empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS_INFO,
 +    crate::entry::MAP_ENTRY_INFO,
 +    crate::enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT_INFO,
 +    crate::enum_variants::ENUM_VARIANT_NAMES_INFO,
 +    crate::enum_variants::MODULE_INCEPTION_INFO,
 +    crate::enum_variants::MODULE_NAME_REPETITIONS_INFO,
 +    crate::equatable_if_let::EQUATABLE_IF_LET_INFO,
 +    crate::escape::BOXED_LOCAL_INFO,
 +    crate::eta_reduction::REDUNDANT_CLOSURE_INFO,
 +    crate::eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS_INFO,
 +    crate::excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS_INFO,
 +    crate::excessive_bools::STRUCT_EXCESSIVE_BOOLS_INFO,
 +    crate::exhaustive_items::EXHAUSTIVE_ENUMS_INFO,
 +    crate::exhaustive_items::EXHAUSTIVE_STRUCTS_INFO,
 +    crate::exit::EXIT_INFO,
 +    crate::explicit_write::EXPLICIT_WRITE_INFO,
 +    crate::fallible_impl_from::FALLIBLE_IMPL_FROM_INFO,
 +    crate::float_literal::EXCESSIVE_PRECISION_INFO,
 +    crate::float_literal::LOSSY_FLOAT_LITERAL_INFO,
 +    crate::floating_point_arithmetic::IMPRECISE_FLOPS_INFO,
 +    crate::floating_point_arithmetic::SUBOPTIMAL_FLOPS_INFO,
 +    crate::format::USELESS_FORMAT_INFO,
 +    crate::format_args::FORMAT_IN_FORMAT_ARGS_INFO,
 +    crate::format_args::TO_STRING_IN_FORMAT_ARGS_INFO,
 +    crate::format_args::UNINLINED_FORMAT_ARGS_INFO,
 +    crate::format_args::UNUSED_FORMAT_SPECS_INFO,
 +    crate::format_impl::PRINT_IN_FORMAT_IMPL_INFO,
 +    crate::format_impl::RECURSIVE_FORMAT_IMPL_INFO,
 +    crate::format_push_string::FORMAT_PUSH_STRING_INFO,
 +    crate::formatting::POSSIBLE_MISSING_COMMA_INFO,
 +    crate::formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING_INFO,
 +    crate::formatting::SUSPICIOUS_ELSE_FORMATTING_INFO,
 +    crate::formatting::SUSPICIOUS_UNARY_OP_FORMATTING_INFO,
 +    crate::from_over_into::FROM_OVER_INTO_INFO,
 +    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,
 +    crate::functions::RESULT_LARGE_ERR_INFO,
 +    crate::functions::RESULT_UNIT_ERR_INFO,
 +    crate::functions::TOO_MANY_ARGUMENTS_INFO,
 +    crate::functions::TOO_MANY_LINES_INFO,
 +    crate::future_not_send::FUTURE_NOT_SEND_INFO,
 +    crate::if_let_mutex::IF_LET_MUTEX_INFO,
 +    crate::if_not_else::IF_NOT_ELSE_INFO,
 +    crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO,
 +    crate::implicit_hasher::IMPLICIT_HASHER_INFO,
 +    crate::implicit_return::IMPLICIT_RETURN_INFO,
 +    crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
 +    crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
 +    crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
 +    crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
 +    crate::indexing_slicing::INDEXING_SLICING_INFO,
 +    crate::indexing_slicing::OUT_OF_BOUNDS_INDEXING_INFO,
 +    crate::infinite_iter::INFINITE_ITER_INFO,
 +    crate::infinite_iter::MAYBE_INFINITE_ITER_INFO,
 +    crate::inherent_impl::MULTIPLE_INHERENT_IMPL_INFO,
 +    crate::inherent_to_string::INHERENT_TO_STRING_INFO,
 +    crate::inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY_INFO,
 +    crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO,
 +    crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO,
 +    crate::instant_subtraction::MANUAL_INSTANT_ELAPSED_INFO,
 +    crate::instant_subtraction::UNCHECKED_DURATION_SUBTRACTION_INFO,
 +    crate::int_plus_one::INT_PLUS_ONE_INFO,
 +    crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
 +    crate::invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED_INFO,
 +    crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
 +    crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
 +    crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
 +    crate::large_enum_variant::LARGE_ENUM_VARIANT_INFO,
 +    crate::large_include_file::LARGE_INCLUDE_FILE_INFO,
 +    crate::large_stack_arrays::LARGE_STACK_ARRAYS_INFO,
 +    crate::len_zero::COMPARISON_TO_EMPTY_INFO,
 +    crate::len_zero::LEN_WITHOUT_IS_EMPTY_INFO,
 +    crate::len_zero::LEN_ZERO_INFO,
 +    crate::let_if_seq::USELESS_LET_IF_SEQ_INFO,
 +    crate::let_underscore::LET_UNDERSCORE_FUTURE_INFO,
 +    crate::let_underscore::LET_UNDERSCORE_LOCK_INFO,
 +    crate::let_underscore::LET_UNDERSCORE_MUST_USE_INFO,
 +    crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
 +    crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
 +    crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
 +    crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO,
 +    crate::literal_representation::LARGE_DIGIT_GROUPS_INFO,
 +    crate::literal_representation::MISTYPED_LITERAL_SUFFIXES_INFO,
 +    crate::literal_representation::UNREADABLE_LITERAL_INFO,
 +    crate::literal_representation::UNUSUAL_BYTE_GROUPINGS_INFO,
 +    crate::loops::EMPTY_LOOP_INFO,
 +    crate::loops::EXPLICIT_COUNTER_LOOP_INFO,
 +    crate::loops::EXPLICIT_INTO_ITER_LOOP_INFO,
 +    crate::loops::EXPLICIT_ITER_LOOP_INFO,
 +    crate::loops::FOR_KV_MAP_INFO,
 +    crate::loops::ITER_NEXT_LOOP_INFO,
 +    crate::loops::MANUAL_FIND_INFO,
 +    crate::loops::MANUAL_FLATTEN_INFO,
 +    crate::loops::MANUAL_MEMCPY_INFO,
 +    crate::loops::MISSING_SPIN_LOOP_INFO,
 +    crate::loops::MUT_RANGE_BOUND_INFO,
 +    crate::loops::NEEDLESS_RANGE_LOOP_INFO,
 +    crate::loops::NEVER_LOOP_INFO,
 +    crate::loops::SAME_ITEM_PUSH_INFO,
 +    crate::loops::SINGLE_ELEMENT_LOOP_INFO,
 +    crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
 +    crate::loops::WHILE_LET_LOOP_INFO,
 +    crate::loops::WHILE_LET_ON_ITERATOR_INFO,
 +    crate::macro_use::MACRO_USE_IMPORTS_INFO,
 +    crate::main_recursion::MAIN_RECURSION_INFO,
 +    crate::manual_assert::MANUAL_ASSERT_INFO,
 +    crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
 +    crate::manual_bits::MANUAL_BITS_INFO,
 +    crate::manual_clamp::MANUAL_CLAMP_INFO,
 +    crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
 +    crate::manual_let_else::MANUAL_LET_ELSE_INFO,
 +    crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
 +    crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
 +    crate::manual_retain::MANUAL_RETAIN_INFO,
 +    crate::manual_string_new::MANUAL_STRING_NEW_INFO,
 +    crate::manual_strip::MANUAL_STRIP_INFO,
 +    crate::map_unit_fn::OPTION_MAP_UNIT_FN_INFO,
 +    crate::map_unit_fn::RESULT_MAP_UNIT_FN_INFO,
 +    crate::match_result_ok::MATCH_RESULT_OK_INFO,
 +    crate::matches::COLLAPSIBLE_MATCH_INFO,
 +    crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO,
 +    crate::matches::MANUAL_FILTER_INFO,
 +    crate::matches::MANUAL_MAP_INFO,
 +    crate::matches::MANUAL_UNWRAP_OR_INFO,
 +    crate::matches::MATCH_AS_REF_INFO,
 +    crate::matches::MATCH_BOOL_INFO,
 +    crate::matches::MATCH_LIKE_MATCHES_MACRO_INFO,
 +    crate::matches::MATCH_ON_VEC_ITEMS_INFO,
 +    crate::matches::MATCH_OVERLAPPING_ARM_INFO,
 +    crate::matches::MATCH_REF_PATS_INFO,
 +    crate::matches::MATCH_SAME_ARMS_INFO,
 +    crate::matches::MATCH_SINGLE_BINDING_INFO,
 +    crate::matches::MATCH_STR_CASE_MISMATCH_INFO,
 +    crate::matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS_INFO,
 +    crate::matches::MATCH_WILD_ERR_ARM_INFO,
 +    crate::matches::NEEDLESS_MATCH_INFO,
 +    crate::matches::REDUNDANT_PATTERN_MATCHING_INFO,
 +    crate::matches::REST_PAT_IN_FULLY_BOUND_STRUCTS_INFO,
 +    crate::matches::SIGNIFICANT_DROP_IN_SCRUTINEE_INFO,
 +    crate::matches::SINGLE_MATCH_INFO,
 +    crate::matches::SINGLE_MATCH_ELSE_INFO,
 +    crate::matches::TRY_ERR_INFO,
 +    crate::matches::WILDCARD_ENUM_MATCH_ARM_INFO,
 +    crate::matches::WILDCARD_IN_OR_PATTERNS_INFO,
 +    crate::mem_forget::MEM_FORGET_INFO,
 +    crate::mem_replace::MEM_REPLACE_OPTION_WITH_NONE_INFO,
 +    crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO,
 +    crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO,
 +    crate::methods::BIND_INSTEAD_OF_MAP_INFO,
 +    crate::methods::BYTES_COUNT_TO_LEN_INFO,
 +    crate::methods::BYTES_NTH_INFO,
 +    crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO,
 +    crate::methods::CHARS_LAST_CMP_INFO,
 +    crate::methods::CHARS_NEXT_CMP_INFO,
 +    crate::methods::CLONED_INSTEAD_OF_COPIED_INFO,
 +    crate::methods::CLONE_DOUBLE_REF_INFO,
 +    crate::methods::CLONE_ON_COPY_INFO,
 +    crate::methods::CLONE_ON_REF_PTR_INFO,
 +    crate::methods::COLLAPSIBLE_STR_REPLACE_INFO,
 +    crate::methods::ERR_EXPECT_INFO,
 +    crate::methods::EXPECT_FUN_CALL_INFO,
 +    crate::methods::EXPECT_USED_INFO,
 +    crate::methods::EXTEND_WITH_DRAIN_INFO,
 +    crate::methods::FILETYPE_IS_FILE_INFO,
 +    crate::methods::FILTER_MAP_IDENTITY_INFO,
 +    crate::methods::FILTER_MAP_NEXT_INFO,
 +    crate::methods::FILTER_NEXT_INFO,
 +    crate::methods::FLAT_MAP_IDENTITY_INFO,
 +    crate::methods::FLAT_MAP_OPTION_INFO,
 +    crate::methods::FROM_ITER_INSTEAD_OF_COLLECT_INFO,
 +    crate::methods::GET_FIRST_INFO,
 +    crate::methods::GET_LAST_WITH_LEN_INFO,
 +    crate::methods::GET_UNWRAP_INFO,
 +    crate::methods::IMPLICIT_CLONE_INFO,
 +    crate::methods::INEFFICIENT_TO_STRING_INFO,
 +    crate::methods::INSPECT_FOR_EACH_INFO,
 +    crate::methods::INTO_ITER_ON_REF_INFO,
 +    crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
 +    crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
 +    crate::methods::ITER_CLONED_COLLECT_INFO,
 +    crate::methods::ITER_COUNT_INFO,
 +    crate::methods::ITER_KV_MAP_INFO,
 +    crate::methods::ITER_NEXT_SLICE_INFO,
 +    crate::methods::ITER_NTH_INFO,
 +    crate::methods::ITER_NTH_ZERO_INFO,
 +    crate::methods::ITER_ON_EMPTY_COLLECTIONS_INFO,
 +    crate::methods::ITER_ON_SINGLE_ITEMS_INFO,
 +    crate::methods::ITER_OVEREAGER_CLONED_INFO,
 +    crate::methods::ITER_SKIP_NEXT_INFO,
 +    crate::methods::ITER_WITH_DRAIN_INFO,
 +    crate::methods::MANUAL_FILTER_MAP_INFO,
 +    crate::methods::MANUAL_FIND_MAP_INFO,
 +    crate::methods::MANUAL_OK_OR_INFO,
 +    crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
 +    crate::methods::MANUAL_SPLIT_ONCE_INFO,
 +    crate::methods::MANUAL_STR_REPEAT_INFO,
 +    crate::methods::MAP_CLONE_INFO,
 +    crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
 +    crate::methods::MAP_ERR_IGNORE_INFO,
 +    crate::methods::MAP_FLATTEN_INFO,
 +    crate::methods::MAP_IDENTITY_INFO,
 +    crate::methods::MAP_UNWRAP_OR_INFO,
 +    crate::methods::MUT_MUTEX_LOCK_INFO,
 +    crate::methods::NAIVE_BYTECOUNT_INFO,
 +    crate::methods::NEEDLESS_COLLECT_INFO,
 +    crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
 +    crate::methods::NEEDLESS_OPTION_TAKE_INFO,
 +    crate::methods::NEEDLESS_SPLITN_INFO,
 +    crate::methods::NEW_RET_NO_SELF_INFO,
 +    crate::methods::NONSENSICAL_OPEN_OPTIONS_INFO,
 +    crate::methods::NO_EFFECT_REPLACE_INFO,
 +    crate::methods::OBFUSCATED_IF_ELSE_INFO,
 +    crate::methods::OK_EXPECT_INFO,
 +    crate::methods::OPTION_AS_REF_DEREF_INFO,
 +    crate::methods::OPTION_FILTER_MAP_INFO,
 +    crate::methods::OPTION_MAP_OR_NONE_INFO,
 +    crate::methods::OR_FUN_CALL_INFO,
 +    crate::methods::OR_THEN_UNWRAP_INFO,
 +    crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
 +    crate::methods::RANGE_ZIP_WITH_LEN_INFO,
 +    crate::methods::REPEAT_ONCE_INFO,
 +    crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
 +    crate::methods::SEARCH_IS_SOME_INFO,
 +    crate::methods::SEEK_FROM_CURRENT_INFO,
 +    crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
 +    crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
 +    crate::methods::SINGLE_CHAR_ADD_STR_INFO,
 +    crate::methods::SINGLE_CHAR_PATTERN_INFO,
 +    crate::methods::SKIP_WHILE_NEXT_INFO,
 +    crate::methods::STABLE_SORT_PRIMITIVE_INFO,
 +    crate::methods::STRING_EXTEND_CHARS_INFO,
 +    crate::methods::SUSPICIOUS_MAP_INFO,
 +    crate::methods::SUSPICIOUS_SPLITN_INFO,
 +    crate::methods::SUSPICIOUS_TO_OWNED_INFO,
 +    crate::methods::UNINIT_ASSUMED_INIT_INFO,
 +    crate::methods::UNIT_HASH_INFO,
 +    crate::methods::UNNECESSARY_FILTER_MAP_INFO,
 +    crate::methods::UNNECESSARY_FIND_MAP_INFO,
 +    crate::methods::UNNECESSARY_FOLD_INFO,
 +    crate::methods::UNNECESSARY_JOIN_INFO,
 +    crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
 +    crate::methods::UNNECESSARY_SORT_BY_INFO,
 +    crate::methods::UNNECESSARY_TO_OWNED_INFO,
 +    crate::methods::UNWRAP_OR_ELSE_DEFAULT_INFO,
 +    crate::methods::UNWRAP_USED_INFO,
 +    crate::methods::USELESS_ASREF_INFO,
 +    crate::methods::VEC_RESIZE_TO_ZERO_INFO,
 +    crate::methods::VERBOSE_FILE_READS_INFO,
 +    crate::methods::WRONG_SELF_CONVENTION_INFO,
 +    crate::methods::ZST_OFFSET_INFO,
 +    crate::minmax::MIN_MAX_INFO,
 +    crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
 +    crate::misc::TOPLEVEL_REF_ARG_INFO,
 +    crate::misc::USED_UNDERSCORE_BINDING_INFO,
 +    crate::misc::ZERO_PTR_INFO,
 +    crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
 +    crate::misc_early::DOUBLE_NEG_INFO,
 +    crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
 +    crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO,
 +    crate::misc_early::REDUNDANT_PATTERN_INFO,
 +    crate::misc_early::SEPARATED_LITERAL_SUFFIX_INFO,
 +    crate::misc_early::UNNEEDED_FIELD_PATTERN_INFO,
 +    crate::misc_early::UNNEEDED_WILDCARD_PATTERN_INFO,
 +    crate::misc_early::UNSEPARATED_LITERAL_SUFFIX_INFO,
 +    crate::misc_early::ZERO_PREFIXED_LITERAL_INFO,
 +    crate::mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER_INFO,
 +    crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
 +    crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
 +    crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
 +    crate::missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS_INFO,
 +    crate::missing_trait_methods::MISSING_TRAIT_METHODS_INFO,
 +    crate::mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION_INFO,
 +    crate::mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION_INFO,
 +    crate::module_style::MOD_MODULE_FILES_INFO,
 +    crate::module_style::SELF_NAMED_MODULE_FILES_INFO,
 +    crate::multi_assignments::MULTI_ASSIGNMENTS_INFO,
 +    crate::mut_key::MUTABLE_KEY_TYPE_INFO,
 +    crate::mut_mut::MUT_MUT_INFO,
 +    crate::mut_reference::UNNECESSARY_MUT_PASSED_INFO,
 +    crate::mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL_INFO,
 +    crate::mutex_atomic::MUTEX_ATOMIC_INFO,
 +    crate::mutex_atomic::MUTEX_INTEGER_INFO,
 +    crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
 +    crate::needless_bool::BOOL_COMPARISON_INFO,
 +    crate::needless_bool::NEEDLESS_BOOL_INFO,
 +    crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
 +    crate::needless_continue::NEEDLESS_CONTINUE_INFO,
 +    crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
 +    crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
 +    crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
 +    crate::needless_pass_by_value::NEEDLESS_PASS_BY_VALUE_INFO,
 +    crate::needless_question_mark::NEEDLESS_QUESTION_MARK_INFO,
 +    crate::needless_update::NEEDLESS_UPDATE_INFO,
 +    crate::neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD_INFO,
 +    crate::neg_multiply::NEG_MULTIPLY_INFO,
 +    crate::new_without_default::NEW_WITHOUT_DEFAULT_INFO,
 +    crate::no_effect::NO_EFFECT_INFO,
 +    crate::no_effect::NO_EFFECT_UNDERSCORE_BINDING_INFO,
 +    crate::no_effect::UNNECESSARY_OPERATION_INFO,
 +    crate::non_copy_const::BORROW_INTERIOR_MUTABLE_CONST_INFO,
 +    crate::non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST_INFO,
 +    crate::non_expressive_names::JUST_UNDERSCORES_AND_DIGITS_INFO,
 +    crate::non_expressive_names::MANY_SINGLE_CHAR_NAMES_INFO,
 +    crate::non_expressive_names::SIMILAR_NAMES_INFO,
 +    crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO,
 +    crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO,
 +    crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO,
 +    crate::octal_escapes::OCTAL_ESCAPES_INFO,
 +    crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO,
 +    crate::operators::ABSURD_EXTREME_COMPARISONS_INFO,
 +    crate::operators::ARITHMETIC_SIDE_EFFECTS_INFO,
 +    crate::operators::ASSIGN_OP_PATTERN_INFO,
 +    crate::operators::BAD_BIT_MASK_INFO,
 +    crate::operators::CMP_NAN_INFO,
 +    crate::operators::CMP_OWNED_INFO,
 +    crate::operators::DOUBLE_COMPARISONS_INFO,
 +    crate::operators::DURATION_SUBSEC_INFO,
 +    crate::operators::EQ_OP_INFO,
 +    crate::operators::ERASING_OP_INFO,
 +    crate::operators::FLOAT_ARITHMETIC_INFO,
 +    crate::operators::FLOAT_CMP_INFO,
 +    crate::operators::FLOAT_CMP_CONST_INFO,
 +    crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO,
 +    crate::operators::IDENTITY_OP_INFO,
 +    crate::operators::INEFFECTIVE_BIT_MASK_INFO,
 +    crate::operators::INTEGER_ARITHMETIC_INFO,
 +    crate::operators::INTEGER_DIVISION_INFO,
 +    crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
 +    crate::operators::MODULO_ARITHMETIC_INFO,
 +    crate::operators::MODULO_ONE_INFO,
 +    crate::operators::NEEDLESS_BITWISE_BOOL_INFO,
 +    crate::operators::OP_REF_INFO,
 +    crate::operators::PTR_EQ_INFO,
 +    crate::operators::SELF_ASSIGNMENT_INFO,
 +    crate::operators::VERBOSE_BIT_MASK_INFO,
 +    crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO,
 +    crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO,
 +    crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO,
 +    crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO,
 +    crate::panic_unimplemented::PANIC_INFO,
 +    crate::panic_unimplemented::TODO_INFO,
 +    crate::panic_unimplemented::UNIMPLEMENTED_INFO,
 +    crate::panic_unimplemented::UNREACHABLE_INFO,
 +    crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO,
 +    crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO,
 +    crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO,
 +    crate::pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE_INFO,
 +    crate::pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF_INFO,
 +    crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO,
 +    crate::precedence::PRECEDENCE_INFO,
 +    crate::ptr::CMP_NULL_INFO,
 +    crate::ptr::INVALID_NULL_PTR_USAGE_INFO,
 +    crate::ptr::MUT_FROM_REF_INFO,
 +    crate::ptr::PTR_ARG_INFO,
 +    crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
 +    crate::pub_use::PUB_USE_INFO,
 +    crate::question_mark::QUESTION_MARK_INFO,
 +    crate::ranges::MANUAL_RANGE_CONTAINS_INFO,
 +    crate::ranges::RANGE_MINUS_ONE_INFO,
 +    crate::ranges::RANGE_PLUS_ONE_INFO,
 +    crate::ranges::REVERSED_EMPTY_RANGES_INFO,
 +    crate::rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT_INFO,
 +    crate::read_zero_byte_vec::READ_ZERO_BYTE_VEC_INFO,
 +    crate::redundant_clone::REDUNDANT_CLONE_INFO,
 +    crate::redundant_closure_call::REDUNDANT_CLOSURE_CALL_INFO,
 +    crate::redundant_else::REDUNDANT_ELSE_INFO,
 +    crate::redundant_field_names::REDUNDANT_FIELD_NAMES_INFO,
 +    crate::redundant_pub_crate::REDUNDANT_PUB_CRATE_INFO,
 +    crate::redundant_slicing::DEREF_BY_SLICING_INFO,
 +    crate::redundant_slicing::REDUNDANT_SLICING_INFO,
 +    crate::redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES_INFO,
 +    crate::ref_option_ref::REF_OPTION_REF_INFO,
 +    crate::reference::DEREF_ADDROF_INFO,
 +    crate::regex::INVALID_REGEX_INFO,
 +    crate::regex::TRIVIAL_REGEX_INFO,
 +    crate::return_self_not_must_use::RETURN_SELF_NOT_MUST_USE_INFO,
 +    crate::returns::LET_AND_RETURN_INFO,
 +    crate::returns::NEEDLESS_RETURN_INFO,
 +    crate::same_name_method::SAME_NAME_METHOD_INFO,
 +    crate::self_named_constructors::SELF_NAMED_CONSTRUCTORS_INFO,
 +    crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO,
 +    crate::serde_api::SERDE_API_MISUSE_INFO,
 +    crate::shadow::SHADOW_REUSE_INFO,
 +    crate::shadow::SHADOW_SAME_INFO,
 +    crate::shadow::SHADOW_UNRELATED_INFO,
 +    crate::single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES_INFO,
 +    crate::single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS_INFO,
 +    crate::size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT_INFO,
 +    crate::slow_vector_initialization::SLOW_VECTOR_INITIALIZATION_INFO,
 +    crate::std_instead_of_core::ALLOC_INSTEAD_OF_CORE_INFO,
 +    crate::std_instead_of_core::STD_INSTEAD_OF_ALLOC_INFO,
 +    crate::std_instead_of_core::STD_INSTEAD_OF_CORE_INFO,
 +    crate::strings::STRING_ADD_INFO,
 +    crate::strings::STRING_ADD_ASSIGN_INFO,
 +    crate::strings::STRING_FROM_UTF8_AS_BYTES_INFO,
 +    crate::strings::STRING_LIT_AS_BYTES_INFO,
 +    crate::strings::STRING_SLICE_INFO,
 +    crate::strings::STRING_TO_STRING_INFO,
 +    crate::strings::STR_TO_STRING_INFO,
 +    crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
 +    crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
 +    crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
 +    crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
 +    crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,
 +    crate::suspicious_xor_used_as_pow::SUSPICIOUS_XOR_USED_AS_POW_INFO,
 +    crate::swap::ALMOST_SWAPPED_INFO,
 +    crate::swap::MANUAL_SWAP_INFO,
 +    crate::swap_ptr_to_ref::SWAP_PTR_TO_REF_INFO,
 +    crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
 +    crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
 +    crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
 +    crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
 +    crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
 +    crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
 +    crate::transmute::CROSSPOINTER_TRANSMUTE_INFO,
 +    crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO,
 +    crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO,
 +    crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO,
 +    crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
 +    crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
 +    crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
 +    crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
 +    crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
 +    crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,
 +    crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO,
 +    crate::transmute::TRANSMUTING_NULL_INFO,
 +    crate::transmute::UNSOUND_COLLECTION_TRANSMUTE_INFO,
 +    crate::transmute::USELESS_TRANSMUTE_INFO,
 +    crate::transmute::WRONG_TRANSMUTE_INFO,
 +    crate::types::BORROWED_BOX_INFO,
 +    crate::types::BOX_COLLECTION_INFO,
 +    crate::types::LINKEDLIST_INFO,
 +    crate::types::OPTION_OPTION_INFO,
 +    crate::types::RC_BUFFER_INFO,
 +    crate::types::RC_MUTEX_INFO,
 +    crate::types::REDUNDANT_ALLOCATION_INFO,
 +    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,
 +    crate::uninit_vec::UNINIT_VEC_INFO,
 +    crate::unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD_INFO,
 +    crate::unit_types::LET_UNIT_VALUE_INFO,
 +    crate::unit_types::UNIT_ARG_INFO,
 +    crate::unit_types::UNIT_CMP_INFO,
 +    crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
 +    crate::unnamed_address::VTABLE_ADDRESS_COMPARISONS_INFO,
 +    crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO,
 +    crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO,
 +    crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO,
 +    crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO,
 +    crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO,
 +    crate::unused_async::UNUSED_ASYNC_INFO,
 +    crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO,
 +    crate::unused_peekable::UNUSED_PEEKABLE_INFO,
 +    crate::unused_rounding::UNUSED_ROUNDING_INFO,
 +    crate::unused_self::UNUSED_SELF_INFO,
 +    crate::unused_unit::UNUSED_UNIT_INFO,
 +    crate::unwrap::PANICKING_UNWRAP_INFO,
 +    crate::unwrap::UNNECESSARY_UNWRAP_INFO,
 +    crate::unwrap_in_result::UNWRAP_IN_RESULT_INFO,
 +    crate::upper_case_acronyms::UPPER_CASE_ACRONYMS_INFO,
 +    crate::use_self::USE_SELF_INFO,
 +    crate::useless_conversion::USELESS_CONVERSION_INFO,
 +    crate::vec::USELESS_VEC_INFO,
 +    crate::vec_init_then_push::VEC_INIT_THEN_PUSH_INFO,
 +    crate::wildcard_imports::ENUM_GLOB_USE_INFO,
 +    crate::wildcard_imports::WILDCARD_IMPORTS_INFO,
 +    crate::write::PRINTLN_EMPTY_STRING_INFO,
 +    crate::write::PRINT_LITERAL_INFO,
 +    crate::write::PRINT_STDERR_INFO,
 +    crate::write::PRINT_STDOUT_INFO,
 +    crate::write::PRINT_WITH_NEWLINE_INFO,
 +    crate::write::USE_DEBUG_INFO,
 +    crate::write::WRITELN_EMPTY_STRING_INFO,
 +    crate::write::WRITE_LITERAL_INFO,
 +    crate::write::WRITE_WITH_NEWLINE_INFO,
 +    crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO,
 +    crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO,
 +];
index 47ea98956be24cc98b6c5e41d59a6b6a4badc646,0000000000000000000000000000000000000000..38329659e02b76108ed46d071dd49f38c2a04650
mode 100644,000000..100644
--- /dev/null
@@@ -1,1629 -1,0 +1,1638 @@@
-     fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
-     walk_to_expr_usage,
 +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::{
- use rustc_semver::RustcVersion;
++    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};
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_ty, Visitor};
 +use rustc_hir::{
 +    self as hir,
 +    def_id::{DefId, LocalDefId},
 +    BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, ImplItem,
 +    ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
 +    TraitItemKind, TyKind, UnOp,
 +};
 +use rustc_index::bit_set::BitSet;
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::{Rvalue, StatementKind};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 +use rustc_middle::ty::{
 +    self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
 +    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
 +};
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span, Symbol};
 +use rustc_trait_selection::infer::InferCtxtExt as _;
 +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 +use std::collections::VecDeque;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `deref()` or `deref_mut()` method calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
 +    /// when not part of a method chain.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ops::Deref;
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b: &str = a.deref();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b = &*a;
 +    /// ```
 +    ///
 +    /// This lint excludes:
 +    /// ```rust,ignore
 +    /// let _ = d.unwrap().deref();
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub EXPLICIT_DEREF_METHODS,
 +    pedantic,
 +    "Explicit use of deref or deref_mut method while not in a method chain."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for address of operations (`&`) that are going to
 +    /// be dereferenced immediately by the compiler.
 +    ///
 +    /// ### Why is this bad?
 +    /// Suggests that the receiver of the expression borrows
 +    /// the expression.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun(_a: &i32) {}
 +    ///
 +    /// let x: &i32 = &&&&&&5;
 +    /// fun(&x);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun(_a: &i32) {}
 +    /// let x: &i32 = &5;
 +    /// fun(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_BORROW,
 +    style,
 +    "taking a reference that is going to be automatically dereferenced"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `ref` bindings which create a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// The address-of operator at the use site is clearer about the need for a reference.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(ref x) = x {
 +    ///     // use `x` here
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(x) = x {
 +    ///     // use `&x` here
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub REF_BINDING_TO_REFERENCE,
 +    pedantic,
 +    "`ref` binding to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for dereferencing expressions which would be covered by auto-deref.
 +    ///
 +    /// ### Why is this bad?
 +    /// This unnecessarily complicates the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &*x;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &x;
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub EXPLICIT_AUTO_DEREF,
 +    complexity,
 +    "dereferencing when the compiler would automatically dereference"
 +}
 +
 +impl_lint_pass!(Dereferencing<'_> => [
 +    EXPLICIT_DEREF_METHODS,
 +    NEEDLESS_BORROW,
 +    REF_BINDING_TO_REFERENCE,
 +    EXPLICIT_AUTO_DEREF,
 +]);
 +
 +#[derive(Default)]
 +pub struct Dereferencing<'tcx> {
 +    state: Option<(State, StateData)>,
 +
 +    // While parsing a `deref` method call in ufcs form, the path to the function is itself an
 +    // expression. This is to store the id of that expression so it can be skipped when
 +    // `check_expr` is called for it.
 +    skip_expr: Option<HirId>,
 +
 +    /// The body the first local was found in. Used to emit lints when the traversal of the body has
 +    /// been finished. Note we can't lint at the end of every body as they can be nested within each
 +    /// other.
 +    current_body: Option<BodyId>,
 +
 +    /// The list of locals currently being checked by the lint.
 +    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
 +    /// This is needed for or patterns where one of the branches can be linted, but another can not
 +    /// be.
 +    ///
 +    /// e.g. `m!(x) | Foo::Bar(ref x)`
 +    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
 +
 +    /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
 +    /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
 +    /// be moved.
 +    possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 +
 +    // `IntoIterator` for arrays requires Rust 1.53.
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl<'tcx> Dereferencing<'tcx> {
 +    #[must_use]
-                 let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
++    pub fn new(msrv: Msrv) -> Self {
 +        Self {
 +            msrv,
 +            ..Dereferencing::default()
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct StateData {
 +    /// Span of the top level expression
 +    span: Span,
 +    hir_id: HirId,
 +    position: Position,
 +}
 +
 +#[derive(Debug)]
 +struct DerefedBorrow {
 +    count: usize,
 +    msg: &'static str,
 +    snip_expr: Option<HirId>,
 +}
 +
 +#[derive(Debug)]
 +enum State {
 +    // Any number of deref method calls.
 +    DerefMethod {
 +        // The number of calls in a sequence which changed the referenced type
 +        ty_changed_count: usize,
 +        is_final_ufcs: bool,
 +        /// The required mutability
 +        target_mut: Mutability,
 +    },
 +    DerefedBorrow(DerefedBorrow),
 +    ExplicitDeref {
 +        mutability: Option<Mutability>,
 +    },
 +    ExplicitDerefField {
 +        name: Symbol,
 +    },
 +    Reborrow {
 +        mutability: Mutability,
 +    },
 +    Borrow {
 +        mutability: Mutability,
 +    },
 +}
 +
 +// A reference operation considered by this lint pass
 +enum RefOp {
 +    Method(Mutability),
 +    Deref,
 +    AddrOf(Mutability),
 +}
 +
 +struct RefPat {
 +    /// Whether every usage of the binding is dereferenced.
 +    always_deref: bool,
 +    /// The spans of all the ref bindings for this local.
 +    spans: Vec<Span>,
 +    /// The applicability of this suggestion.
 +    app: Applicability,
 +    /// All the replacements which need to be made.
 +    replacements: Vec<(Span, String)>,
 +    /// The [`HirId`] that the lint should be emitted at.
 +    hir_id: HirId,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
 +        if Some(expr.hir_id) == self.skip_expr.take() {
 +            return;
 +        }
 +
 +        if let Some(local) = path_to_local(expr) {
 +            self.check_local_usage(cx, expr, local);
 +        }
 +
 +        // Stop processing sub expressions when a macro call is seen
 +        if expr.span.from_expansion() {
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        }
 +
 +        let typeck = cx.typeck_results();
 +        let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else {
 +            // The whole chain of reference operations has been seen
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        };
 +
 +        match (self.state.take(), kind) {
 +            (None, kind) => {
 +                let expr_ty = typeck.expr_ty(expr);
-                             && !ty_contains_field(typeck.expr_ty(sub_expr), name)
++                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
-                         } else if position.is_deref_stable() {
++                            && !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() && sub_ty.is_ref() {
 +                            self.state = Some((
 +                                State::ExplicitDeref { mutability: None },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        }
-                                 position
++                    },
 +                    RefOp::Method(target_mut)
 +                        if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
 +                            && position.lint_explicit_deref() =>
 +                    {
 +                        let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
 +                        self.state = Some((
 +                            State::DerefMethod {
 +                                ty_changed_count,
 +                                is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                                target_mut,
 +                            },
 +                            StateData {
 +                                span: expr.span,
 +                                hir_id: expr.hir_id,
-                                 StateData { span: expr.span, hir_id: expr.hir_id, position },
++                                position,
 +                            },
 +                        ));
 +                    },
 +                    RefOp::AddrOf(mutability) => {
 +                        // Find the number of times the borrow is auto-derefed.
 +                        let mut iter = adjustments.iter();
 +                        let mut deref_count = 0usize;
 +                        let next_adjust = loop {
 +                            match iter.next() {
 +                                Some(adjust) => {
 +                                    if !matches!(adjust.kind, Adjust::Deref(_)) {
 +                                        break Some(adjust);
 +                                    } else if !adjust.target.is_ref() {
 +                                        deref_count += 1;
 +                                        break iter.next();
 +                                    }
 +                                    deref_count += 1;
 +                                },
 +                                None => break None,
 +                            };
 +                        };
 +
 +                        // Determine the required number of references before any can be removed. In all cases the
 +                        // reference made by the current expression will be removed. After that there are four cases to
 +                        // handle.
 +                        //
 +                        // 1. Auto-borrow will trigger in the current position, so no further references are required.
 +                        // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
 +                        //    handle the automatically inserted re-borrow.
 +                        // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
 +                        //    start auto-deref.
 +                        // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
 +                        //    adjustments will not be inserted automatically, then leave one further reference to avoid
 +                        //    moving a mutable borrow.
 +                        //    e.g.
 +                        //        fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
 +                        //            let x = match x {
 +                        //                // Removing the borrow will cause `x` to be moved
 +                        //                Some(x) => &mut *x,
 +                        //                None => y
 +                        //            };
 +                        //        }
 +                        let deref_msg =
 +                            "this expression creates a reference which is immediately dereferenced by the compiler";
 +                        let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
 +                        let impl_msg = "the borrowed expression implements the required traits";
 +
 +                        let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
 +                            (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
 +                        } else if let Position::ImplArg(hir_id) = position {
 +                            (0, impl_msg, Some(hir_id))
 +                        } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
 +                            next_adjust.map(|a| &a.kind)
 +                        {
 +                            if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
 +                            {
 +                                (3, deref_msg, None)
 +                            } else {
 +                                (2, deref_msg, None)
 +                            }
 +                        } else {
 +                            (2, deref_msg, None)
 +                        };
 +
 +                        if deref_count >= required_refs {
 +                            self.state = Some((
 +                                State::DerefedBorrow(DerefedBorrow {
 +                                    // One of the required refs is for the current borrow expression, the remaining ones
 +                                    // can't be removed without breaking the code. See earlier comment.
 +                                    count: deref_count - required_refs,
 +                                    msg,
 +                                    snip_expr,
 +                                }),
-                                     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
 +                            && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                            && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                        {
 +                            self.state = Some((
 +                                State::Borrow { mutability },
 +                                StateData {
 +                                    span: expr.span,
 +                                    hir_id: expr.hir_id,
-     msrv: Option<RustcVersion>,
++                                    position,
 +                                },
 +                            ));
 +                        }
 +                    },
 +                    RefOp::Method(..) => (),
 +                }
 +            },
 +            (
 +                Some((
 +                    State::DerefMethod {
 +                        target_mut,
 +                        ty_changed_count,
 +                        ..
 +                    },
 +                    data,
 +                )),
 +                RefOp::Method(_),
 +            ) => {
 +                self.state = Some((
 +                    State::DerefMethod {
 +                        ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
 +                            ty_changed_count
 +                        } else {
 +                            ty_changed_count + 1
 +                        },
 +                        is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                        target_mut,
 +                    },
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
 +                self.state = Some((
 +                    State::DerefedBorrow(DerefedBorrow {
 +                        count: state.count - 1,
 +                        ..state
 +                    }),
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::Borrow { mutability },
 +                        StateData {
 +                            span: expr.span,
 +                            hir_id: expr.hir_id,
 +                            position,
 +                        },
 +                    ));
 +                }
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if let Position::FieldAccess{name, ..} = position
 +                    && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                {
 +                    self.state = Some((
 +                        State::ExplicitDerefField { name },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                } else if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::ExplicitDeref { mutability: None },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                }
 +            },
 +
 +            (Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
 +                if typeck.expr_ty(sub_expr).is_ref() {
 +                    self.state = Some((State::Reborrow { mutability }, data));
 +                } else {
 +                    self.state = Some((
 +                        State::ExplicitDeref {
 +                            mutability: Some(mutability),
 +                        },
 +                        data,
 +                    ));
 +                }
 +            },
 +            (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
 +                self.state = Some((
 +                    State::ExplicitDeref {
 +                        mutability: Some(mutability),
 +                    },
 +                    data,
 +                ));
 +            },
 +            (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
 +                self.state = state;
 +            },
 +            (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
 +                if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
 +            {
 +                self.state = Some((State::ExplicitDerefField { name }, data));
 +            },
 +
 +            (Some((state, data)), _) => report(cx, expr, state, data),
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        if let PatKind::Binding(BindingAnnotation::REF, id, name, _) = pat.kind {
 +            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
 +                // This binding id has been seen before. Add this pattern to the list of changes.
 +                if let Some(prev_pat) = opt_prev_pat {
 +                    if pat.span.from_expansion() {
 +                        // Doesn't match the context of the previous pattern. Can't lint here.
 +                        *opt_prev_pat = None;
 +                    } else {
 +                        prev_pat.spans.push(pat.span);
 +                        prev_pat.replacements.push((
 +                            pat.span,
 +                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
 +                                .0
 +                                .into(),
 +                        ));
 +                    }
 +                }
 +                return;
 +            }
 +
 +            if_chain! {
 +                if !pat.span.from_expansion();
 +                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
 +                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
 +                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
 +                then {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
 +                    self.current_body = self.current_body.or(cx.enclosing_body);
 +                    self.ref_locals.insert(
 +                        id,
 +                        Some(RefPat {
 +                            always_deref: true,
 +                            spans: vec![pat.span],
 +                            app,
 +                            replacements: vec![(pat.span, snip.into())],
 +                            hir_id: pat.hir_id,
 +                        }),
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
 +            local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
 +        }) {
 +            self.possible_borrowers.pop();
 +        }
 +
 +        if Some(body.id()) == self.current_body {
 +            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
 +                let replacements = pat.replacements;
 +                let app = pat.app;
 +                let lint = if pat.always_deref {
 +                    NEEDLESS_BORROW
 +                } else {
 +                    REF_BINDING_TO_REFERENCE
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    lint,
 +                    pat.hir_id,
 +                    pat.spans,
 +                    "this pattern creates a reference to a reference",
 +                    |diag| {
 +                        diag.multipart_suggestion("try this", replacements, app);
 +                    },
 +                );
 +            }
 +            self.current_body = None;
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn try_parse_ref_op<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    typeck: &'tcx TypeckResults<'_>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
 +    let (def_id, arg) = match expr.kind {
 +        ExprKind::MethodCall(_, arg, [], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(path),
 +                hir_id,
 +                ..
 +            },
 +            [arg],
 +        ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
 +        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
 +            return Some((RefOp::Deref, sub_expr));
 +        },
 +        ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
 +        _ => return None,
 +    };
 +    if tcx.is_diagnostic_item(sym::deref_method, def_id) {
 +        Some((RefOp::Method(Mutability::Not), arg))
 +    } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
 +        Some((RefOp::Method(Mutability::Mut), arg))
 +    } else {
 +        None
 +    }
 +}
 +
 +// Checks whether the type for a deref call actually changed the type, not just the mutability of
 +// the reference.
 +fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
 +    match (result_ty.kind(), arg_ty.kind()) {
 +        (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
 +
 +        // The result type for a deref method is always a reference
 +        // Not matching the previous pattern means the argument type is not a reference
 +        // This means that the type did change
 +        _ => false,
 +    }
 +}
 +
 +/// The position of an expression relative to it's parent.
 +#[derive(Clone, Copy, Debug)]
 +enum Position {
 +    MethodReceiver,
 +    /// The method is defined on a reference type. e.g. `impl Foo for &T`
 +    MethodReceiverRefImpl,
 +    Callee,
 +    ImplArg(HirId),
 +    FieldAccess {
 +        name: Symbol,
 +        of_union: bool,
 +    }, // union fields cannot be auto borrowed
 +    Postfix,
 +    Deref,
 +    /// Any other location which will trigger auto-deref to a specific time.
 +    /// Contains the precedence of the parent expression and whether the target type is sized.
 +    DerefStable(i8, bool),
 +    /// Any other location which will trigger auto-reborrowing.
 +    /// Contains the precedence of the parent expression.
 +    ReborrowStable(i8),
 +    /// Contains the precedence of the parent expression.
 +    Other(i8),
 +}
 +impl Position {
 +    fn is_deref_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..))
 +    }
 +
 +    fn is_reborrow_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn can_auto_borrow(self) -> bool {
 +        matches!(
 +            self,
 +            Self::MethodReceiver | Self::FieldAccess { of_union: false, .. } | Self::Callee
 +        )
 +    }
 +
 +    fn lint_explicit_deref(self) -> bool {
 +        matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn precedence(self) -> i8 {
 +        match self {
 +            Self::MethodReceiver
 +            | Self::MethodReceiverRefImpl
 +            | Self::Callee
 +            | Self::FieldAccess { .. }
 +            | Self::Postfix => PREC_POSTFIX,
 +            Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
 +            Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
 +        }
 +    }
 +}
 +
 +/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
 +/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
 +/// locations as those follow different rules.
 +#[expect(clippy::too_many_lines)]
 +fn walk_parents<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 +    e: &'tcx Expr<'_>,
-                                 .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env)
++    msrv: &Msrv,
 +) -> (Position, &'tcx [Adjustment<'tcx>]) {
 +    let mut adjustments = [].as_slice();
 +    let mut precedence = 0i8;
 +    let ctxt = e.span.ctxt();
 +    let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
 +        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
 +        if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
 +            adjustments = cx.typeck_results().expr_adjustments(e);
 +        }
 +        match parent {
 +            Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
 +                Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
 +            },
 +            Node::Item(&Item {
 +                kind: ItemKind::Static(..) | ItemKind::Const(..),
 +                owner_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Const(..),
 +                owner_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Const(..),
 +                owner_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let ty = cx.tcx.type_of(owner_id.def_id);
 +                Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
 +            },
 +
 +            Node::Item(&Item {
 +                kind: ItemKind::Fn(..),
 +                owner_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Fn(..),
 +                owner_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Fn(..),
 +                owner_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let output = cx
 +                    .tcx
 +                    .erase_late_bound_regions(cx.tcx.fn_sig(owner_id.to_def_id()).output());
 +                Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
 +            },
 +
 +            Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
 +                Some(Expr {
 +                    hir_id,
 +                    kind: ExprKind::Struct(path, ..),
 +                    ..
 +                }) => variant_of_res(cx, cx.qpath_res(path, *hir_id))
 +                    .and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name))
 +                    .map(|field_def| {
 +                        ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg()
 +                    }),
 +                _ => None,
 +            },
 +
 +            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
 +                ExprKind::Ret(_) => {
 +                    let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
 +                    Some(
 +                        if let Node::Expr(
 +                            closure_expr @ Expr {
 +                                kind: ExprKind::Closure(closure),
 +                                ..
 +                            },
 +                        ) = cx.tcx.hir().get(owner_id)
 +                        {
 +                            closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
 +                        } else {
 +                            let output = cx
 +                                .tcx
 +                                .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
 +                            ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
 +                        },
 +                    )
 +                },
 +                ExprKind::Closure(closure) => Some(closure_result_position(
 +                    cx,
 +                    closure,
 +                    cx.typeck_results().expr_ty(parent),
 +                    precedence,
 +                )),
 +                ExprKind::Call(func, _) if func.hir_id == child_id => {
 +                    (child_id == e.hir_id).then_some(Position::Callee)
 +                },
 +                ExprKind::Call(func, args) => args
 +                    .iter()
 +                    .position(|arg| arg.hir_id == child_id)
 +                    .zip(expr_sig(cx, func))
 +                    .and_then(|(i, sig)| {
 +                        sig.input_with_hir(i).map(|(hir_ty, ty)| {
 +                            match hir_ty {
 +                                // Type inference for closures can depend on how they're called. Only go by the explicit
 +                                // types here.
 +                                Some(hir_ty) => {
 +                                    binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars())
 +                                },
 +                                None => {
 +                                    // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
 +                                    // `!call_is_qualified(func)` for https://github.com/rust-lang/rust-clippy/issues/9782
 +                                    if e.hir_id == child_id
 +                                        && !call_is_qualified(func)
 +                                        && let ty::Param(param_ty) = ty.skip_binder().kind()
 +                                    {
 +                                        needless_borrow_impl_arg_position(
 +                                            cx,
 +                                            possible_borrowers,
 +                                            parent,
 +                                            i,
 +                                            *param_ty,
 +                                            e,
 +                                            precedence,
 +                                            msrv,
 +                                        )
 +                                    } else {
 +                                        ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
 +                                            .position_for_arg()
 +                                    }
 +                                },
 +                            }
 +                        })
 +                    }),
 +                ExprKind::MethodCall(method, receiver, args, _) => {
 +                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
 +                    if receiver.hir_id == child_id {
 +                        // Check for calls to trait methods where the trait is implemented on a reference.
 +                        // Two cases need to be handled:
 +                        // * `self` methods on `&T` will never have auto-borrow
 +                        // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
 +                        //   priority.
 +                        if e.hir_id != child_id {
 +                            return Some(Position::ReborrowStable(precedence))
 +                        } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
 +                            && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
 +                            && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
 +                            && let subs = cx
 +                                .typeck_results()
 +                                .node_substs_opt(parent.hir_id).map(|subs| &subs[1..]).unwrap_or_default()
 +                            && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
 +                                // Trait methods taking `&self`
 +                                sub_ty
 +                            } else {
 +                                // Trait methods taking `self`
 +                                arg_ty
 +                            } && impl_ty.is_ref()
 +                            && let infcx = cx.tcx.infer_ctxt().build()
 +                            && infcx
-     msrv: Option<RustcVersion>,
++                                .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)
 +                        }
 +                        return Some(Position::MethodReceiver);
 +                    }
 +                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
 +                        let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
 +                        // `e.hir_id == child_id` for https://github.com/rust-lang/rust-clippy/issues/9739
 +                        // `method.args.is_none()` for https://github.com/rust-lang/rust-clippy/issues/9782
 +                        if e.hir_id == child_id && method.args.is_none() && let ty::Param(param_ty) = ty.kind() {
 +                            needless_borrow_impl_arg_position(
 +                                cx,
 +                                possible_borrowers,
 +                                parent,
 +                                i + 1,
 +                                *param_ty,
 +                                e,
 +                                precedence,
 +                                msrv,
 +                            )
 +                        } else {
 +                            ty_auto_deref_stability(
 +                                cx,
 +                                cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
 +                                precedence,
 +                            )
 +                            .position_for_arg()
 +                        }
 +                    })
 +                },
 +                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess {
 +                    name: name.name,
 +                    of_union: is_union(cx.typeck_results(), child),
 +                }),
 +                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
 +                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
 +                | ExprKind::Index(child, _)
 +                    if child.hir_id == e.hir_id =>
 +                {
 +                    Some(Position::Postfix)
 +                },
 +                _ if child_id == e.hir_id => {
 +                    precedence = parent.precedence().order();
 +                    None
 +                },
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    })
 +    .unwrap_or(Position::Other(precedence));
 +    (position, adjustments)
 +}
 +
 +fn is_union<'tcx>(typeck: &'tcx TypeckResults<'_>, path_expr: &'tcx Expr<'_>) -> bool {
 +    typeck
 +        .expr_ty_adjusted(path_expr)
 +        .ty_adt_def()
 +        .map_or(false, rustc_middle::ty::AdtDef::is_union)
 +}
 +
 +fn closure_result_position<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    closure: &'tcx Closure<'_>,
 +    ty: Ty<'tcx>,
 +    precedence: i8,
 +) -> Position {
 +    match closure.fn_decl.output {
 +        FnRetTy::Return(hir_ty) => {
 +            if let Some(sig) = ty_sig(cx, ty)
 +                && let Some(output) = sig.output()
 +            {
 +                binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
 +            } else {
 +                Position::Other(precedence)
 +            }
 +        },
 +        FnRetTy::DefaultReturn(_) => Position::Other(precedence),
 +    }
 +}
 +
 +// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
 +//
 +// e.g.
 +// let x = Box::new(Box::new(0u32));
 +// let y1: &Box<_> = x.deref();
 +// let y2: &Box<_> = &x;
 +//
 +// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
 +// switching to auto-dereferencing.
 +fn binding_ty_auto_deref_stability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx hir::Ty<'_>,
 +    precedence: i8,
 +    binder_args: &'tcx List<BoundVariableKind>,
 +) -> Position {
 +    let TyKind::Rptr(_, ty) = &ty.kind else {
 +        return Position::Other(precedence);
 +    };
 +    let mut ty = ty;
 +
 +    loop {
 +        break match ty.ty.kind {
 +            TyKind::Rptr(_, ref ref_ty) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            TyKind::Path(
 +                QPath::TypeRelative(_, path)
 +                | QPath::Resolved(
 +                    _,
 +                    Path {
 +                        segments: [.., path], ..
 +                    },
 +                ),
 +            ) => {
 +                if let Some(args) = path.args
 +                    && args.args.iter().any(|arg| match arg {
 +                        GenericArg::Infer(_) => true,
 +                        GenericArg::Type(ty) => ty_contains_infer(ty),
 +                        _ => false,
 +                    })
 +                {
 +                    Position::ReborrowStable(precedence)
 +                } else {
 +                    Position::DerefStable(
 +                        precedence,
 +                        cx.tcx
 +                            .erase_late_bound_regions(Binder::bind_with_vars(
 +                                cx.typeck_results().node_type(ty.ty.hir_id),
 +                                binder_args,
 +                            ))
 +                            .is_sized(cx.tcx, cx.param_env.without_caller_bounds()),
 +                    )
 +                }
 +            },
 +            TyKind::Slice(_) => Position::DerefStable(precedence, false),
 +            TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
 +            TyKind::Never
 +            | TyKind::Tup(_)
 +            | TyKind::Path(_) => Position::DerefStable(
 +                precedence,
 +                cx.tcx
 +                    .erase_late_bound_regions(Binder::bind_with_vars(
 +                        cx.typeck_results().node_type(ty.ty.hir_id),
 +                        binder_args,
 +                    ))
 +                    .is_sized(cx.tcx, cx.param_env.without_caller_bounds()),
 +            ),
 +            TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
 +                Position::ReborrowStable(precedence)
 +            },
 +        };
 +    }
 +}
 +
 +// Checks whether a type is inferred at some point.
 +// e.g. `_`, `Box<_>`, `[_]`
 +fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
 +    struct V(bool);
 +    impl Visitor<'_> for V {
 +        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
 +            if self.0
 +                || matches!(
 +                    ty.kind,
 +                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
 +                )
 +            {
 +                self.0 = true;
 +            } else {
 +                walk_ty(self, ty);
 +            }
 +        }
 +
 +        fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
 +            if self.0 || matches!(arg, GenericArg::Infer(_)) {
 +                self.0 = true;
 +            } else if let GenericArg::Type(ty) = arg {
 +                self.visit_ty(ty);
 +            }
 +        }
 +    }
 +    let mut v = V(false);
 +    v.visit_ty(ty);
 +    v.0
 +}
 +
 +fn call_is_qualified(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Path(path) = &expr.kind {
 +        match path {
 +            QPath::Resolved(_, path) => path.segments.last().map_or(false, |segment| segment.args.is_some()),
 +            QPath::TypeRelative(_, segment) => segment.args.is_some(),
 +            QPath::LangItem(..) => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +// Checks whether:
 +// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
 +// * `e`'s type implements `Trait` and is copyable
 +// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
 +//   The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
 +// be moved, but it cannot be.
 +#[expect(clippy::too_many_arguments, clippy::too_many_lines)]
 +fn needless_borrow_impl_arg_position<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 +    parent: &Expr<'tcx>,
 +    arg_index: usize,
 +    param_ty: ParamTy,
 +    mut expr: &Expr<'tcx>,
 +    precedence: i8,
-                 && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
++    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();
 +
 +    let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
 +    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +    let substs_with_expr_ty = cx
 +        .typeck_results()
 +        .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
 +            callee.hir_id
 +        } else {
 +            parent.hir_id
 +        });
 +
 +    let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
 +    let projection_predicates = predicates
 +        .iter()
 +        .filter_map(|predicate| {
 +            if let PredicateKind::Clause(Clause::Projection(projection_predicate)) = predicate.kind().skip_binder() {
 +                Some(projection_predicate)
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    let mut trait_with_ref_mut_self_method = false;
 +
 +    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
 +    if predicates
 +        .iter()
 +        .filter_map(|predicate| {
 +            if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
 +                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
 +            {
 +                Some(trait_predicate.trait_ref.def_id)
 +            } else {
 +                None
 +            }
 +        })
 +        .inspect(|trait_def_id| {
 +            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
 +        })
 +        .all(|trait_def_id| {
 +            Some(trait_def_id) == destruct_trait_def_id
 +                || Some(trait_def_id) == sized_trait_def_id
 +                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
 +        })
 +    {
 +        return Position::Other(precedence);
 +    }
 +
 +    // See:
 +    // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1289294201
 +    // - https://github.com/rust-lang/rust-clippy/pull/9674#issuecomment-1292225232
 +    if projection_predicates
 +        .iter()
 +        .any(|projection_predicate| is_mixed_projection_predicate(cx, callee_def_id, projection_predicate))
 +    {
 +        return Position::Other(precedence);
 +    }
 +
 +    // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
 +    // elements are modified each time `check_referent` is called.
 +    let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
 +
 +    let mut check_reference_and_referent = |reference, referent| {
 +        let referent_ty = cx.typeck_results().expr_ty(referent);
 +
 +        if !is_copy(cx, referent_ty)
 +            && (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
 +                || !referent_used_exactly_once(cx, possible_borrowers, reference))
 +        {
 +            return false;
 +        }
 +
 +        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
 +        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +            return false;
 +        }
 +
 +        if !replace_types(
 +            cx,
 +            param_ty,
 +            referent_ty,
 +            fn_sig,
 +            arg_index,
 +            &projection_predicates,
 +            &mut substs_with_referent_ty,
 +        ) {
 +            return false;
 +        }
 +
 +        predicates.iter().all(|predicate| {
 +            if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder()
 +                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
 +                && 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()
++                && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR)
 +            {
 +                return false;
 +            }
 +
 +            let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
 +            let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
 +            let infcx = cx.tcx.infer_ctxt().build();
 +            infcx.predicate_must_hold_modulo_regions(&obligation)
 +        })
 +    };
 +
 +    let mut needless_borrow = false;
 +    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
 +        if !check_reference_and_referent(expr, referent) {
 +            break;
 +        }
 +        expr = referent;
 +        needless_borrow = true;
 +    }
 +
 +    if needless_borrow {
 +        Position::ImplArg(expr.hir_id)
 +    } else {
 +        Position::Other(precedence)
 +    }
 +}
 +
 +fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
 +    cx.tcx
 +        .associated_items(trait_def_id)
 +        .in_definition_order()
 +        .any(|assoc_item| {
 +            if assoc_item.fn_has_self_parameter {
 +                let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
 +                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
 +            } else {
 +                false
 +            }
 +        })
 +}
 +
 +fn is_mixed_projection_predicate<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    callee_def_id: DefId,
 +    projection_predicate: &ProjectionPredicate<'tcx>,
 +) -> bool {
 +    let generics = cx.tcx.generics_of(callee_def_id);
 +    // The predicate requires the projected type to equal a type parameter from the parent context.
 +    if let Some(term_ty) = projection_predicate.term.ty()
 +        && let ty::Param(term_param_ty) = term_ty.kind()
 +        && (term_param_ty.index as usize) < generics.parent_count
 +    {
 +        // The inner-most self type is a type parameter from the current function.
 +        let mut projection_ty = projection_predicate.projection_ty;
 +        loop {
 +            match projection_ty.self_ty().kind() {
 +                ty::Projection(inner_projection_ty) => {
 +                    projection_ty = *inner_projection_ty;
 +                }
 +                ty::Param(param_ty) => {
 +                    return (param_ty.index as usize) >= generics.parent_count;
 +                }
 +                _ => {
 +                    return false;
 +                }
 +            }
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +fn referent_used_exactly_once<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 +    reference: &Expr<'tcx>,
 +) -> bool {
 +    let mir = enclosing_mir(cx.tcx, reference.hir_id);
 +    if let Some(local) = expr_local(cx.tcx, reference)
 +        && let [location] = *local_assignments(mir, local).as_slice()
 +        && let Some(statement) = mir.basic_blocks[location.block].statements.get(location.statement_index)
 +        && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
 +        && !place.has_deref()
 +        // Ensure not in a loop (https://github.com/rust-lang/rust-clippy/issues/9710)
 +        && TriColorDepthFirstSearch::new(&mir.basic_blocks).run_from(location.block, &mut CycleDetector).is_none()
 +    {
 +        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
 +        if possible_borrowers
 +            .last()
 +            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
 +        {
 +            possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
 +        }
 +        let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
 +        // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
 +        // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
 +        // itself. See the comment in that method for an explanation as to why.
 +        possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
 +            && used_exactly_once(mir, place.local).unwrap_or(false)
 +    } else {
 +        false
 +    }
 +}
 +
 +// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
 +// projected type that is a type parameter. Returns `false` if replacing the types would have an
 +// effect on the function signature beyond substituting `new_ty` for `param_ty`.
 +// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
 +fn replace_types<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_ty: ParamTy,
 +    new_ty: Ty<'tcx>,
 +    fn_sig: FnSig<'tcx>,
 +    arg_index: usize,
 +    projection_predicates: &[ProjectionPredicate<'tcx>],
 +    substs: &mut [ty::GenericArg<'tcx>],
 +) -> bool {
 +    let mut replaced = BitSet::new_empty(substs.len());
 +
 +    let mut deque = VecDeque::with_capacity(substs.len());
 +    deque.push_back((param_ty, new_ty));
 +
 +    while let Some((param_ty, new_ty)) = deque.pop_front() {
 +        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
 +        if !fn_sig
 +            .inputs_and_output
 +            .iter()
 +            .enumerate()
 +            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
 +        {
 +            return false;
 +        }
 +
 +        substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
 +
 +        // The `replaced.insert(...)` check provides some protection against infinite loops.
 +        if replaced.insert(param_ty.index) {
 +            for projection_predicate in projection_predicates {
 +                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
 +                    && let Some(term_ty) = projection_predicate.term.ty()
 +                    && let ty::Param(term_param_ty) = term_ty.kind()
 +                {
 +                    let item_def_id = projection_predicate.projection_ty.item_def_id;
 +                    let assoc_item = cx.tcx.associated_item(item_def_id);
 +                    let projection = cx.tcx
 +                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, []));
 +
 +                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
 +                        && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
 +                    {
 +                        deque.push_back((*term_param_ty, projected_ty));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    true
 +}
 +
 +struct TyPosition<'tcx> {
 +    position: Position,
 +    ty: Option<Ty<'tcx>>,
 +}
 +impl From<Position> for TyPosition<'_> {
 +    fn from(position: Position) -> Self {
 +        Self { position, ty: None }
 +    }
 +}
 +impl<'tcx> TyPosition<'tcx> {
 +    fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
 +        Self {
 +            position: Position::ReborrowStable(precedence),
 +            ty: Some(ty),
 +        }
 +    }
 +    fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
 +        match (self.position, self.ty) {
 +            (Position::ReborrowStable(precedence), Some(ty)) => {
 +                Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env))
 +            },
 +            (position, _) => position,
 +        }
 +    }
 +    fn position_for_arg(self) -> Position {
 +        self.position
 +    }
 +}
 +
 +// Checks whether a type is stable when switching to auto dereferencing,
 +fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
 +    let ty::Ref(_, mut ty, _) = *ty.kind() else {
 +        return Position::Other(precedence).into();
 +    };
 +
 +    loop {
 +        break match *ty.kind() {
 +            ty::Ref(_, ref_ty, _) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
 +            ty::Projection(_) if ty.has_non_region_param() => TyPosition::new_deref_stable_for_result(precedence, ty),
 +            ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(_, substs) if substs.has_non_region_param() => {
 +                TyPosition::new_deref_stable_for_result(precedence, ty)
 +            },
 +            ty::Bool
 +            | ty::Char
 +            | ty::Int(_)
 +            | ty::Uint(_)
 +            | ty::Array(..)
 +            | ty::Float(_)
 +            | ty::RawPtr(..)
 +            | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
 +            ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
 +            ty::Adt(..)
 +            | ty::Foreign(_)
 +            | ty::FnDef(..)
 +            | ty::Generator(..)
 +            | ty::GeneratorWitness(..)
 +            | ty::Closure(..)
 +            | ty::Never
 +            | ty::Tuple(_)
 +            | ty::Projection(_) => {
 +                Position::DerefStable(precedence, ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds())).into()
 +            },
 +        };
 +    }
 +}
 +
 +fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
 +    if let ty::Adt(adt, _) = *ty.kind() {
 +        adt.is_struct() && adt.all_fields().any(|f| f.name == name)
 +    } else {
 +        false
 +    }
 +}
 +
 +#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
 +fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
 +    match state {
 +        State::DerefMethod {
 +            ty_changed_count,
 +            is_final_ufcs,
 +            target_mut,
 +        } => {
 +            let mut app = Applicability::MachineApplicable;
 +            let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +            let ty = cx.typeck_results().expr_ty(expr);
 +            let (_, ref_count) = peel_mid_ty_refs(ty);
 +            let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
 +                // a deref call changing &T -> &U requires two deref operators the first time
 +                // this occurs. One to remove the reference, a second to call the deref impl.
 +                "*".repeat(ty_changed_count + 1)
 +            } else {
 +                "*".repeat(ty_changed_count)
 +            };
 +            let addr_of_str = if ty_changed_count < ref_count {
 +                // Check if a reborrow from &mut T -> &T is required.
 +                if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +                    "&*"
 +                } else {
 +                    ""
 +                }
 +            } else if target_mut == Mutability::Mut {
 +                "&mut "
 +            } else {
 +                "&"
 +            };
 +
 +            let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
 +                format!("({expr_str})")
 +            } else {
 +                expr_str.into_owned()
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                EXPLICIT_DEREF_METHODS,
 +                data.span,
 +                match target_mut {
 +                    Mutability::Not => "explicit `deref` method call",
 +                    Mutability::Mut => "explicit `deref_mut` method call",
 +                },
 +                "try this",
 +                format!("{addr_of_str}{deref_str}{expr_str}"),
 +                app,
 +            );
 +        },
 +        State::DerefedBorrow(state) => {
 +            let mut app = Applicability::MachineApplicable;
 +            let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
 +            let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
 +            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
 +                let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
 +                let sugg = if !snip_is_macro
 +                    && !has_enclosing_paren(&snip)
 +                    && (expr.precedence().order() < data.position.precedence() || calls_field)
 +                {
 +                    format!("({snip})")
 +                } else {
 +                    snip.into()
 +                };
 +                diag.span_suggestion(data.span, "change this to", sugg, app);
 +            });
 +        },
 +        State::ExplicitDeref { mutability } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            let (prefix, precedence) = if let Some(mutability) = mutability
 +                && !cx.typeck_results().expr_ty(expr).is_ref()
 +            {
 +                let prefix = match mutability {
 +                    Mutability::Not => "&",
 +                    Mutability::Mut => "&mut ",
 +                };
 +                (prefix, 0)
 +            } else {
 +                ("", data.position.precedence())
 +            };
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +                    let sugg =
 +                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
 +                            format!("{prefix}({snip})")
 +                        } else {
 +                            format!("{prefix}{snip}")
 +                        };
 +                    diag.span_suggestion(data.span, "try this", sugg, app);
 +                },
 +            );
 +        },
 +        State::ExplicitDerefField { .. } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
 +                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
 +                },
 +            );
 +        },
 +        State::Borrow { .. } | State::Reborrow { .. } => (),
 +    }
 +}
 +
 +impl<'tcx> Dereferencing<'tcx> {
 +    fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
 +        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
 +            if let Some(pat) = outer_pat {
 +                // Check for auto-deref
 +                if !matches!(
 +                    cx.typeck_results().expr_adjustments(e),
 +                    [
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        ..
 +                    ]
 +                ) {
 +                    match get_parent_expr(cx, e) {
 +                        // Field accesses are the same no matter the number of references.
 +                        Some(Expr {
 +                            kind: ExprKind::Field(..),
 +                            ..
 +                        }) => (),
 +                        Some(&Expr {
 +                            span,
 +                            kind: ExprKind::Unary(UnOp::Deref, _),
 +                            ..
 +                        }) if !span.from_expansion() => {
 +                            // Remove explicit deref.
 +                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
 +                            pat.replacements.push((span, snip.into()));
 +                        },
 +                        Some(parent) if !parent.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            if parent.precedence().order() == PREC_POSTFIX {
 +                                // Parentheses would be needed here, don't lint.
 +                                *outer_pat = None;
 +                            } else {
 +                                pat.always_deref = false;
 +                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
 +                                pat.replacements.push((e.span, format!("&{snip}")));
 +                            }
 +                        },
 +                        _ if !e.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            pat.always_deref = false;
 +                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
 +                            pat.replacements.push((e.span, format!("&{snip}")));
 +                        },
 +                        // Edge case for macros. The span of the identifier will usually match the context of the
 +                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
 +                        // macros
 +                        _ => *outer_pat = None,
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index d870e0ceef471ae2505aa6aafcdf1f483a2c4c6c,0000000000000000000000000000000000000000..9e596ca8157eb93b58fc827d6c4d3eda261a116c
mode 100644,000000..100644
--- /dev/null
@@@ -1,528 -1,0 +1,528 @@@
-     self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
-     Ty, TyCtxt,
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 +use clippy_utils::{is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 +use rustc_hir::{
 +    self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
 +    Unsafety,
 +};
 +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,
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Hash` but implementing `PartialEq`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `HashMap`) so it’s probably a bad idea to use a
 +    /// default-generated `Hash` implementation with an explicitly defined
 +    /// `PartialEq`. In particular, the following must hold for any type:
 +    ///
 +    /// ```text
 +    /// k1 == k2 ⇒ hash(k1) == hash(k2)
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[derive(Hash)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialEq for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DERIVE_HASH_XOR_EQ,
 +    correctness,
 +    "deriving `Hash` but implementing `PartialEq` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Ord` but implementing `PartialOrd`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `sort`) so it’s probably a bad idea to use a
 +    /// default-generated `Ord` implementation with an explicitly defined
 +    /// `PartialOrd`. In particular, the following must hold for any type
 +    /// implementing `Ord`:
 +    ///
 +    /// ```text
 +    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
 +    ///        Some(self.cmp(other))
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Ord for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// or, if you don't need a custom ordering:
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
 +    /// struct Foo;
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub DERIVE_ORD_XOR_PARTIAL_ORD,
 +    correctness,
 +    "deriving `Ord` but implementing `PartialOrd` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `Clone` implementations for `Copy`
 +    /// types.
 +    ///
 +    /// ### Why is this bad?
 +    /// To avoid surprising behavior, these traits should
 +    /// agree and the behavior of `Copy` cannot be overridden. In almost all
 +    /// situations a `Copy` type should have a `Clone` implementation that does
 +    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
 +    /// gets you.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Copy)]
 +    /// struct Foo;
 +    ///
 +    /// impl Clone for Foo {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPL_IMPL_CLONE_ON_COPY,
 +    pedantic,
 +    "implementing `Clone` explicitly on `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `serde::Deserialize` on a type that
 +    /// has methods using `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Deriving `serde::Deserialize` will create a constructor
 +    /// that may violate invariants hold by another constructor.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use serde::Deserialize;
 +    ///
 +    /// #[derive(Deserialize)]
 +    /// pub struct Foo {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         // setup here ..
 +    ///     }
 +    ///
 +    ///     pub unsafe fn parts() -> (&str, &str) {
 +    ///         // assumes invariants hold
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types that derive `PartialEq` and could implement `Eq`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
 +    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
 +    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
 +    /// `Eq` themselves.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[derive(PartialEq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    nursery,
 +    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE,
 +    DERIVE_PARTIAL_EQ_WITHOUT_EQ
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Derive {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(ref trait_ref),
 +            ..
 +        }) = item.kind
 +        {
 +            let ty = cx.tcx.type_of(item.owner_id);
 +            let is_automatically_derived = cx.tcx.has_attr(item.owner_id.to_def_id(), sym::automatically_derived);
 +
 +            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
 +            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 +
 +            if is_automatically_derived {
 +                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
 +                check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
 +            } else {
 +                check_copy_clone(cx, item, trait_ref, ty);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
 +fn check_hash_peq<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    hash_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
 +        then {
 +            // Look for the PartialEq implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
 +                let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
 +        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
 +        if let Some(def_id) = &trait_ref.trait_def_id();
 +        if *def_id == ord_trait_def_id;
 +        then {
 +            // Look for the PartialOrd implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
 +                let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    let clone_id = match cx.tcx.lang_items().clone_trait() {
 +        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
 +        _ => return,
 +    };
 +    let Some(copy_id) = cx.tcx.lang_items().copy_trait() else { return };
 +    let (ty_adt, ty_subs) = match *ty.kind() {
 +        // Unions can't derive clone.
 +        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
 +        _ => return,
 +    };
 +    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
 +    // there's a Copy impl for any instance of the adt.
 +    if !is_copy(cx, ty) {
 +        if ty_subs.non_erasable_generics().next().is_some() {
 +            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
 +                impls
 +                    .iter()
 +                    .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
 +            });
 +            if !has_copy_impl {
 +                return;
 +            }
 +        } else {
 +            return;
 +        }
 +    }
 +    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
 +    // this impl.
 +    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
 +        return;
 +    }
 +
 +    span_lint_and_note(
 +        cx,
 +        EXPL_IMPL_CLONE_ON_COPY,
 +        item.span,
 +        "you are implementing `Clone` explicitly on a `Copy` type",
 +        Some(item.span),
 +        "consider deriving `Clone` or removing `Copy`",
 +    );
 +}
 +
 +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
 +fn check_unsafe_derive_deserialize<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item: &Item<'_>,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
 +        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
 +        walk_item(&mut visitor, item);
 +        visitor.has_unsafe
 +    }
 +
 +    if_chain! {
 +        if let Some(trait_def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if let Some(local_def_id) = def.did().as_local();
 +        let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +        if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
 +        if cx.tcx.inherent_impls(def.did())
 +            .iter()
 +            .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
 +            .any(|imp| has_unsafe(cx, imp));
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                UNSAFE_DERIVE_DESERIALIZE,
 +                item.span,
 +                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
 +                None,
 +                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
 +            );
 +        }
 +    }
 +}
 +
 +struct UnsafeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    has_unsafe: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: HirId) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.unsafety == Unsafety::Unsafe;
 +            then {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_fn(self, kind, decl, body_id, id);
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
 +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::Adt(adt, substs) = ty.kind();
 +        if cx.tcx.visibility(adt.did()).is_public();
 +        if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
 +        let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
 +        if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []);
 +        // If all of our fields implement `Eq`, we can implement `Eq` too
 +        if adt
 +            .all_fields()
 +            .map(|f| f.ty(cx.tcx, substs))
 +            .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, []));
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +                span.ctxt().outer_expn_data().call_site,
 +                "you are deriving `PartialEq` and can implement `Eq`",
 +                "consider deriving `Eq` as well",
 +                "PartialEq, Eq".to_string(),
 +                Applicability::MachineApplicable,
 +            )
 +        }
 +    }
 +}
 +
 +/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
 +fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
 +    // Initial map from generic index to param def.
 +    // Vec<(param_def, needs_eq)>
 +    let mut params = tcx
 +        .generics_of(did)
 +        .params
 +        .iter()
 +        .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
 +        .collect::<Vec<_>>();
 +
 +    let ty_predicates = tcx.predicates_of(did).predicates;
 +    for (p, _) in ty_predicates {
 +        if let PredicateKind::Clause(Clause::Trait(p)) = p.kind().skip_binder()
 +            && p.trait_ref.def_id == eq_trait_id
 +            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
 +            && p.constness == BoundConstness::NotConst
 +        {
 +            // Flag types which already have an `Eq` bound.
 +            params[self_ty.index as usize].1 = false;
 +        }
 +    }
 +
 +    ParamEnv::new(
 +        tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
 +            params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
 +                tcx.mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Trait(TraitPredicate {
 +                    trait_ref: TraitRef::new(
 +                        eq_trait_id,
 +                        tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
 +                    ),
 +                    constness: BoundConstness::NotConst,
 +                    polarity: ImplPolarity::Positive,
 +                }))))
 +            }),
 +        )),
 +        Reveal::UserFacing,
 +        Constness::NotConst,
 +    )
 +}
index ae5f9424b2323b015f34a85547c880b5b387bb49,0000000000000000000000000000000000000000..cdc23a4d22739ef79ebc7993a88ea902de69e617
mode 100644,000000..100644
--- /dev/null
@@@ -1,935 -1,0 +1,935 @@@
-     style,
 +use clippy_utils::attrs::is_doc_hidden;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 +use clippy_utils::source::{first_line_of_span, snippet_with_applicability};
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
 +use if_chain::if_chain;
 +use itertools::Itertools;
 +use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind};
 +use rustc_ast::token::CommentKind;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_data_structures::sync::Lrc;
 +use rustc_errors::emitter::EmitterWriter;
 +use rustc_errors::{Applicability, Handler, SuggestionStyle};
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::{AnonConst, Expr};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_parse::maybe_new_parser_from_source_str;
 +use rustc_parse::parser::ForceCollect;
 +use rustc_session::parse::ParseSess;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::edition::Edition;
 +use rustc_span::source_map::{BytePos, FilePathMapping, SourceMap, Span};
 +use rustc_span::{sym, FileName, Pos};
 +use std::io;
 +use std::ops::Range;
 +use std::thread;
 +use url::Url;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the presence of `_`, `::` or camel-case words
 +    /// outside ticks in documentation.
 +    ///
 +    /// ### Why is this bad?
 +    /// *Rustdoc* supports markdown formatting, `_`, `::` and
 +    /// camel-case probably indicates some code which should be included between
 +    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
 +    /// consider that.
 +    ///
 +    /// ### Known problems
 +    /// Lots of bad docs won’t be fixed, what the lint checks
 +    /// for is limited, and there are still false positives. HTML elements and their
 +    /// content are not linted.
 +    ///
 +    /// In addition, when writing documentation comments, including `[]` brackets
 +    /// inside a link text would trip the parser. Therefore, documenting link with
 +    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
 +    /// would fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// /// Do something with the foo_bar parameter. See also
 +    /// /// that::other::module::foo.
 +    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
 +    /// fn doit(foo_bar: usize) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Link text with `[]` brackets should be written as following:
 +    /// /// Consume the array and return the inner
 +    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
 +    /// /// [SmallVec]: SmallVec
 +    /// fn main() {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "presence of `_`, `::` or camel-case outside backticks in documentation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the doc comments of publicly visible
 +    /// unsafe functions and warns if there is no `# Safety` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unsafe functions should document their safety
 +    /// preconditions, so that users can be sure they are using them safely.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// This function should really be documented
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    ///
 +    /// At least write a line about safety:
 +    ///
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// # Safety
 +    /// ///
 +    /// /// This function should not be called before the horsemen are ready.
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MISSING_SAFETY_DOC,
 +    style,
 +    "`pub unsafe fn` without `# Safety` docs"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// return a `Result` type and warns if there is no `# Errors` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the type of errors that can be returned from a
 +    /// function can help callers write code to handle the errors appropriately.
 +    ///
 +    /// ### Examples
 +    /// Since the following function returns a `Result` it has an `# Errors` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    ///# use std::io;
 +    /// /// # Errors
 +    /// ///
 +    /// /// Will return `Err` if `filename` does not exist or the user does not have
 +    /// /// permission to read it.
 +    /// pub fn read(filename: String) -> io::Result<String> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub MISSING_ERRORS_DOC,
 +    pedantic,
 +    "`pub fn` returns `Result` without `# Errors` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// may panic and warns if there is no `# Panics` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the scenarios in which panicking occurs
 +    /// can help callers who do not want to panic to avoid those situations.
 +    ///
 +    /// ### Examples
 +    /// Since the following function may panic it has a `# Panics` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    /// /// # Panics
 +    /// ///
 +    /// /// Will panic if y is 0
 +    /// pub fn divide_by(x: i32, y: i32) -> i32 {
 +    ///     if y == 0 {
 +    ///         panic!("Cannot divide by 0")
 +    ///     } else {
 +    ///         x / y
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MISSING_PANICS_DOC,
 +    pedantic,
 +    "`pub fn` may panic without `# Panics` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `fn main() { .. }` in doctests
 +    ///
 +    /// ### Why is this bad?
 +    /// The test can be shorter (and likely more readable)
 +    /// if the `fn main()` is left implicit.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// /// An example of a doctest with a `main()` function
 +    /// ///
 +    /// /// # Examples
 +    /// ///
 +    /// /// ```
 +    /// /// fn main() {
 +    /// ///     // this needs not be in an `fn`
 +    /// /// }
 +    /// /// ```
 +    /// fn needless_main() {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub NEEDLESS_DOCTEST_MAIN,
 +    style,
 +    "presence of `fn main() {` in code examples"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
 +    /// outside of code blocks
 +    /// ### Why is this bad?
 +    /// It is likely a typo when defining an intra-doc link
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// /// See also: ['foo']
 +    /// fn bar() {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// /// See also: [`foo`]
 +    /// fn bar() {}
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub DOC_LINK_WITH_QUOTES,
 +    pedantic,
 +    "possible typo for an intra-doc link"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the doc comments of publicly visible
 +    /// safe functions and traits and warns if there is a `# Safety` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Safe functions and traits are safe to implement and therefore do not
 +    /// need to describe safety preconditions that users are required to uphold.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// # Safety
 +    /// ///
 +    /// /// This function should not be called before the horsemen are ready.
 +    /// pub fn start_apocalypse_but_safely(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    ///
 +    /// The function is safe, so there shouldn't be any preconditions
 +    /// that have to be explained for safety reasons.
 +    ///
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// This function should really be documented
 +    /// pub fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub UNNECESSARY_SAFETY_DOC,
++    restriction,
 +    "`pub fn` or `pub trait` with `# Safety` docs"
 +}
 +
 +#[expect(clippy::module_name_repetitions)]
 +#[derive(Clone)]
 +pub struct DocMarkdown {
 +    valid_idents: FxHashSet<String>,
 +    in_trait_impl: bool,
 +}
 +
 +impl DocMarkdown {
 +    pub fn new(valid_idents: FxHashSet<String>) -> Self {
 +        Self {
 +            valid_idents,
 +            in_trait_impl: false,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DocMarkdown => [
 +    DOC_LINK_WITH_QUOTES,
 +    DOC_MARKDOWN,
 +    MISSING_SAFETY_DOC,
 +    MISSING_ERRORS_DOC,
 +    MISSING_PANICS_DOC,
 +    NEEDLESS_DOCTEST_MAIN,
 +    UNNECESSARY_SAFETY_DOC,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
 +    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
 +        let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
 +        check_attrs(cx, &self.valid_idents, attrs);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
 +        match item.kind {
 +            hir::ItemKind::Fn(ref sig, _, body_id) => {
 +                if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    let mut fpu = FindPanicUnwrap {
 +                        cx,
 +                        typeck_results: cx.tcx.typeck(item.owner_id.def_id),
 +                        panic_span: None,
 +                    };
 +                    fpu.visit_expr(body.value);
 +                    lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
 +                }
 +            },
 +            hir::ItemKind::Impl(impl_) => {
 +                self.in_trait_impl = impl_.of_trait.is_some();
 +            },
 +            hir::ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
 +                (false, hir::Unsafety::Unsafe) => span_lint(
 +                    cx,
 +                    MISSING_SAFETY_DOC,
 +                    cx.tcx.def_span(item.owner_id),
 +                    "docs for unsafe trait missing `# Safety` section",
 +                ),
 +                (true, hir::Unsafety::Normal) => span_lint(
 +                    cx,
 +                    UNNECESSARY_SAFETY_DOC,
 +                    cx.tcx.def_span(item.owner_id),
 +                    "docs for safe trait have unnecessary `# Safety` section",
 +                ),
 +                _ => (),
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl { .. } = item.kind {
 +            self.in_trait_impl = false;
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
 +            if !in_external_macro(cx.tcx.sess, item.span) {
 +                lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, None, None);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else { return };
 +        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
 +            let body = cx.tcx.hir().body(body_id);
 +            let mut fpu = FindPanicUnwrap {
 +                cx,
 +                typeck_results: cx.tcx.typeck(item.owner_id.def_id),
 +                panic_span: None,
 +            };
 +            fpu.visit_expr(body.value);
 +            lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
 +        }
 +    }
 +}
 +
 +fn lint_for_missing_headers(
 +    cx: &LateContext<'_>,
 +    def_id: LocalDefId,
 +    sig: &hir::FnSig<'_>,
 +    headers: DocHeaders,
 +    body_id: Option<hir::BodyId>,
 +    panic_span: Option<Span>,
 +) {
 +    if !cx.effective_visibilities.is_exported(def_id) {
 +        return; // Private functions do not require doc comments
 +    }
 +
 +    // do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
 +    if cx
 +        .tcx
 +        .hir()
 +        .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
 +        .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
 +    {
 +        return;
 +    }
 +
 +    let span = cx.tcx.def_span(def_id);
 +    match (headers.safety, sig.header.unsafety) {
 +        (false, hir::Unsafety::Unsafe) => span_lint(
 +            cx,
 +            MISSING_SAFETY_DOC,
 +            span,
 +            "unsafe function's docs miss `# Safety` section",
 +        ),
 +        (true, hir::Unsafety::Normal) => span_lint(
 +            cx,
 +            UNNECESSARY_SAFETY_DOC,
 +            span,
 +            "safe function's docs have unnecessary `# Safety` section",
 +        ),
 +        _ => (),
 +    }
 +    if !headers.panics && panic_span.is_some() {
 +        span_lint_and_note(
 +            cx,
 +            MISSING_PANICS_DOC,
 +            span,
 +            "docs for function which may panic missing `# Panics` section",
 +            panic_span,
 +            "first possible panic found here",
 +        );
 +    }
 +    if !headers.errors {
 +        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +        if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
 +            span_lint(
 +                cx,
 +                MISSING_ERRORS_DOC,
 +                span,
 +                "docs for function returning `Result` missing `# Errors` section",
 +            );
 +        } else {
 +            if_chain! {
 +                if let Some(body_id) = body_id;
 +                if let Some(future) = cx.tcx.lang_items().future_trait();
 +                let typeck = cx.tcx.typeck_body(body_id);
 +                let body = cx.tcx.hir().body(body_id);
 +                let ret_ty = typeck.expr_ty(body.value);
 +                if implements_trait(cx, ret_ty, future, &[]);
 +                if let ty::Generator(_, subs, _) = ret_ty.kind();
 +                if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
 +                then {
 +                    span_lint(
 +                        cx,
 +                        MISSING_ERRORS_DOC,
 +                        span,
 +                        "docs for function returning `Result` missing `# Errors` section",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Cleanup documentation decoration.
 +///
 +/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 +/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
 +/// need to keep track of
 +/// the spans but this function is inspired from the later.
 +#[expect(clippy::cast_possible_truncation)]
 +#[must_use]
 +pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
 +    // one-line comments lose their prefix
 +    if comment_kind == CommentKind::Line {
 +        let mut doc = doc.to_owned();
 +        doc.push('\n');
 +        let len = doc.len();
 +        // +3 skips the opening delimiter
 +        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
 +    }
 +
 +    let mut sizes = vec![];
 +    let mut contains_initial_stars = false;
 +    for line in doc.lines() {
 +        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
 +        debug_assert_eq!(offset as u32 as usize, offset);
 +        contains_initial_stars |= line.trim_start().starts_with('*');
 +        // +1 adds the newline, +3 skips the opening delimiter
 +        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
 +    }
 +    if !contains_initial_stars {
 +        return (doc.to_string(), sizes);
 +    }
 +    // remove the initial '*'s if any
 +    let mut no_stars = String::with_capacity(doc.len());
 +    for line in doc.lines() {
 +        let mut chars = line.chars();
 +        for c in &mut chars {
 +            if c.is_whitespace() {
 +                no_stars.push(c);
 +            } else {
 +                no_stars.push(if c == '*' { ' ' } else { c });
 +                break;
 +            }
 +        }
 +        no_stars.push_str(chars.as_str());
 +        no_stars.push('\n');
 +    }
 +
 +    (no_stars, sizes)
 +}
 +
 +#[derive(Copy, Clone, Default)]
 +struct DocHeaders {
 +    safety: bool,
 +    errors: bool,
 +    panics: bool,
 +}
 +
 +fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[Attribute]) -> Option<DocHeaders> {
 +    use pulldown_cmark::{BrokenLink, CowStr, Options};
 +    /// We don't want the parser to choke on intra doc links. Since we don't
 +    /// actually care about rendering them, just pretend that all broken links are
 +    /// point to a fake address.
 +    #[expect(clippy::unnecessary_wraps)] // we're following a type signature
 +    fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
 +        Some(("fake".into(), "fake".into()))
 +    }
 +
 +    let mut doc = String::new();
 +    let mut spans = vec![];
 +
 +    for attr in attrs {
 +        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
 +            let (comment, current_spans) = strip_doc_comment_decoration(comment.as_str(), comment_kind, attr.span);
 +            spans.extend_from_slice(&current_spans);
 +            doc.push_str(&comment);
 +        } else if attr.has_name(sym::doc) {
 +            // ignore mix of sugared and non-sugared doc
 +            // don't trigger the safety or errors check
 +            return None;
 +        }
 +    }
 +
 +    let mut current = 0;
 +    for &mut (ref mut offset, _) in &mut spans {
 +        let offset_copy = *offset;
 +        *offset = current;
 +        current += offset_copy;
 +    }
 +
 +    if doc.is_empty() {
 +        return Some(DocHeaders::default());
 +    }
 +
 +    let mut cb = fake_broken_link_callback;
 +
 +    let parser =
 +        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
 +    // Iterate over all `Events` and combine consecutive events into one
 +    let events = parser.coalesce(|previous, current| {
 +        use pulldown_cmark::Event::Text;
 +
 +        let previous_range = previous.1;
 +        let current_range = current.1;
 +
 +        match (previous.0, current.0) {
 +            (Text(previous), Text(current)) => {
 +                let mut previous = previous.to_string();
 +                previous.push_str(&current);
 +                Ok((Text(previous.into()), previous_range))
 +            },
 +            (previous, current) => Err(((previous, previous_range), (current, current_range))),
 +        }
 +    });
 +    Some(check_doc(cx, valid_idents, events, &spans))
 +}
 +
 +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 +
 +fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
 +    cx: &LateContext<'_>,
 +    valid_idents: &FxHashSet<String>,
 +    events: Events,
 +    spans: &[(usize, Span)],
 +) -> DocHeaders {
 +    // true if a safety header was found
 +    use pulldown_cmark::Event::{
 +        Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 +    };
 +    use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
 +    use pulldown_cmark::{CodeBlockKind, CowStr};
 +
 +    let mut headers = DocHeaders::default();
 +    let mut in_code = false;
 +    let mut in_link = None;
 +    let mut in_heading = false;
 +    let mut is_rust = false;
 +    let mut edition = None;
 +    let mut ticks_unbalanced = false;
 +    let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
 +    let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
 +    for (event, range) in events {
 +        match event {
 +            Start(CodeBlock(ref kind)) => {
 +                in_code = true;
 +                if let CodeBlockKind::Fenced(lang) = kind {
 +                    for item in lang.split(',') {
 +                        if item == "ignore" {
 +                            is_rust = false;
 +                            break;
 +                        }
 +                        if let Some(stripped) = item.strip_prefix("edition") {
 +                            is_rust = true;
 +                            edition = stripped.parse::<Edition>().ok();
 +                        } else if item.is_empty() || RUST_CODE.contains(&item) {
 +                            is_rust = true;
 +                        }
 +                    }
 +                }
 +            },
 +            End(CodeBlock(_)) => {
 +                in_code = false;
 +                is_rust = false;
 +            },
 +            Start(Link(_, url, _)) => in_link = Some(url),
 +            End(Link(..)) => in_link = None,
 +            Start(Heading(_, _, _) | Paragraph | Item) => {
 +                if let Start(Heading(_, _, _)) = event {
 +                    in_heading = true;
 +                }
 +                ticks_unbalanced = false;
 +                let (_, span) = get_current_span(spans, range.start);
 +                paragraph_span = first_line_of_span(cx, span);
 +            },
 +            End(Heading(_, _, _) | Paragraph | Item) => {
 +                if let End(Heading(_, _, _)) = event {
 +                    in_heading = false;
 +                }
 +                if ticks_unbalanced {
 +                    span_lint_and_help(
 +                        cx,
 +                        DOC_MARKDOWN,
 +                        paragraph_span,
 +                        "backticks are unbalanced",
 +                        None,
 +                        "a backtick may be missing a pair",
 +                    );
 +                } else {
 +                    for (text, span) in text_to_check {
 +                        check_text(cx, valid_idents, &text, span);
 +                    }
 +                }
 +                text_to_check = Vec::new();
 +            },
 +            Start(_tag) | End(_tag) => (), // We don't care about other tags
 +            Html(_html) => (),             // HTML is weird, just ignore it
 +            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
 +            FootnoteReference(text) | Text(text) => {
 +                let (begin, span) = get_current_span(spans, range.start);
 +                paragraph_span = paragraph_span.with_hi(span.hi());
 +                ticks_unbalanced |= text.contains('`') && !in_code;
 +                if Some(&text) == in_link.as_ref() || ticks_unbalanced {
 +                    // Probably a link of the form `<http://example.com>`
 +                    // Which are represented as a link to "http://example.com" with
 +                    // text "http://example.com" by pulldown-cmark
 +                    continue;
 +                }
 +                let trimmed_text = text.trim();
 +                headers.safety |= in_heading && trimmed_text == "Safety";
 +                headers.safety |= in_heading && trimmed_text == "Implementation safety";
 +                headers.safety |= in_heading && trimmed_text == "Implementation Safety";
 +                headers.errors |= in_heading && trimmed_text == "Errors";
 +                headers.panics |= in_heading && trimmed_text == "Panics";
 +                if in_code {
 +                    if is_rust {
 +                        let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
 +                        check_code(cx, &text, edition, span);
 +                    }
 +                } else {
 +                    check_link_quotes(cx, in_link.is_some(), trimmed_text, span, &range, begin, text.len());
 +                    // Adjust for the beginning of the current `Event`
 +                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
 +                    text_to_check.push((text, span));
 +                }
 +            },
 +        }
 +    }
 +    headers
 +}
 +
 +fn check_link_quotes(
 +    cx: &LateContext<'_>,
 +    in_link: bool,
 +    trimmed_text: &str,
 +    span: Span,
 +    range: &Range<usize>,
 +    begin: usize,
 +    text_len: usize,
 +) {
 +    if in_link && trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'') {
 +        // fix the span to only point at the text within the link
 +        let lo = span.lo() + BytePos::from_usize(range.start - begin);
 +        span_lint(
 +            cx,
 +            DOC_LINK_WITH_QUOTES,
 +            span.with_lo(lo).with_hi(lo + BytePos::from_usize(text_len)),
 +            "possible intra-doc link using quotes instead of backticks",
 +        );
 +    }
 +}
 +
 +fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
 +    let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
 +        Ok(o) => o,
 +        Err(e) => e - 1,
 +    };
 +    spans[index]
 +}
 +
 +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
 +    fn has_needless_main(code: String, edition: Edition) -> bool {
 +        rustc_driver::catch_fatal_errors(|| {
 +            rustc_span::create_session_globals_then(edition, || {
 +                let filename = FileName::anon_source_code(&code);
 +
 +                let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +                let fallback_bundle =
 +                    rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
 +                let emitter = EmitterWriter::new(
 +                    Box::new(io::sink()),
 +                    None,
 +                    None,
 +                    fallback_bundle,
 +                    false,
 +                    false,
 +                    false,
 +                    None,
 +                    false,
 +                    false,
 +                );
 +                let handler = Handler::with_emitter(false, None, Box::new(emitter));
 +                let sess = ParseSess::with_span_handler(handler, sm);
 +
 +                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
 +                    Ok(p) => p,
 +                    Err(errs) => {
 +                        drop(errs);
 +                        return false;
 +                    },
 +                };
 +
 +                let mut relevant_main_found = false;
 +                loop {
 +                    match parser.parse_item(ForceCollect::No) {
 +                        Ok(Some(item)) => match &item.kind {
 +                            ItemKind::Fn(box Fn {
 +                                sig, body: Some(block), ..
 +                            }) if item.ident.name == sym::main => {
 +                                let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
 +                                let returns_nothing = match &sig.decl.output {
 +                                    FnRetTy::Default(..) => true,
 +                                    FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
 +                                    FnRetTy::Ty(_) => false,
 +                                };
 +
 +                                if returns_nothing && !is_async && !block.stmts.is_empty() {
 +                                    // This main function should be linted, but only if there are no other functions
 +                                    relevant_main_found = true;
 +                                } else {
 +                                    // This main function should not be linted, we're done
 +                                    return false;
 +                                }
 +                            },
 +                            // Tests with one of these items are ignored
 +                            ItemKind::Static(..)
 +                            | ItemKind::Const(..)
 +                            | ItemKind::ExternCrate(..)
 +                            | ItemKind::ForeignMod(..)
 +                            // Another function was found; this case is ignored
 +                            | ItemKind::Fn(..) => return false,
 +                            _ => {},
 +                        },
 +                        Ok(None) => break,
 +                        Err(e) => {
 +                            e.cancel();
 +                            return false;
 +                        },
 +                    }
 +                }
 +
 +                relevant_main_found
 +            })
 +        })
 +        .ok()
 +        .unwrap_or_default()
 +    }
 +
 +    // Because of the global session, we need to create a new session in a different thread with
 +    // the edition we need.
 +    let text = text.to_owned();
 +    if thread::spawn(move || has_needless_main(text, edition))
 +        .join()
 +        .expect("thread::spawn failed")
 +    {
 +        span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
 +    }
 +}
 +
 +fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
 +    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
 +        // Trim punctuation as in `some comment (see foo::bar).`
 +        //                                                   ^^
 +        // Or even as in `_foo bar_` which is emphasized. Also preserve `::` as a prefix/suffix.
 +        let mut word = word.trim_matches(|c: char| !c.is_alphanumeric() && c != ':');
 +
 +        // Remove leading or trailing single `:` which may be part of a sentence.
 +        if word.starts_with(':') && !word.starts_with("::") {
 +            word = word.trim_start_matches(':');
 +        }
 +        if word.ends_with(':') && !word.ends_with("::") {
 +            word = word.trim_end_matches(':');
 +        }
 +
 +        if valid_idents.contains(word) || word.chars().all(|c| c == ':') {
 +            continue;
 +        }
 +
 +        // Adjust for the current word
 +        let offset = word.as_ptr() as usize - text.as_ptr() as usize;
 +        let span = Span::new(
 +            span.lo() + BytePos::from_usize(offset),
 +            span.lo() + BytePos::from_usize(offset + word.len()),
 +            span.ctxt(),
 +            span.parent(),
 +        );
 +
 +        check_word(cx, word, span);
 +    }
 +}
 +
 +fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
 +    /// Checks if a string is camel-case, i.e., contains at least two uppercase
 +    /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
 +    /// Plurals are also excluded (`IDs` is ok).
 +    fn is_camel_case(s: &str) -> bool {
 +        if s.starts_with(|c: char| c.is_ascii_digit()) {
 +            return false;
 +        }
 +
 +        let s = s.strip_suffix('s').unwrap_or(s);
 +
 +        s.chars().all(char::is_alphanumeric)
 +            && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
 +            && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
 +    }
 +
 +    fn has_underscore(s: &str) -> bool {
 +        s != "_" && !s.contains("\\_") && s.contains('_')
 +    }
 +
 +    fn has_hyphen(s: &str) -> bool {
 +        s != "-" && s.contains('-')
 +    }
 +
 +    if let Ok(url) = Url::parse(word) {
 +        // try to get around the fact that `foo::bar` parses as a valid URL
 +        if !url.cannot_be_a_base() {
 +            span_lint(
 +                cx,
 +                DOC_MARKDOWN,
 +                span,
 +                "you should put bare URLs between `<`/`>` or make a proper Markdown link",
 +            );
 +
 +            return;
 +        }
 +    }
 +
 +    // We assume that mixed-case words are not meant to be put inside backticks. (Issue #2343)
 +    if has_underscore(word) && has_hyphen(word) {
 +        return;
 +    }
 +
 +    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
 +        let mut applicability = Applicability::MachineApplicable;
 +
 +        span_lint_and_then(
 +            cx,
 +            DOC_MARKDOWN,
 +            span,
 +            "item in documentation is missing backticks",
 +            |diag| {
 +                let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
 +                diag.span_suggestion_with_style(
 +                    span,
 +                    "try",
 +                    format!("`{snippet}`"),
 +                    applicability,
 +                    // always show the suggestion in a separate line, since the
 +                    // inline presentation adds another pair of backticks
 +                    SuggestionStyle::ShowAlways,
 +                );
 +            },
 +        );
 +    }
 +}
 +
 +struct FindPanicUnwrap<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    panic_span: Option<Span>,
 +    typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.panic_span.is_some() {
 +            return;
 +        }
 +
 +        if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) {
 +            if is_panic(self.cx, macro_call.def_id)
 +                || matches!(
 +                    self.cx.tcx.item_name(macro_call.def_id).as_str(),
 +                    "assert" | "assert_eq" | "assert_ne" | "todo"
 +                )
 +            {
 +                self.panic_span = Some(macro_call.span);
 +            }
 +        }
 +
 +        // check for `unwrap`
 +        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
 +            let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
 +            if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
 +                || is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
 +            {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    // Panics in const blocks will cause compilation to fail.
 +    fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index f34cbee03558b74d0c4babaa3a7dab979d2b6ba9,0000000000000000000000000000000000000000..3543910c3b55b470cb58db75a3e8c94cad7c0e5f
mode 100644,000000..100644
--- /dev/null
@@@ -1,238 -1,0 +1,252 @@@
- use rustc_middle::ty::{self, Ty, TypeVisitable};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::higher::VecArgs;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::usage::local_used_after_expr;
 +use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +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;
-                             && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::<Vec<_>>())
++use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which just call another function where
 +    /// the function can be called directly. `unsafe` functions or calls where types
 +    /// get adjusted are ignored.
 +    ///
 +    /// ### Why is this bad?
 +    /// Needlessly creating a closure adds code for no benefit
 +    /// and gives the optimizer more work.
 +    ///
 +    /// ### Known problems
 +    /// If creating the closure inside the closure has a side-
 +    /// effect then moving the closure creation out will change when that side-
 +    /// effect runs.
 +    /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// xs.map(|x| foo(x))
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // where `foo(_)` is a plain function that takes the exact argument type of `x`.
 +    /// xs.map(foo)
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_CLOSURE,
 +    style,
 +    "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which only invoke a method on the closure
 +    /// argument and can be replaced by referencing the method directly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's unnecessary to create the closure.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// Some('a').map(|s| s.to_uppercase());
 +    /// ```
 +    /// may be rewritten as
 +    /// ```rust,ignore
 +    /// Some('a').map(char::to_uppercase);
 +    /// ```
 +    #[clippy::version = "1.35.0"]
 +    pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    pedantic,
 +    "redundant closures for method calls"
 +}
 +
 +declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EtaReduction {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +        let body = match expr.kind {
 +            ExprKind::Closure(&Closure { body, .. }) => cx.tcx.hir().body(body),
 +            _ => return,
 +        };
 +        if body.value.span.from_expansion() {
 +            if body.params.is_empty() {
 +                if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) {
 +                    // replace `|| vec![]` with `Vec::new`
 +                    span_lint_and_sugg(
 +                        cx,
 +                        REDUNDANT_CLOSURE,
 +                        expr.span,
 +                        "redundant closure",
 +                        "replace the closure with `Vec::new`",
 +                        "std::vec::Vec::new".into(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +            // skip `foo(|| macro!())`
 +            return;
 +        }
 +
 +        let closure_ty = cx.typeck_results().expr_ty(expr);
 +
 +        if_chain!(
 +            if !is_adjusted(cx, body.value);
 +            if let ExprKind::Call(callee, args) = body.value.kind;
 +            if let ExprKind::Path(_) = callee.kind;
 +            if check_inputs(cx, body.params, None, args);
 +            let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
 +            let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
 +                .map_or(callee_ty, |id| cx.tcx.type_of(id));
 +            if check_sig(cx, closure_ty, call_ty);
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            // This fixes some false positives that I don't entirely understand
 +            if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
 +            // A type param function ref like `T::f` is not 'static, however
 +            // it is if cast like `T::f as fn()`. This seems like a rustc bug.
 +            if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
 +            let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
 +            if let ty::Closure(_, substs) = *closure_ty.kind();
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
 +                    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()
-                     let name = get_ufcs_type_name(cx, method_def_id);
++                            && 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.
 +                                snippet = format!("&mut {snippet}");
 +                        }
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            "replace the closure with the function itself",
 +                            snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                });
 +            }
 +        );
 +
 +        if_chain!(
 +            if !is_adjusted(cx, body.value);
 +            if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind;
 +            if check_inputs(cx, body.params, Some(receiver), args);
 +            let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
 +            let substs = cx.typeck_results().node_substs(body.value.hir_id);
 +            let call_ty = cx.tcx.bound_type_of(method_def_id).subst(cx.tcx, substs);
 +            if check_sig(cx, closure_ty, call_ty);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
- fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
++                    let name = get_ufcs_type_name(cx, method_def_id, substs);
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "replace the closure with the method itself",
 +                        format!("{name}::{}", path.ident.name),
 +                        Applicability::MachineApplicable,
 +                    );
 +                })
 +            }
 +        );
 +    }
 +}
 +
 +fn check_inputs(
 +    cx: &LateContext<'_>,
 +    params: &[Param<'_>],
 +    receiver: Option<&Expr<'_>>,
 +    call_args: &[Expr<'_>],
 +) -> bool {
 +    if receiver.map_or(params.len() != call_args.len(), |_| params.len() != call_args.len() + 1) {
 +        return false;
 +    }
 +    let binding_modes = cx.typeck_results().pat_binding_modes();
 +    let check_inputs = |param: &Param<'_>, arg| {
 +        match param.pat.kind {
 +            PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
 +            _ => return false,
 +        }
 +        // checks that parameters are not bound as `ref` or `ref mut`
 +        if let Some(BindingMode::BindByReference(_)) = binding_modes.get(param.pat.hir_id) {
 +            return false;
 +        }
 +
 +        match *cx.typeck_results().expr_adjustments(arg) {
 +            [] => true,
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
 +                    ..
 +                },
 +            ] => {
 +                // re-borrow with the same mutability is allowed
 +                let ty = cx.typeck_results().expr_ty(arg);
 +                matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
 +            },
 +            _ => false,
 +        }
 +    };
 +    std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg))
 +}
 +
 +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
 +    let call_sig = call_ty.fn_sig(cx.tcx);
 +    if call_sig.unsafety() == Unsafety::Unsafe {
 +        return false;
 +    }
 +    if !closure_ty.has_late_bound_regions() {
 +        return true;
 +    }
 +    let ty::Closure(_, substs) = closure_ty.kind() else {
 +        return false;
 +    };
 +    let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
 +    cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 +}
 +
++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 {
 +        ty::TraitContainer => cx.tcx.def_path_str(def_id),
 +        ty::ImplContainer => {
 +            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(),
 +            }
 +        },
 +    }
 +}
index 407dd1b39575f4d63596ea341c8be18a2d7917f1,0000000000000000000000000000000000000000..9c8b0d076dfd78ff353e09f68c6c437bf2d2eadb
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,59 @@@
-     /// `exit()`  terminates the program and doesn't provide a
-     /// stack trace.
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{is_entrypoint_fn, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// Ideally a program is terminated by finishing
++    /// Detects calls to the `exit()` function which terminates the program.
 +    ///
 +    /// ### Why is this bad?
-     /// ```ignore
++    /// 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
-     "`std::process::exit` is called, terminating the program"
++    /// ```
 +    /// 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,
++    "detects `std::process::exit` calls"
 +}
 +
 +declare_lint_pass!(Exit => [EXIT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Exit {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(path_expr, _args) = e.kind;
 +            if let ExprKind::Path(ref path) = path_expr.kind;
 +            if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id();
 +            if match_def_path(cx, def_id, &paths::EXIT);
 +            let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id;
 +            if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent);
 +            // If the next item up is a function we check if it is an entry point
 +            // and only then emit a linter warning
 +            if !is_entrypoint_fn(cx, parent.to_def_id());
 +            then {
 +                span_lint(cx, EXIT, e.span, "usage of `process::exit`");
 +            }
 +        }
 +    }
 +}
index f0fe845d330373ba036d55d70b6081047a3043ab,0000000000000000000000000000000000000000..111b624f52994d747fc327b41830a4cbbba11ff8
mode 100644,000000..100644
--- /dev/null
@@@ -1,431 -1,0 +1,461 @@@
- use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
- use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs};
++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 rustc_errors::Applicability;
 +use if_chain::if_chain;
 +use itertools::Itertools;
- use rustc_semver::RustcVersion;
++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;
-     /// ### Known Problems
-     ///
-     /// There may be a false positive if the format string is expanded from certain proc macros:
-     ///
-     /// ```ignore
-     /// println!(indoc!("{}"), var);
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::DefId;
 +use rustc_span::edition::Edition::Edition2021;
 +use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `format!` within the arguments of another macro that does
 +    /// formatting such as `format!` itself, `write!` or `println!`. Suggests
 +    /// inlining the `format!` call.
 +    ///
 +    /// ### Why is this bad?
 +    /// The recommended code is both shorter and avoids a temporary allocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: {}", format!("something failed at {}", Location::caller()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FORMAT_IN_FORMAT_ARGS,
 +    perf,
 +    "`format!` used in a macro that does formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
 +    /// applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)
 +    /// in a macro that does formatting.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since the type implements `Display`, the use of `to_string` is
 +    /// unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller().to_string());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TO_STRING_IN_FORMAT_ARGS,
 +    perf,
 +    "`to_string` applied to a type that implements `Display` in format args"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detect when a variable is not inlined in a format string,
 +    /// and suggests to inline it.
 +    ///
 +    /// ### Why is this bad?
 +    /// Non-inlined code is slightly more difficult to read and understand,
 +    /// as it requires arguments to be matched against the format string.
 +    /// The inlined syntax, where allowed, is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let var = 42;
 +    /// # let width = 1;
 +    /// # let prec = 2;
 +    /// format!("{}", var);
 +    /// format!("{v:?}", v = var);
 +    /// format!("{0} {0}", var);
 +    /// format!("{0:1$}", var, width);
 +    /// format!("{:.*}", prec, var);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let var = 42;
 +    /// # let width = 1;
 +    /// # let prec = 2;
 +    /// format!("{var}");
 +    /// format!("{var:?}");
 +    /// format!("{var} {var}");
 +    /// format!("{var:width$}");
 +    /// format!("{var:.prec$}");
 +    /// ```
 +    ///
-     pedantic,
++    /// 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,
-     msrv: Option<RustcVersion>,
++    style,
 +    "using non-inlined variables in `format!` calls"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects [formatting parameters] that have no effect on the output of
 +    /// `format!()`, `println!()` or similar macros.
 +    ///
 +    /// ### Why is this bad?
 +    /// Shorter format specifiers are easier to read, it may also indicate that
 +    /// an expected formatting operation such as adding padding isn't happening.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("{:.}", 1.0);
 +    ///
 +    /// println!("not padded: {:5}", format_args!("..."));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// println!("{}", 1.0);
 +    ///
 +    /// println!("not padded: {}", format_args!("..."));
 +    /// // OR
 +    /// println!("padded: {:5}", format!("..."));
 +    /// ```
 +    ///
 +    /// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
 +    #[clippy::version = "1.66.0"]
 +    pub UNUSED_FORMAT_SPECS,
 +    complexity,
 +    "use of a format specifier that has no effect"
 +}
 +
 +impl_lint_pass!(FormatArgs => [
 +    FORMAT_IN_FORMAT_ARGS,
 +    TO_STRING_IN_FORMAT_ARGS,
 +    UNINLINED_FORMAT_ARGS,
 +    UNUSED_FORMAT_SPECS,
 +]);
 +
 +pub struct FormatArgs {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
-         Self { msrv }
++    msrv: Msrv,
++    ignore_mixed: bool,
 +}
 +
 +impl FormatArgs {
 +    #[must_use]
-             if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) {
-                 check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id);
++    pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
++        Self {
++            msrv,
++            ignore_mixed: allow_mixed_uninlined_format_args,
++        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for FormatArgs {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if let Some(format_args) = FormatArgsExpn::parse(cx, expr)
 +            && let expr_expn_data = expr.span.ctxt().outer_expn_data()
 +            && let outermost_expn_data = outermost_expn_data(expr_expn_data)
 +            && let Some(macro_def_id) = outermost_expn_data.macro_def_id
 +            && is_format_macro(cx, macro_def_id)
 +            && let ExpnKind::Macro(_, name) = outermost_expn_data.kind
 +        {
 +            for arg in &format_args.args {
 +                check_unused_format_specifier(cx, arg);
 +                if !arg.format.is_default() {
 +                    continue;
 +                }
 +                if is_aliased(&format_args, arg.param.value.hir_id) {
 +                    continue;
 +                }
 +                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);
 +            }
- fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) {
++            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);
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) {
 +    let param_ty = cx.typeck_results().expr_ty(arg.param.value).peel_refs();
 +
 +    if let Count::Implied(Some(mut span)) = arg.format.precision
 +        && !span.is_empty()
 +    {
 +        span_lint_and_then(
 +            cx,
 +            UNUSED_FORMAT_SPECS,
 +            span,
 +            "empty precision specifier has no effect",
 +            |diag| {
 +                if param_ty.is_floating_point() {
 +                    diag.note("a precision specifier is not required to format floats");
 +                }
 +
 +                if arg.format.is_default() {
 +                    // If there's no other specifiers remove the `:` too
 +                    span = arg.format_span();
 +                }
 +
 +                diag.span_suggestion_verbose(span, "remove the `.`", "", Applicability::MachineApplicable);
 +            },
 +        );
 +    }
 +
 +    if is_type_diagnostic_item(cx, param_ty, sym::Arguments) && !arg.format.is_default_for_trait() {
 +        span_lint_and_then(
 +            cx,
 +            UNUSED_FORMAT_SPECS,
 +            arg.span,
 +            "format specifiers have no effect on `format_args!()`",
 +            |diag| {
 +                let mut suggest_format = |spec, span| {
 +                    let message = format!("for the {spec} to apply consider using `format!()`");
 +
 +                    if let Some(mac_call) = root_macro_call(arg.param.value.span)
 +                        && cx.tcx.is_diagnostic_item(sym::format_args_macro, mac_call.def_id)
 +                        && arg.span.eq_ctxt(mac_call.span)
 +                    {
 +                        diag.span_suggestion(
 +                            cx.sess().source_map().span_until_char(mac_call.span, '!'),
 +                            message,
 +                            "format",
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                    } else if let Some(span) = span {
 +                        diag.span_help(span, message);
 +                    }
 +                };
 +
 +                if !arg.format.width.is_implied() {
 +                    suggest_format("width", arg.format.width.span());
 +                }
 +
 +                if !arg.format.precision.is_implied() {
 +                    suggest_format("precision", arg.format.precision.span());
 +                }
 +
 +                diag.span_suggestion_verbose(
 +                    arg.format_span(),
 +                    "if the current behavior is intentional, remove the format specifiers",
 +                    "",
 +                    Applicability::MaybeIncorrect,
 +                );
 +            },
 +        );
 +    }
 +}
 +
-     if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() {
++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;
 +    }
 +    if call_site.edition() < Edition2021 && is_panic(cx, def_id) {
 +        // panic! before 2021 edition considers a single string argument as non-format
 +        return;
 +    }
 +
 +    let mut fixes = Vec::new();
 +    // If any of the arguments are referenced by an index number,
 +    // and that argument is not a simple variable and cannot be inlined,
 +    // 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)
-     // 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;
-     }
++    if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() {
 +        return;
 +    }
 +
-             diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable);
++    // 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,
 +        UNINLINED_FORMAT_ARGS,
 +        call_site,
 +        "variables can be used directly in the `format!` string",
 +        |diag| {
- fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool {
++            diag.multipart_suggestion_with_style(
++                "change this to",
++                fixes,
++                Applicability::MachineApplicable,
++                if multiline_fix { CompletelyHidden } else { ShowCode },
++            );
 +        },
 +    );
 +}
 +
-         // if we can't inline a numbered argument, we can't continue
-         param.kind != Numbered
++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
 +        && let Some(arg_span) = args.value_with_prev_comma_span(param.value.hir_id)
 +    {
 +        let replacement = match param.usage {
 +            FormatParamUsage::Argument => segment.ident.name.to_string(),
 +            FormatParamUsage::Width => format!("{}$", segment.ident.name),
 +            FormatParamUsage::Precision => format!(".{}$", segment.ident.name),
 +        };
 +        fixes.push((param.span, replacement));
 +        fixes.push((arg_span, String::new()));
 +        true  // successful inlining, continue checking
 +    } else {
++        // Do not continue inlining (return false) in case
++        // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)`
++        // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already
++        param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_)))
 +    }
 +}
 +
 +fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
 +    if expn_data.call_site.from_expansion() {
 +        outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
 +    } else {
 +        expn_data
 +    }
 +}
 +
 +fn check_format_in_format_args(
 +    cx: &LateContext<'_>,
 +    call_site: Span,
 +    name: Symbol,
 +    arg: &Expr<'_>,
 +) {
 +    let expn_data = arg.span.ctxt().outer_expn_data();
 +    if expn_data.call_site.from_expansion() {
 +        return;
 +    }
 +    let Some(mac_id) = expn_data.macro_def_id else { return };
 +    if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
 +        return;
 +    }
 +    span_lint_and_then(
 +        cx,
 +        FORMAT_IN_FORMAT_ARGS,
 +        call_site,
 +        &format!("`format!` in `{name}!` args"),
 +        |diag| {
 +            diag.help(&format!(
 +                "combine the `format!(..)` arguments with the outer `{name}!(..)` call"
 +            ));
 +            diag.help("or consider changing `format!` to `format_args!`");
 +        },
 +    );
 +}
 +
 +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
 +    if_chain! {
 +        if !value.span.from_expansion();
 +        if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id);
 +        if is_diag_trait_item(cx, method_def_id, sym::ToString);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
 +        let (n_needed_derefs, target) =
 +            count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
 +        if implements_trait(cx, target, display_trait_id, &[]);
 +        if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
 +            if n_needed_derefs == 0 && !needs_ref {
 +                span_lint_and_sugg(
 +                    cx,
 +                    TO_STRING_IN_FORMAT_ARGS,
 +                    to_string_span.with_lo(receiver.span.hi()),
 +                    &format!(
 +                        "`to_string` applied to a type that implements `Display` in `{name}!` args"
 +                    ),
 +                    "remove this",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            } else {
 +                span_lint_and_sugg(
 +                    cx,
 +                    TO_STRING_IN_FORMAT_ARGS,
 +                    value.span,
 +                    &format!(
 +                        "`to_string` applied to a type that implements `Display` in `{name}!` args"
 +                    ),
 +                    "use this",
 +                    format!(
 +                        "{}{:*>n_needed_derefs$}{receiver_snippet}",
 +                        if needs_ref { "&" } else { "" },
 +                        ""
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Returns true if `hir_id` is referred to by multiple format params
 +fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
 +    args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
 +}
 +
 +fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
 +where
 +    I: Iterator<Item = &'tcx Adjustment<'tcx>>,
 +{
 +    let mut n_total = 0;
 +    let mut n_needed = 0;
 +    loop {
 +        if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
 +            n_total += 1;
 +            if overloaded_deref.is_some() {
 +                n_needed = n_total;
 +            }
 +            ty = *target;
 +        } else {
 +            return (n_needed, ty);
 +        }
 +    }
 +}
index 8b24a4962fb296ca4366c36e933206b987d1fa25,0000000000000000000000000000000000000000..8621504c1b47776ef41b3128a9144831bce3bde5
mode 100644,000000..100644
--- /dev/null
@@@ -1,213 -1,0 +1,213 @@@
- use clippy_utils::{meets_msrv, msrvs, path_def_id};
 +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 rustc_semver::RustcVersion;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_path, Visitor};
 +use rustc_hir::{
 +    GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty,
 +    TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter::OnlyBodies;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{kw, sym};
 +use rustc_span::{Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Searches for implementations of the `Into<..>` trait and suggests to implement `From<..>` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// According the std docs implementing `From<..>` is preferred since it gives you `Into<..>` for free where the reverse isn't true.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct StringWrapper(String);
 +    ///
 +    /// impl Into<StringWrapper> for String {
 +    ///     fn into(self) -> StringWrapper {
 +    ///         StringWrapper(self)
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct StringWrapper(String);
 +    ///
 +    /// impl From<String> for StringWrapper {
 +    ///     fn from(s: String) -> StringWrapper {
 +    ///         StringWrapper(s)
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub FROM_OVER_INTO,
 +    style,
 +    "Warns on implementations of `Into<..>` to use `From<..>`"
 +}
 +
 +pub struct FromOverInto {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl FromOverInto {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
++    pub fn new(msrv: Msrv) -> Self {
 +        FromOverInto { msrv }
 +    }
 +}
 +
 +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 !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) {
 +            return;
 +        }
 +
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(hir_trait_ref),
 +            self_ty,
 +            items: [impl_item_ref],
 +            ..
 +        }) = item.kind
 +            && let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
 +            // `impl Into<target_ty> for self_ty`
 +            && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
 +            && let Some(middle_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
 +            && cx.tcx.is_diagnostic_item(sym::Into, middle_trait_ref.def_id)
 +        {
 +            span_lint_and_then(
 +                cx,
 +                FROM_OVER_INTO,
 +                cx.tcx.sess.source_map().guess_head_span(item.span),
 +                "an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true",
 +                |diag| {
 +                    // If the target type is likely foreign mention the orphan rules as it's a common source of confusion
 +                    if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) {
 +                        diag.help(
 +                            "`impl From<Local> for Foreign` is allowed by the orphan rules, for more information see\n\
 +                            https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence"
 +                        );
 +                    }
 +
 +                    let message = format!("replace the `Into` implentation with `From<{}>`", middle_trait_ref.self_ty());
 +                    if let Some(suggestions) = convert_to_from(cx, into_trait_seg, target_ty, self_ty, impl_item_ref) {
 +                        diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
 +                    } else {
 +                        diag.help(message);
 +                    }
 +                },
 +            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Finds the occurences of `Self` and `self`
 +struct SelfFinder<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    /// Occurences of `Self`
 +    upper: Vec<Span>,
 +    /// Occurences of `self`
 +    lower: Vec<Span>,
 +    /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding
 +    /// already named `val`
 +    invalid: bool,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for SelfFinder<'a, 'tcx> {
 +    type NestedFilter = OnlyBodies;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
 +        for segment in path.segments {
 +            match segment.ident.name {
 +                kw::SelfLower => self.lower.push(segment.ident.span),
 +                kw::SelfUpper => self.upper.push(segment.ident.span),
 +                _ => continue,
 +            }
 +        }
 +
 +        self.invalid |= path.span.from_expansion();
 +        if !self.invalid {
 +            walk_path(self, path);
 +        }
 +    }
 +
 +    fn visit_name(&mut self, name: Symbol) {
 +        if name == sym::val {
 +            self.invalid = true;
 +        }
 +    }
 +}
 +
 +fn convert_to_from(
 +    cx: &LateContext<'_>,
 +    into_trait_seg: &PathSegment<'_>,
 +    target_ty: &Ty<'_>,
 +    self_ty: &Ty<'_>,
 +    impl_item_ref: &ImplItemRef,
 +) -> Option<Vec<(Span, String)>> {
 +    let impl_item = cx.tcx.hir().impl_item(impl_item_ref.id);
 +    let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else { return None };
 +    let body = cx.tcx.hir().body(body_id);
 +    let [input] = body.params else { return None };
 +    let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None };
 +
 +    let from = snippet_opt(cx, self_ty.span)?;
 +    let into = snippet_opt(cx, target_ty.span)?;
 +
 +    let mut suggestions = vec![
 +        // impl Into<T> for U  ->  impl From<T> for U
 +        //      ~~~~                    ~~~~
 +        (into_trait_seg.ident.span, String::from("From")),
 +        // impl Into<T> for U  ->  impl Into<U> for U
 +        //           ~                       ~
 +        (target_ty.span, from.clone()),
 +        // impl Into<T> for U  ->  impl Into<T> for T
 +        //                  ~                       ~
 +        (self_ty.span, into),
 +        // fn into(self) -> T  ->  fn from(self) -> T
 +        //    ~~~~                    ~~~~
 +        (impl_item.ident.span, String::from("from")),
 +        // fn into([mut] self) -> T  ->  fn into([mut] v: T) -> T
 +        //               ~~~~                          ~~~~
 +        (self_ident.span, format!("val: {from}")),
 +        // fn into(self) -> T  ->  fn into(self) -> Self
 +        //                  ~                       ~~~~
 +        (sig.decl.output.span(), String::from("Self")),
 +    ];
 +
 +    let mut finder = SelfFinder {
 +        cx,
 +        upper: Vec::new(),
 +        lower: Vec::new(),
 +        invalid: false,
 +    };
 +    finder.visit_expr(body.value);
 +
 +    if finder.invalid {
 +        return None;
 +    }
 +
 +    // don't try to replace e.g. `Self::default()` with `&[T]::default()`
 +    if !finder.upper.is_empty() && !matches!(self_ty.kind, TyKind::Path(_)) {
 +        return None;
 +    }
 +
 +    for span in finder.upper {
 +        suggestions.push((span, from.clone()));
 +    }
 +    for span in finder.lower {
 +        suggestions.push((span, String::from("val")));
 +    }
 +
 +    Some(suggestions)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..27acad45ccf729e00e63982d50ead26f92ec6b7b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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);
++            },
++        );
++    }
++}
index ae0e08334463803dbfce17b707bdbbab3fda0773,0000000000000000000000000000000000000000..91e6ffe6447909d610bd14774b269c301c1eb0cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,322 -1,0 +1,367 @@@
++mod misnamed_getters;
 +mod must_use;
 +mod not_unsafe_ptr_arg_deref;
 +mod result;
 +mod too_many_arguments;
 +mod too_many_lines;
 +
 +use rustc_hir as hir;
 +use rustc_hir::intravisit;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions with too many parameters.
 +    ///
 +    /// ### Why is this bad?
 +    /// Functions with lots of parameters are considered bad
 +    /// style and reduce readability (“what does the 5th parameter mean?”). Consider
 +    /// grouping some parameters into a new type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct Color;
 +    /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TOO_MANY_ARGUMENTS,
 +    complexity,
 +    "functions with too many arguments"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions with a large amount of lines.
 +    ///
 +    /// ### Why is this bad?
 +    /// Functions with a lot of lines are harder to understand
 +    /// due to having to look at a larger amount of code to understand what the
 +    /// function is doing. Consider splitting the body of the function into
 +    /// multiple functions.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn im_too_long() {
 +    ///     println!("");
 +    ///     // ... 100 more LoC
 +    ///     println!("");
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub TOO_MANY_LINES,
 +    pedantic,
 +    "functions with too many lines"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that dereference raw pointer
 +    /// arguments but are not marked `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function should probably be marked `unsafe`, since
 +    /// for an arbitrary raw pointer, there is no way of telling for sure if it is
 +    /// valid.
 +    ///
 +    /// ### Known problems
 +    /// * It does not check functions recursively so if the pointer is passed to a
 +    /// private non-`unsafe` function which does the dereferencing, the lint won't
 +    /// trigger.
 +    /// * It only checks for arguments whose type are raw pointers, not raw pointers
 +    /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
 +    /// `some_argument.get_raw_ptr()`).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// pub fn foo(x: *const u8) {
 +    ///     println!("{}", unsafe { *x });
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// pub unsafe fn foo(x: *const u8) {
 +    ///     println!("{}", unsafe { *x });
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NOT_UNSAFE_PTR_ARG_DEREF,
 +    correctness,
 +    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a `#[must_use]` attribute on
 +    /// unit-returning functions and methods.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit values are useless. The attribute is likely
 +    /// a remnant of a refactoring that removed the return type.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// #[must_use]
 +    /// fn useless() { }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub MUST_USE_UNIT,
 +    style,
 +    "`#[must_use]` attribute on a unit-returning function / method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a `#[must_use]` attribute without
 +    /// further information on functions and methods that return a type already
 +    /// marked as `#[must_use]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The attribute isn't needed. Not using the result
 +    /// will already be reported. Alternatively, one can add some text to the
 +    /// attribute to improve the lint message.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// #[must_use]
 +    /// fn double_must_use() -> Result<(), ()> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub DOUBLE_MUST_USE,
 +    style,
 +    "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that have no
 +    /// `#[must_use]` attribute, but return something not already marked
 +    /// must-use, have no mutable arg and mutate no statics.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not bad at all, this lint just shows places where
 +    /// you could add the attribute.
 +    ///
 +    /// ### Known problems
 +    /// The lint only checks the arguments for mutable
 +    /// types without looking if they are actually changed. On the other hand,
 +    /// it also ignores a broad range of potentially interesting side effects,
 +    /// because we cannot decide whether the programmer intends the function to
 +    /// be called for the side effect or the result. Expect many false
 +    /// positives. At least we don't lint if the result type is unit or already
 +    /// `#[must_use]`.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// // this could be annotated with `#[must_use]`.
 +    /// fn id<T>(t: T) -> T { t }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub MUST_USE_CANDIDATE,
 +    pedantic,
 +    "function or method that could take a `#[must_use]` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that return a `Result`
 +    /// with an `Err` type of `()`. It suggests using a custom type that
 +    /// implements `std::error::Error`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit does not implement `Error` and carries no
 +    /// further information about what went wrong.
 +    ///
 +    /// ### Known problems
 +    /// Of course, this lint assumes that `Result` is used
 +    /// for a fallible operation (which is after all the intended use). However
 +    /// code may opt to (mis)use it as a basic two-variant-enum. In that case,
 +    /// the suggestion is misguided, and the code should use a custom enum
 +    /// instead.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// pub fn read_u8() -> Result<u8, ()> { Err(()) }
 +    /// ```
 +    /// should become
 +    /// ```rust,should_panic
 +    /// use std::fmt;
 +    ///
 +    /// #[derive(Debug)]
 +    /// pub struct EndOfStream;
 +    ///
 +    /// impl fmt::Display for EndOfStream {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         write!(f, "End of Stream")
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for EndOfStream { }
 +    ///
 +    /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
 +    ///# fn main() {
 +    ///#     read_u8().unwrap();
 +    ///# }
 +    /// ```
 +    ///
 +    /// Note that there are crates that simplify creating the error type, e.g.
 +    /// [`thiserror`](https://docs.rs/thiserror).
 +    #[clippy::version = "1.49.0"]
 +    pub RESULT_UNIT_ERR,
 +    style,
 +    "public function returning `Result` with an `Err` type of `()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions that return `Result` with an unusually large
 +    /// `Err`-variant.
 +    ///
 +    /// ### Why is this bad?
 +    /// A `Result` is at least as large as the `Err`-variant. While we
 +    /// expect that variant to be seldomly used, the compiler needs to reserve
 +    /// and move that much memory every single time.
 +    ///
 +    /// ### Known problems
 +    /// The size determined by Clippy is platform-dependent.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// pub enum ParseError {
 +    ///     UnparsedBytes([u8; 512]),
 +    ///     UnexpectedEof,
 +    /// }
 +    ///
 +    /// // The `Result` has at least 512 bytes, even in the `Ok`-case
 +    /// pub fn parse() -> Result<(), ParseError> {
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    /// should be
 +    /// ```
 +    /// pub enum ParseError {
 +    ///     UnparsedBytes(Box<[u8; 512]>),
 +    ///     UnexpectedEof,
 +    /// }
 +    ///
 +    /// // The `Result` is slightly larger than a pointer
 +    /// pub fn parse() -> Result<(), ParseError> {
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub RESULT_LARGE_ERR,
 +    perf,
 +    "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,
 +    too_many_lines_threshold: u64,
 +    large_error_threshold: u64,
 +}
 +
 +impl Functions {
 +    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
 +        Self {
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +            large_error_threshold,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Functions => [
 +    TOO_MANY_ARGUMENTS,
 +    TOO_MANY_LINES,
 +    NOT_UNSAFE_PTR_ARG_DEREF,
 +    MUST_USE_UNIT,
 +    DOUBLE_MUST_USE,
 +    MUST_USE_CANDIDATE,
 +    RESULT_UNIT_ERR,
 +    RESULT_LARGE_ERR,
++    MISNAMED_GETTERS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Functions {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: intravisit::FnKind<'tcx>,
 +        decl: &'tcx hir::FnDecl<'_>,
 +        body: &'tcx hir::Body<'_>,
 +        span: Span,
 +        hir_id: hir::HirId,
 +    ) {
 +        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<'_>) {
 +        must_use::check_item(cx, item);
 +        result::check_item(cx, item, self.large_error_threshold);
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        must_use::check_impl_item(cx, item);
 +        result::check_impl_item(cx, item, self.large_error_threshold);
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
 +        not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
 +        must_use::check_trait_item(cx, item);
 +        result::check_trait_item(cx, item, self.large_error_threshold);
 +    }
 +}
index f7e30b051a694a84d6be001e103f067a804d58b7,0000000000000000000000000000000000000000..23da145d038257be75f38c88c8247ac55aaa169a
mode 100644,000000..100644
--- /dev/null
@@@ -1,140 -1,0 +1,142 @@@
-             if variants_size[0].size >= large_err_threshold {
 +use rustc_errors::Diagnostic;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, Adt, Ty};
 +use rustc_span::{sym, Span};
 +
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::trait_ref_of_method;
 +use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo};
 +
 +use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
 +
 +/// The type of the `Err`-variant in a `std::result::Result` returned by the
 +/// given `FnDecl`
 +fn result_err_ty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &hir::FnDecl<'tcx>,
 +    id: hir::def_id::LocalDefId,
 +    item_span: Span,
 +) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
 +    if !in_external_macro(cx.sess(), item_span)
 +        && let hir::FnRetTy::Return(hir_ty) = decl.output
 +        && let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).output())
 +        && is_type_diagnostic_item(cx, ty, sym::Result)
 +        && let ty::Adt(_, substs) = ty.kind()
 +    {
 +        let err_ty = substs.type_at(1);
 +        Some((hir_ty, err_ty))
 +    } else {
 +        None
 +    }
 +}
 +
 +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
 +    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
 +        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span)
 +    {
 +        if cx.effective_visibilities.is_exported(item.owner_id.def_id) {
 +            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +            check_result_unit_err(cx, err_ty, fn_header_span);
 +        }
 +        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
 +    }
 +}
 +
 +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
 +    // Don't lint if method is a trait's implementation, we can't do anything about those
 +    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
 +        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span)
 +        && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
 +    {
 +        if cx.effective_visibilities.is_exported(item.owner_id.def_id) {
 +            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +            check_result_unit_err(cx, err_ty, fn_header_span);
 +        }
 +        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
 +    }
 +}
 +
 +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
 +    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.owner_id.def_id, item.span) {
 +            if cx.effective_visibilities.is_exported(item.owner_id.def_id) {
 +                check_result_unit_err(cx, err_ty, fn_header_span);
 +            }
 +            check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
 +        }
 +    }
 +}
 +
 +fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
 +    if err_ty.is_unit() {
 +        span_lint_and_help(
 +            cx,
 +            RESULT_UNIT_ERR,
 +            fn_header_span,
 +            "this returns a `Result<_, ()>`",
 +            None,
 +            "use a custom `Error` type instead",
 +        );
 +    }
 +}
 +
 +fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
 +    if_chain! {
 +        if let Adt(adt, subst) = err_ty.kind();
 +        if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local();
 +        if let Some(hir::Node::Item(item)) = cx
 +            .tcx
 +            .hir()
 +            .find_by_def_id(local_def_id);
 +        if let hir::ItemKind::Enum(ref def, _) = item.kind;
 +        then {
 +            let variants_size = AdtVariantInfo::new(cx, *adt, subst);
-                             def.variants[variants_size[0].ind].span,
++            if let Some((first_variant, variants)) = variants_size.split_first()
++                && first_variant.size >= large_err_threshold
++            {
 +                span_lint_and_then(
 +                    cx,
 +                    RESULT_LARGE_ERR,
 +                    hir_ty_span,
 +                    "the `Err`-variant returned from this function is very large",
 +                    |diag| {
 +                        diag.span_label(
-                         for variant in &variants_size[1..] {
++                            def.variants[first_variant.ind].span,
 +                            format!("the largest variant contains at least {} bytes", variants_size[0].size),
 +                        );
 +
++                        for variant in variants {
 +                            if variant.size >= large_err_threshold {
 +                                let variant_def = &def.variants[variant.ind];
 +                                diag.span_label(
 +                                    variant_def.span,
 +                                    format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size),
 +                                );
 +                            }
 +                        }
 +
 +                        diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
 +                    }
 +                );
 +            }
 +        }
 +        else {
 +            let ty_size = approx_ty_size(cx, err_ty);
 +            if ty_size >= large_err_threshold {
 +                span_lint_and_then(
 +                    cx,
 +                    RESULT_LARGE_ERR,
 +                    hir_ty_span,
 +                    "the `Err`-variant returned from this function is very large",
 +                    |diag: &mut Diagnostic| {
 +                        diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
 +                        diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
index a9425a40f885ea192bf50e263c900b44d09d77b4,0000000000000000000000000000000000000000..61934a9142633003f805f421ea4f9855451e76f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,110 @@@
-                                 if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::return_ty;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{Body, FnDecl, HirId};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{Clause, EarlyBinder, Opaque, PredicateKind};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt;
 +use rustc_trait_selection::traits::{self, FulfillmentError};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint requires Future implementations returned from
 +    /// functions and methods to implement the `Send` marker trait. It is mostly
 +    /// used by library authors (public and internal) that target an audience where
 +    /// multithreaded executors are likely to be used for running these Futures.
 +    ///
 +    /// ### Why is this bad?
 +    /// A Future implementation captures some state that it
 +    /// needs to eventually produce its final value. When targeting a multithreaded
 +    /// executor (which is the norm on non-embedded devices) this means that this
 +    /// state may need to be transported to other threads, in other words the
 +    /// whole Future needs to implement the `Send` marker trait. If it does not,
 +    /// then the resulting Future cannot be submitted to a thread pool in the
 +    /// end user’s code.
 +    ///
 +    /// Especially for generic functions it can be confusing to leave the
 +    /// discovery of this problem to the end user: the reported error location
 +    /// will be far from its cause and can in many cases not even be fixed without
 +    /// modifying the library where the offending Future implementation is
 +    /// produced.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// async fn not_send(bytes: std::rc::Rc<[u8]>) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// async fn is_send(bytes: std::sync::Arc<[u8]>) {}
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub FUTURE_NOT_SEND,
 +    nursery,
 +    "public Futures must be Send"
 +}
 +
 +declare_lint_pass!(FutureNotSend => [FUTURE_NOT_SEND]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'tcx>,
 +        _: &'tcx Body<'tcx>,
 +        _: Span,
 +        hir_id: HirId,
 +    ) {
 +        if let FnKind::Closure = kind {
 +            return;
 +        }
 +        let ret_ty = return_ty(cx, hir_id);
 +        if let Opaque(id, subst) = *ret_ty.kind() {
 +            let preds = cx.tcx.explicit_item_bounds(id);
 +            let mut is_future = false;
 +            for &(p, _span) in preds {
 +                let p = EarlyBinder(p).subst(cx.tcx, subst);
 +                if let Some(trait_pred) = p.to_opt_poly_trait_pred() {
 +                    if Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait() {
 +                        is_future = true;
 +                        break;
 +                    }
 +                }
 +            }
 +            if is_future {
 +                let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
 +                let span = decl.output.span();
 +                let infcx = cx.tcx.infer_ctxt().build();
 +                let cause = traits::ObligationCause::misc(span, hir_id);
 +                let send_errors = traits::fully_solve_bound(&infcx, cause, cx.param_env, ret_ty, send_trait);
 +                if !send_errors.is_empty() {
 +                    span_lint_and_then(
 +                        cx,
 +                        FUTURE_NOT_SEND,
 +                        span,
 +                        "future cannot be sent between threads safely",
 +                        |db| {
 +                            for FulfillmentError { obligation, .. } in send_errors {
 +                                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()
++                                {
 +                                    db.note(&format!(
 +                                        "`{}` doesn't implement `{}`",
 +                                        trait_pred.self_ty(),
 +                                        trait_pred.trait_ref.print_only_trait_path(),
 +                                    ));
 +                                }
 +                            }
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 0d6718c168a5c9da12865ee7633dc5078ad7e33a,0000000000000000000000000000000000000000..9cadaaa493e465c0b4032f6adec143c752eb5681
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,125 @@@
- use clippy_utils::{
-     contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks,
- };
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
 +    /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
 +    /// in comparison to `bool::then`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let v = vec![0];
 +    /// let a = if v.is_empty() {
 +    ///     println!("true!");
 +    ///     Some(42)
 +    /// } else {
 +    ///     None
 +    /// };
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// # let v = vec![0];
 +    /// let a = v.is_empty().then(|| {
 +    ///     println!("true!");
 +    ///     42
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub IF_THEN_SOME_ELSE_NONE,
 +    restriction,
 +    "Finds if-else that could be written using either `bool::then` or `bool::then_some`"
 +}
 +
 +pub struct IfThenSomeElseNone {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl IfThenSomeElseNone {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +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>) {
-             let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
++        if !self.msrv.meets(msrvs::BOOL_THEN) {
 +            return;
 +        }
 +
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        // We only care about the top-most `if` in the chain
 +        if is_else_clause(cx.tcx, expr) {
 +            return;
 +        }
 +
 +        if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
 +            && let ExprKind::Block(then_block, _) = then.kind
 +            && let Some(then_expr) = then_block.expr
 +            && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
 +            && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
 +            && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
 +            && !stmts_contains_early_return(then_block.stmts)
 +        {
 +            let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
 +            let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
 +                format!("({cond_snip})")
 +            } else {
 +                cond_snip.into_owned()
 +            };
 +            let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
 +            let mut method_body = if then_block.stmts.is_empty() {
 +                arg_snip.into_owned()
 +            } else {
 +                format!("{{ /* snippet */ {arg_snip} }}")
 +            };
++            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, "|| ");
 +                "then"
 +            };
 +
 +            let help = format!(
 +                "consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",
 +            );
 +            span_lint_and_help(
 +                cx,
 +                IF_THEN_SOME_ELSE_NONE,
 +                expr.span,
 +                &format!("this could be simplified with `bool::{method_name}`"),
 +                None,
 +                &help,
 +            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
 +    stmts.iter().any(|stmt| {
 +        let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false };
 +
 +        contains_return(e)
 +    })
 +}
index 0d5099bde6de015b5aa069e5bdeb6611afd9d471,0000000000000000000000000000000000000000..cf35b1f175c602fd4fa70e677c1b202233968811
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,276 @@@
- use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local};
 +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 rustc_semver::RustcVersion;
++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;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty;
-     nursery,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::Ident, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// The lint checks for slice bindings in patterns that are only used to
 +    /// access individual slice values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Accessing slice values using indices can lead to panics. Using refutable
 +    /// patterns can avoid these. Binding to individual values also improves the
 +    /// readability as they can be named.
 +    ///
 +    /// ### Limitations
 +    /// This lint currently only checks for immutable access inside `if let`
 +    /// patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
 +    ///
 +    /// if let Some(slice) = slice {
 +    ///     println!("{}", slice[0]);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let slice: Option<&[u32]> = Some(&[1, 2, 3]);
 +    ///
 +    /// if let Some(&[first, ..]) = slice {
 +    ///     println!("{}", first);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub INDEX_REFUTABLE_SLICE,
- #[derive(Copy, Clone)]
++    pedantic,
 +    "avoid indexing on slices which could be destructed"
 +}
 +
-     msrv: Option<RustcVersion>,
 +pub struct IndexRefutableSlice {
 +    max_suggested_slice: u64,
-     pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl IndexRefutableSlice {
-             if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS);
++    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self {
 +        Self {
 +            max_suggested_slice: max_suggested_slice_pattern_length,
 +            msrv,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if_chain! {
 +            if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some();
 +            if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr);
 +            if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id);
++            if self.msrv.meets(msrvs::SLICE_PATTERNS);
 +
 +            let found_slices = find_slice_values(cx, let_pat);
 +            if !found_slices.is_empty();
 +            let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then);
 +            if !filtered_slices.is_empty();
 +            then {
 +                for slice in filtered_slices.values() {
 +                    lint_slice(cx, slice);
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn find_slice_values(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> FxIndexMap<hir::HirId, SliceLintInformation> {
 +    let mut removed_pat: FxHashSet<hir::HirId> = FxHashSet::default();
 +    let mut slices: FxIndexMap<hir::HirId, SliceLintInformation> = FxIndexMap::default();
 +    pat.walk_always(|pat| {
 +        // We'll just ignore mut and ref mut for simplicity sake right now
 +        if let hir::PatKind::Binding(
 +            hir::BindingAnnotation(by_ref, hir::Mutability::Not),
 +            value_hir_id,
 +            ident,
 +            sub_pat,
 +        ) = pat.kind
 +        {
 +            // This block catches bindings with sub patterns. It would be hard to build a correct suggestion
 +            // for them and it's likely that the user knows what they are doing in such a case.
 +            if removed_pat.contains(&value_hir_id) {
 +                return;
 +            }
 +            if sub_pat.is_some() {
 +                removed_pat.insert(value_hir_id);
 +                slices.remove(&value_hir_id);
 +                return;
 +            }
 +
 +            let bound_ty = cx.typeck_results().node_type(pat.hir_id);
 +            if let ty::Slice(inner_ty) | ty::Array(inner_ty, _) = bound_ty.peel_refs().kind() {
 +                // The values need to use the `ref` keyword if they can't be copied.
 +                // This will need to be adjusted if the lint want to support mutable access in the future
 +                let src_is_ref = bound_ty.is_ref() && by_ref != hir::ByRef::Yes;
 +                let needs_ref = !(src_is_ref || is_copy(cx, *inner_ty));
 +
 +                let slice_info = slices
 +                    .entry(value_hir_id)
 +                    .or_insert_with(|| SliceLintInformation::new(ident, needs_ref));
 +                slice_info.pattern_spans.push(pat.span);
 +            }
 +        }
 +    });
 +
 +    slices
 +}
 +
 +fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
 +    let used_indices = slice
 +        .index_use
 +        .iter()
 +        .map(|(index, _)| *index)
 +        .collect::<FxHashSet<_>>();
 +
 +    let value_name = |index| format!("{}_{index}", slice.ident.name);
 +
 +    if let Some(max_index) = used_indices.iter().max() {
 +        let opt_ref = if slice.needs_ref { "ref " } else { "" };
 +        let pat_sugg_idents = (0..=*max_index)
 +            .map(|index| {
 +                if used_indices.contains(&index) {
 +                    format!("{opt_ref}{}", value_name(index))
 +                } else {
 +                    "_".to_string()
 +                }
 +            })
 +            .collect::<Vec<_>>();
 +        let pat_sugg = format!("[{}, ..]", pat_sugg_idents.join(", "));
 +
 +        span_lint_and_then(
 +            cx,
 +            INDEX_REFUTABLE_SLICE,
 +            slice.ident.span,
 +            "this binding can be a slice pattern to avoid indexing",
 +            |diag| {
 +                diag.multipart_suggestion(
 +                    "try using a slice pattern here",
 +                    slice
 +                        .pattern_spans
 +                        .iter()
 +                        .map(|span| (*span, pat_sugg.clone()))
 +                        .collect(),
 +                    Applicability::MaybeIncorrect,
 +                );
 +
 +                diag.multipart_suggestion(
 +                    "and replace the index expressions here",
 +                    slice
 +                        .index_use
 +                        .iter()
 +                        .map(|(index, span)| (*span, value_name(*index)))
 +                        .collect(),
 +                    Applicability::MaybeIncorrect,
 +                );
 +
 +                // The lint message doesn't contain a warning about the removed index expression,
 +                // since `filter_lintable_slices` will only return slices where all access indices
 +                // are known at compile time. Therefore, they can be removed without side effects.
 +            },
 +        );
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct SliceLintInformation {
 +    ident: Ident,
 +    needs_ref: bool,
 +    pattern_spans: Vec<Span>,
 +    index_use: Vec<(u64, Span)>,
 +}
 +
 +impl SliceLintInformation {
 +    fn new(ident: Ident, needs_ref: bool) -> Self {
 +        Self {
 +            ident,
 +            needs_ref,
 +            pattern_spans: Vec::new(),
 +            index_use: Vec::new(),
 +        }
 +    }
 +}
 +
 +fn filter_lintable_slices<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>,
 +    max_suggested_slice: u64,
 +    scope: &'tcx hir::Expr<'tcx>,
 +) -> FxIndexMap<hir::HirId, SliceLintInformation> {
 +    let mut visitor = SliceIndexLintingVisitor {
 +        cx,
 +        slice_lint_info,
 +        max_suggested_slice,
 +    };
 +
 +    intravisit::walk_expr(&mut visitor, scope);
 +
 +    visitor.slice_lint_info
 +}
 +
 +struct SliceIndexLintingVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    slice_lint_info: FxIndexMap<hir::HirId, SliceLintInformation>,
 +    max_suggested_slice: u64,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +        if let Some(local_id) = path_to_local(expr) {
 +            let Self {
 +                cx,
 +                ref mut slice_lint_info,
 +                max_suggested_slice,
 +            } = *self;
 +
 +            if_chain! {
 +                // Check if this is even a local we're interested in
 +                if let Some(use_info) = slice_lint_info.get_mut(&local_id);
 +
 +                let map = cx.tcx.hir();
 +
 +                // Checking for slice indexing
 +                let parent_id = map.get_parent_node(expr.hir_id);
 +                if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id);
 +                if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind;
 +                if let Some((Constant::Int(index_value), _)) = constant(cx, cx.typeck_results(), index_expr);
 +                if let Ok(index_value) = index_value.try_into();
 +                if index_value < max_suggested_slice;
 +
 +                // Make sure that this slice index is read only
 +                let maybe_addrof_id = map.get_parent_node(parent_id);
 +                if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id);
 +                if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind;
 +                then {
 +                    use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
 +                    return;
 +                }
 +            }
 +
 +            // The slice was used for something other than indexing
 +            self.slice_lint_info.remove(&local_id);
 +        }
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index 60754b224fc840a8f21ff7b153f028f3b5f48ee0,0000000000000000000000000000000000000000..dd1b23e7d9d29f19a513b36efbc1468e690907b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,182 @@@
- 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};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints subtraction between `Instant::now()` and another `Instant`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is easy to accidentally write `prev_instant - Instant::now()`, which will always be 0ns
 +    /// as `Instant` subtraction saturates.
 +    ///
 +    /// `prev_instant.elapsed()` also more clearly signals intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::time::Instant;
 +    /// let prev_instant = Instant::now();
 +    /// let duration = Instant::now() - prev_instant;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::time::Instant;
 +    /// let prev_instant = Instant::now();
 +    /// let duration = prev_instant.elapsed();
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub MANUAL_INSTANT_ELAPSED,
 +    pedantic,
 +    "subtraction between `Instant::now()` and previous `Instant`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints subtraction between an [`Instant`] and a [`Duration`].
 +    ///
 +    /// ### Why is this bad?
 +    /// Unchecked subtraction could cause underflow on certain platforms, leading to
 +    /// unintentional panics.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::time::{Instant, Duration};
 +    /// let time_passed = Instant::now() - Duration::from_secs(5);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::time::{Instant, Duration};
 +    /// let time_passed = Instant::now().checked_sub(Duration::from_secs(5));
 +    /// ```
 +    ///
 +    /// [`Duration`]: std::time::Duration
 +    /// [`Instant::now()`]: std::time::Instant::now;
 +    #[clippy::version = "1.65.0"]
 +    pub UNCHECKED_DURATION_SUBTRACTION,
 +    suspicious,
 +    "finds unchecked subtraction of a 'Duration' from an 'Instant'"
 +}
 +
 +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 }
 +    }
 +}
 +
 +impl_lint_pass!(InstantSubtraction => [MANUAL_INSTANT_ELAPSED, UNCHECKED_DURATION_SUBTRACTION]);
 +
 +impl LateLintPass<'_> for InstantSubtraction {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Sub, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) = expr.kind
 +        {
 +            if_chain! {
 +                if is_instant_now_call(cx, lhs);
 +
 +                if is_an_instant(cx, rhs);
 +                if let Some(sugg) = Sugg::hir_opt(cx, rhs);
 +
 +                then {
 +                    print_manual_instant_elapsed_sugg(cx, expr, sugg)
 +                } 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);
 +
 +                        then {
 +                            print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr)
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
 +    if let ExprKind::Call(fn_expr, []) = expr_block.kind
 +        && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
 +        && clippy_utils::match_def_path(cx, fn_id, &clippy_utils::paths::INSTANT_NOW)
 +    {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let expr_ty = cx.typeck_results().expr_ty(expr);
 +
 +    match expr_ty.kind() {
 +        rustc_middle::ty::Adt(def, _) => clippy_utils::match_def_path(cx, def.did(), &clippy_utils::paths::INSTANT),
 +        _ => false,
 +    }
 +}
 +
 +fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let expr_ty = cx.typeck_results().expr_ty(expr);
 +    ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
 +}
 +
 +fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) {
 +    span_lint_and_sugg(
 +        cx,
 +        MANUAL_INSTANT_ELAPSED,
 +        expr.span,
 +        "manual implementation of `Instant::elapsed`",
 +        "try",
 +        format!("{}.elapsed()", sugg.maybe_par()),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn print_unchecked_duration_subtraction_sugg(
 +    cx: &LateContext<'_>,
 +    left_expr: &Expr<'_>,
 +    right_expr: &Expr<'_>,
 +    expr: &Expr<'_>,
 +) {
 +    let mut applicability = Applicability::MachineApplicable;
 +
 +    let left_expr =
 +        source::snippet_with_applicability(cx, left_expr.span, "std::time::Instant::now()", &mut applicability);
 +    let right_expr = source::snippet_with_applicability(
 +        cx,
 +        right_expr.span,
 +        "std::time::Duration::from_secs(1)",
 +        &mut applicability,
 +    );
 +
 +    diagnostics::span_lint_and_sugg(
 +        cx,
 +        UNCHECKED_DURATION_SUBTRACTION,
 +        expr.span,
 +        "unchecked subtraction of a 'Duration' from an 'Instant'",
 +        "try",
 +        format!("{left_expr}.checked_sub({right_expr}).unwrap()"),
 +        applicability,
 +    );
 +}
index 601990cd6a3169776a4e3f0cd8005d07196d18d0,0000000000000000000000000000000000000000..7b17d8a156d5af6ddb3b9417b7990e65d2a3d5ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,985 -1,0 +1,948 @@@
- use clippy_utils::parse_msrv;
 +#![feature(array_windows)]
 +#![feature(binary_heap_into_iter_sorted)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(drain_filter)]
 +#![feature(iter_intersperse)]
 +#![feature(let_chains)]
 +#![feature(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +// Disable this rustc lint for now, as it was also done in rustc
 +#![allow(rustc::potential_query_instability)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_arena;
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_analysis;
 +extern crate rustc_hir_pretty;
 +extern crate rustc_hir_typeck;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_parse;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +#[macro_use]
 +extern crate declare_clippy_lint;
 +
 +use std::io;
 +use std::path::PathBuf;
 +
- use rustc_semver::RustcVersion;
++use clippy_utils::msrvs::Msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::{Lint, LintId};
-     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
-     }
 +use rustc_session::Session;
 +
 +#[cfg(feature = "internal")]
 +pub mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
 +mod declared_lints;
 +mod renamed_lints;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod almost_complete_letter_range;
 +mod approx_const;
 +mod as_conversions;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assertions_on_result_states;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod bool_to_int_with_if;
 +mod booleans;
 +mod borrow_deref_ref;
 +mod box_default;
 +mod cargo;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_instead_of_iter_empty;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_macros;
 +mod disallowed_methods;
 +mod disallowed_names;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duplicate_mod;
 +mod else_if_without_else;
 +mod empty_drop;
 +mod empty_enum;
 +mod empty_structs_with_brackets;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod equatable_if_let;
 +mod escape;
 +mod eta_reduction;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
 +mod format_impl;
 +mod format_push_string;
 +mod formatting;
 +mod from_over_into;
 +mod from_raw_with_void_ptr;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +mod implicit_saturating_add;
 +mod implicit_saturating_sub;
 +mod inconsistent_struct_constructor;
 +mod index_refutable_slice;
 +mod indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +mod init_numbered_fields;
 +mod inline_fn_without_body;
 +mod instant_subtraction;
 +mod int_plus_one;
 +mod invalid_upcast_comparisons;
 +mod invalid_utf8_in_unchecked;
 +mod items_after_statements;
 +mod iter_not_returning_iterator;
 +mod large_const_arrays;
 +mod large_enum_variant;
 +mod large_include_file;
 +mod large_stack_arrays;
 +mod len_zero;
 +mod let_if_seq;
 +mod let_underscore;
 +mod lifetimes;
 +mod literal_representation;
 +mod loops;
 +mod macro_use;
 +mod main_recursion;
 +mod manual_assert;
 +mod manual_async_fn;
 +mod manual_bits;
 +mod manual_clamp;
 +mod manual_is_ascii_check;
 +mod manual_let_else;
 +mod manual_non_exhaustive;
 +mod manual_rem_euclid;
 +mod manual_retain;
 +mod manual_string_new;
 +mod manual_strip;
 +mod map_unit_fn;
 +mod match_result_ok;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod mismatching_type_param_order;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod missing_trait_methods;
 +mod mixed_read_write_in_expression;
 +mod module_style;
 +mod multi_assignments;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bool;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_late_init;
 +mod needless_parens_on_range_literals;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod non_send_fields_in_send_ty;
 +mod nonstandard_macro_braces;
 +mod octal_escapes;
 +mod only_used_in_recursion;
 +mod operators;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partial_pub_fields;
 +mod partialeq_ne_impl;
 +mod partialeq_to_none;
 +mod pass_by_ref_or_value;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_offset_with_cast;
 +mod pub_use;
 +mod question_mark;
 +mod ranges;
 +mod rc_clone_in_vec_init;
 +mod read_zero_byte_vec;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
 +mod return_self_not_must_use;
 +mod returns;
 +mod same_name_method;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +mod single_char_lifetime_names;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
 +mod std_instead_of_core;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +mod suspicious_xor_used_as_pow;
 +mod swap;
 +mod swap_ptr_to_ref;
 +mod tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
 +mod trailing_empty_array;
 +mod trait_bounds;
 +mod transmute;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
 +mod unused_peekable;
 +mod unused_rounding;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +use crate::utils::conf::{format_error, TryConf};
 +pub use crate::utils::conf::{lookup_conf_file, Conf};
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// 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 = read_msrv(conf, sess);
++    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() }));
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session, path: &io::Result<Option<PathBuf>>) -> Conf {
 +    let file_name = match path {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {error}"))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors, warnings } = utils::conf::read(file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.err(format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(error)
 +        ));
 +    }
 +
 +    for warning in warnings {
 +        sess.struct_warn(format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(warning)
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +#[derive(Default)]
 +struct RegistrationGroups {
 +    all: Vec<LintId>,
 +    cargo: Vec<LintId>,
 +    complexity: Vec<LintId>,
 +    correctness: Vec<LintId>,
 +    nursery: Vec<LintId>,
 +    pedantic: Vec<LintId>,
 +    perf: Vec<LintId>,
 +    restriction: Vec<LintId>,
 +    style: Vec<LintId>,
 +    suspicious: Vec<LintId>,
 +    #[cfg(feature = "internal")]
 +    internal: Vec<LintId>,
 +}
 +
 +impl RegistrationGroups {
 +    #[rustfmt::skip]
 +    fn register(self, store: &mut rustc_lint::LintStore) {
 +        store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
 +        store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
 +        store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
 +        store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness);
 +        store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
 +        store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
 +        store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
 +        store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction);
 +        store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
 +        store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
 +        #[cfg(feature = "internal")]
 +        store.register_group(true, "clippy::internal", Some("clippy_internal"), self.internal);
 +    }
 +}
 +
 +#[derive(Copy, Clone)]
 +pub(crate) enum LintCategory {
 +    Cargo,
 +    Complexity,
 +    Correctness,
 +    Nursery,
 +    Pedantic,
 +    Perf,
 +    Restriction,
 +    Style,
 +    Suspicious,
 +    #[cfg(feature = "internal")]
 +    Internal,
 +}
 +#[allow(clippy::enum_glob_use)]
 +use LintCategory::*;
 +
 +impl LintCategory {
 +    fn is_all(self) -> bool {
 +        matches!(self, Correctness | Suspicious | Style | Complexity | Perf)
 +    }
 +
 +    fn group(self, groups: &mut RegistrationGroups) -> &mut Vec<LintId> {
 +        match self {
 +            Cargo => &mut groups.cargo,
 +            Complexity => &mut groups.complexity,
 +            Correctness => &mut groups.correctness,
 +            Nursery => &mut groups.nursery,
 +            Pedantic => &mut groups.pedantic,
 +            Perf => &mut groups.perf,
 +            Restriction => &mut groups.restriction,
 +            Style => &mut groups.style,
 +            Suspicious => &mut groups.suspicious,
 +            #[cfg(feature = "internal")]
 +            Internal => &mut groups.internal,
 +        }
 +    }
 +}
 +
 +pub(crate) struct LintInfo {
 +    /// Double reference to maintain pointer equality
 +    lint: &'static &'static Lint,
 +    category: LintCategory,
 +    explanation: &'static str,
 +}
 +
 +pub fn explain(name: &str) {
 +    let target = format!("clippy::{}", name.to_ascii_uppercase());
 +    match declared_lints::LINTS.iter().find(|info| info.lint.name == target) {
 +        Some(info) => print!("{}", info.explanation),
 +        None => println!("unknown lint: {name}"),
 +    }
 +}
 +
 +fn register_categories(store: &mut rustc_lint::LintStore) {
 +    let mut groups = RegistrationGroups::default();
 +
 +    for LintInfo { lint, category, .. } in declared_lints::LINTS {
 +        if category.is_all() {
 +            groups.all.push(LintId::of(lint));
 +        }
 +
 +        category.group(&mut groups).push(LintId::of(lint));
 +    }
 +
 +    let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect();
 +
 +    store.register_lints(&lints);
 +    groups.register(store);
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[expect(clippy::too_many_lines)]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +    register_categories(store);
 +
 +    include!("lib.deprecated.rs");
 +
 +    #[cfg(feature = "internal")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal")]
 +    {
 +        store.register_early_pass(|| Box::new(utils::internal_lints::clippy_lints_internal::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls));
 +        store.register_late_pass(|_| {
 +            Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new())
 +        });
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths));
 +        store.register_late_pass(|_| {
 +            Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
 +        });
 +        store.register_late_pass(|_| {
 +            Box::<utils::internal_lints::lint_without_lint_pass::LintWithoutLintPass>::default()
 +        });
 +        store.register_late_pass(|_| Box::<utils::internal_lints::unnecessary_def_path::UnnecessaryDefPath>::default());
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::outer_expn_data_pass::OuterExpnDataPass));
 +        store.register_late_pass(|_| Box::new(utils::internal_lints::msrv_attr_impl::MsrvAttrImpl));
 +    }
 +
 +    let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
 +            arithmetic_side_effects_allowed.clone(),
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
 +    store.register_late_pass(|_| Box::new(utils::author::Author));
 +    let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(await_holding_invalid::AwaitHolding::new(
 +            await_holding_invalid_types.clone(),
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
 +    let vec_box_size_threshold = conf.vec_box_size_threshold;
 +    let type_complexity_threshold = conf.type_complexity_threshold;
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move |_| {
 +        Box::new(types::Types::new(
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::new(booleans::NonminimalBool));
 +    store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
 +    store.register_late_pass(|_| Box::new(ptr::Ptr));
 +    store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
 +    store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
 +    store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
 +    store.register_late_pass(|_| Box::new(misc::MiscLints));
 +    store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
 +    store.register_late_pass(|_| Box::new(mut_mut::MutMut));
 +    store.register_late_pass(|_| Box::new(mut_reference::UnnecessaryMutPassed));
 +    store.register_late_pass(|_| Box::new(len_zero::LenZero));
 +    store.register_late_pass(|_| Box::new(attrs::Attributes));
 +    store.register_late_pass(|_| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
 +    store.register_late_pass(|_| Box::new(unicode::Unicode));
 +    store.register_late_pass(|_| Box::new(uninit_vec::UninitVec));
 +    store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|_| Box::new(strings::StringAdd));
 +    store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
-     store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
++    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;
-             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,
-     store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
++            msrv(),
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        ))
 +    });
-     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(matches::Matches::new(msrv())));
 +    let matches_for_let_else = conf.matches_for_let_else;
-     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(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));
-             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,
-     store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv)));
++            msrv(),
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::<shadow::Shadow>::default());
 +    store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
 +    store.register_late_pass(|_| Box::new(loops::Loops));
 +    store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
 +    store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
 +    store.register_late_pass(|_| Box::new(entry::HashMapPass));
 +    store.register_late_pass(|_| Box::new(minmax::MinMaxPass));
 +    store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv));
 +    store.register_late_pass(|_| Box::new(mutex_atomic::Mutex));
 +    store.register_late_pass(|_| Box::new(needless_update::NeedlessUpdate));
 +    store.register_late_pass(|_| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
 +    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(dereference::Dereferencing::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(
 +            cognitive_complexity_threshold,
 +        ))
 +    });
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack }));
 +    store.register_late_pass(move |_| Box::new(vec::UselessVec { too_large_for_stack }));
 +    store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|_| Box::new(derive::Derive));
 +    store.register_late_pass(|_| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|_| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|_| Box::new(regex::Regex));
 +    store.register_late_pass(|_| Box::new(copies::CopyAndPaste));
 +    store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
 +    store.register_late_pass(|_| Box::new(format::UselessFormat));
 +    store.register_late_pass(|_| Box::new(swap::Swap));
 +    store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
 +    store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
 +    let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
 +    let large_error_threshold = conf.large_error_threshold;
 +    store.register_late_pass(move |_| {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +            large_error_threshold,
 +        ))
 +    });
 +    let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
 +    store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
 +    store.register_late_pass(|_| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
 +    store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
 +    store.register_late_pass(|_| Box::new(missing_doc::MissingDoc::new()));
 +    store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
 +    store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
 +    store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
 +    store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
 +    store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
 +    store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
 +    store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
 +    let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
 +        conf.trivial_copy_size_limit,
 +        conf.pass_by_value_size_limit,
 +        conf.avoid_breaking_exported_api,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move |_| Box::new(pass_by_ref_or_value));
 +    store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
 +    store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
 +    store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
 +    store.register_late_pass(|_| Box::<useless_conversion::UselessConversion>::default());
 +    store.register_late_pass(|_| Box::new(implicit_hasher::ImplicitHasher));
 +    store.register_late_pass(|_| Box::new(fallible_impl_from::FallibleImplFrom));
 +    store.register_late_pass(|_| Box::new(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|_| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|_| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|_| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|_| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|_| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|_| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|_| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
 +    store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
 +    store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
 +    let max_trait_bounds = conf.max_trait_bounds;
 +    store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
 +    store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
 +    let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
 +    store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
 +    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|_| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|_| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|_| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::LiteralDigitGrouping::new(
 +            literal_representation_lint_fraction_readability,
 +        ))
 +    });
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::DecimalLiteralRepresentation::new(
 +            literal_representation_threshold,
 +        ))
 +    });
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_late_pass(move |_| {
 +        Box::new(enum_variants::EnumVariantNames::new(
 +            enum_variant_name_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move |_| {
 +        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
 +            avoid_breaking_exported_api,
 +            upper_case_acronyms_aggressive,
 +        ))
 +    });
 +    store.register_late_pass(|_| Box::<default::Default>::default());
 +    store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|_| Box::new(exit::Exit));
 +    store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
 +    let max_fn_params_bools = conf.max_fn_params_bools;
 +    let max_struct_bools = conf.max_struct_bools;
 +    store.register_late_pass(move |_| {
 +        Box::new(excessive_bools::ExcessiveBools::new(
 +            max_struct_bools,
 +            max_fn_params_bools,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
 +    let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
 +    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(if_then_some_else_none::IfThenSomeElseNone::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));
 +    store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
 +    store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
 +    store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
 +    store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn));
 +    let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(non_expressive_names::NonExpressiveNames {
 +            single_char_binding_names_threshold,
 +        })
 +    });
 +    let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
 +    store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
 +    store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
 +    store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
 +    let disallowed_macros = conf.disallowed_macros.clone();
 +    store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone())));
 +    let disallowed_methods = conf.disallowed_methods.clone();
 +    store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
 +    store.register_late_pass(|_| Box::new(empty_drop::EmptyDrop));
 +    store.register_late_pass(|_| Box::new(strings::StrToString));
 +    store.register_late_pass(|_| Box::new(strings::StringToString));
 +    store.register_late_pass(|_| Box::new(zero_sized_map_values::ZeroSizedMapValues));
 +    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(format_args::FormatArgs::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));
 +    let disallowed_types = conf.disallowed_types.clone();
 +    store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
 +    let import_renames = conf.enforced_import_renames.clone();
 +    store.register_late_pass(move |_| {
 +        Box::new(missing_enforced_import_rename::ImportRename::new(
 +            import_renames.clone(),
 +        ))
 +    });
 +    let scripts = conf.allowed_scripts.clone();
 +    store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
 +    store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings));
 +    store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
 +    store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator));
 +    store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert));
 +    let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
 +    store.register_late_pass(move |_| {
 +        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
 +            enable_raw_pointer_heuristic_for_send,
 +        ))
 +    });
 +    store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
-     store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::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_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::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;
 +    store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
 +    let allow_print_in_tests = conf.allow_print_in_tests;
 +    store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests)));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move |_| {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
 +    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
 +    store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
 +    store.register_early_pass(|| Box::new(pub_use::PubUse));
 +    store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
 +    let max_include_file_size = conf.max_include_file_size;
 +    store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
 +    store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
 +    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_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_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(instant_subtraction::InstantSubtraction::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(manual_clamp::ManualClamp::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_is_ascii_check::ManualIsAsciiCheck::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));
 +    store.register_late_pass(|_| Box::new(bool_to_int_with_if::BoolToIntWithIf));
 +    store.register_late_pass(|_| Box::new(box_default::BoxDefault));
 +    store.register_late_pass(|_| Box::new(implicit_saturating_add::ImplicitSaturatingAdd));
 +    store.register_early_pass(|| Box::new(partial_pub_fields::PartialPubFields));
 +    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())));
 +    // add lints here, do not remove this comment, it's used in `new_lint`
 +}
 +
 +#[rustfmt::skip]
 +fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
 +    store.register_removed(
 +        "should_assert_eq",
 +        "`assert!()` will be more flexible with RFC 2011",
 +    );
 +    store.register_removed(
 +        "extend_from_slice",
 +        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
 +    );
 +    store.register_removed(
 +        "range_step_by_zero",
 +        "`iterator.step_by(0)` panics nowadays",
 +    );
 +    store.register_removed(
 +        "unstable_as_slice",
 +        "`Vec::as_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "unstable_as_mut_slice",
 +        "`Vec::as_mut_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "misaligned_transmute",
 +        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
 +    );
 +    store.register_removed(
 +        "assign_ops",
 +        "using compound assignment operators (e.g., `+=`) is harmless",
 +    );
 +    store.register_removed(
 +        "if_let_redundant_pattern_matching",
 +        "this lint has been changed to redundant_pattern_matching",
 +    );
 +    store.register_removed(
 +        "unsafe_vector_initialization",
 +        "the replacement suggested by this lint had substantially different behavior",
 +    );
 +    store.register_removed(
 +        "reverse_range_loop",
 +        "this lint is now included in reversed_empty_ranges",
 +    );
 +}
 +
 +/// Register renamed lints.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
 +    for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
 +        ls.register_renamed(old_name, new_name);
 +    }
 +}
 +
 +// only exists to let the dogfood integration test works.
 +// Don't run clippy as an executable directly
 +#[allow(dead_code)]
 +fn main() {
 +    panic!("Please use the cargo-clippy executable");
 +}
index 220941dcd5dbf6c4b0696b6c4d7fef98aa578a4f,0000000000000000000000000000000000000000..7cf1a6b8084a613b9053e2c1fe18bd2498f590d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,658 -1,0 +1,651 @@@
-     walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 +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::{
-             TyKind::TraitObject(bounds, ref lt, _) => {
++    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;
 +use rustc_hir::FnRetTy::Return;
 +use rustc_hir::{
 +    BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, ImplItem,
 +    ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, PolyTraitRef, PredicateOrigin, TraitFn,
 +    TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter as middle_nested_filter;
 +use rustc_middle::ty::TyCtxt;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, Ident, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for lifetime annotations which can be removed by
 +    /// relying on lifetime elision.
 +    ///
 +    /// ### Why is this bad?
 +    /// The additional lifetimes make the code look more
 +    /// complicated, while there is nothing out of the ordinary going on. Removing
 +    /// them leads to more readable code.
 +    ///
 +    /// ### Known problems
 +    /// - We bail out if the function has a `where` clause where lifetimes
 +    /// are mentioned due to potential false positives.
 +    /// - Lifetime bounds such as `impl Foo + 'a` and `T: 'a` must be elided with the
 +    /// placeholder notation `'_` because the fully elided notation leaves the type bound to `'static`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Unnecessary lifetime annotations
 +    /// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
 +    ///     x
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// fn elided(x: &u8, y: u8) -> &u8 {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_LIFETIMES,
 +    complexity,
 +    "using explicit lifetimes for references in function arguments when elision rules \
 +     would allow omitting them"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for lifetimes in generics that are never used
 +    /// anywhere else.
 +    ///
 +    /// ### Why is this bad?
 +    /// The additional lifetimes make the code look more
 +    /// complicated, while there is nothing out of the ordinary going on. Removing
 +    /// them leads to more readable code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // unnecessary lifetimes
 +    /// fn unused_lifetime<'a>(x: u8) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// fn no_lifetime(x: u8) {
 +    ///     // ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXTRA_UNUSED_LIFETIMES,
 +    complexity,
 +    "unused lifetimes in function definitions"
 +}
 +
 +declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Lifetimes {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Fn(ref sig, generics, id) = item.kind {
 +            check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
 +        } else if let ItemKind::Impl(impl_) = item.kind {
 +            if !item.span.from_expansion() {
 +                report_extra_impl_lifetimes(cx, impl_);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if let ImplItemKind::Fn(ref sig, id) = item.kind {
 +            let report_extra_lifetimes = trait_ref_of_method(cx, item.owner_id.def_id).is_none();
 +            check_fn_inner(
 +                cx,
 +                sig.decl,
 +                Some(id),
 +                None,
 +                item.generics,
 +                item.span,
 +                report_extra_lifetimes,
 +            );
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(ref sig, ref body) = item.kind {
 +            let (body, trait_sig) = match *body {
 +                TraitFn::Required(sig) => (None, Some(sig)),
 +                TraitFn::Provided(id) => (Some(id), None),
 +            };
 +            check_fn_inner(cx, sig.decl, body, trait_sig, item.generics, item.span, true);
 +        }
 +    }
 +}
 +
 +/// The lifetime of a &-reference.
 +#[derive(PartialEq, Eq, Hash, Debug, Clone)]
 +enum RefLt {
 +    Unnamed,
 +    Static,
 +    Named(LocalDefId),
 +}
 +
 +fn check_fn_inner<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx FnDecl<'_>,
 +    body: Option<BodyId>,
 +    trait_sig: Option<&[Ident]>,
 +    generics: &'tcx Generics<'_>,
 +    span: Span,
 +    report_extra_lifetimes: bool,
 +) {
 +    if span.from_expansion() || has_where_lifetimes(cx, generics) {
 +        return;
 +    }
 +
 +    let types = generics
 +        .params
 +        .iter()
 +        .filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
 +
 +    for typ in types {
 +        for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
 +            if pred.origin == PredicateOrigin::WhereClause {
 +                // has_where_lifetimes checked that this predicate contains no lifetime.
 +                continue;
 +            }
 +
 +            for bound in pred.bounds {
 +                let mut visitor = RefVisitor::new(cx);
 +                walk_param_bound(&mut visitor, bound);
 +                if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
 +                    return;
 +                }
 +                if let GenericBound::Trait(ref trait_ref, _) = *bound {
 +                    let params = &trait_ref
 +                        .trait_ref
 +                        .path
 +                        .segments
 +                        .last()
 +                        .expect("a path must have at least one segment")
 +                        .args;
 +                    if let Some(params) = *params {
 +                        let lifetimes = params.args.iter().filter_map(|arg| match arg {
 +                            GenericArg::Lifetime(lt) => Some(lt),
 +                            _ => None,
 +                        });
 +                        for bound in lifetimes {
 +                            if !bound.is_static() && !bound.is_elided() {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if let Some(elidable_lts) = could_use_elision(cx, decl, body, trait_sig, generics.params) {
 +        let lts = elidable_lts
 +            .iter()
 +            // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
 +            // `Node::GenericParam`.
 +            .filter_map(|&(def_id, _)| cx.tcx.hir().get_by_def_id(def_id).ident())
 +            .map(|ident| ident.to_string())
 +            .collect::<Vec<_>>()
 +            .join(", ");
 +
 +        span_lint_and_then(
 +            cx,
 +            NEEDLESS_LIFETIMES,
 +            span.with_hi(decl.output.span().hi()),
 +            &format!("the following explicit lifetimes could be elided: {lts}"),
 +            |diag| {
 +                if let Some(span) = elidable_lts.iter().find_map(|&(_, span)| span) {
 +                    diag.span_help(span, "replace with `'_` in generic arguments such as here");
 +                }
 +            },
 +        );
 +    }
 +
 +    if report_extra_lifetimes {
 +        self::report_extra_lifetimes(cx, decl, generics);
 +    }
 +}
 +
 +// elision doesn't work for explicit self types, see rust-lang/rust#69064
 +fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
 +    if_chain! {
 +        if let Some(ident) = ident;
 +        if ident.name == kw::SelfLower;
 +        if !func.implicit_self.has_implicit_self();
 +
 +        if let Some(self_ty) = func.inputs.first();
 +        then {
 +            let mut visitor = RefVisitor::new(cx);
 +            visitor.visit_ty(self_ty);
 +
 +            !visitor.all_lts().is_empty()
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn could_use_elision<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    func: &'tcx FnDecl<'_>,
 +    body: Option<BodyId>,
 +    trait_sig: Option<&[Ident]>,
 +    named_generics: &'tcx [GenericParam<'_>],
 +) -> Option<Vec<(LocalDefId, Option<Span>)>> {
 +    // There are two scenarios where elision works:
 +    // * no output references, all input references have different LT
 +    // * output references, exactly one input reference with same LT
 +    // All lifetimes must be unnamed, 'static or defined without bounds on the
 +    // level of the current item.
 +
 +    // check named LTs
 +    let allowed_lts = allowed_lts_from(cx.tcx, named_generics);
 +
 +    // these will collect all the lifetimes for references in arg/return types
 +    let mut input_visitor = RefVisitor::new(cx);
 +    let mut output_visitor = RefVisitor::new(cx);
 +
 +    // extract lifetimes in input argument types
 +    for arg in func.inputs {
 +        input_visitor.visit_ty(arg);
 +    }
 +    // extract lifetimes in output type
 +    if let Return(ty) = func.output {
 +        output_visitor.visit_ty(ty);
 +    }
 +    for lt in named_generics {
 +        input_visitor.visit_generic_param(lt);
 +    }
 +
 +    if input_visitor.abort() || output_visitor.abort() {
 +        return None;
 +    }
 +
 +    let input_lts = input_visitor.lts;
 +    let output_lts = output_visitor.lts;
 +
 +    if let Some(trait_sig) = trait_sig {
 +        if explicit_self_type(cx, func, trait_sig.first().copied()) {
 +            return None;
 +        }
 +    }
 +
 +    if let Some(body_id) = body {
 +        let body = cx.tcx.hir().body(body_id);
 +
 +        let first_ident = body.params.first().and_then(|param| param.pat.simple_ident());
 +        if explicit_self_type(cx, func, first_ident) {
 +            return None;
 +        }
 +
 +        let mut checker = BodyLifetimeChecker {
 +            lifetimes_used_in_body: false,
 +        };
 +        checker.visit_expr(body.value);
 +        if checker.lifetimes_used_in_body {
 +            return None;
 +        }
 +    }
 +
 +    // check for lifetimes from higher scopes
 +    for lt in input_lts.iter().chain(output_lts.iter()) {
 +        if !allowed_lts.contains(lt) {
 +            return None;
 +        }
 +    }
 +
 +    // check for higher-ranked trait bounds
 +    if !input_visitor.nested_elision_site_lts.is_empty() || !output_visitor.nested_elision_site_lts.is_empty() {
 +        let allowed_lts: FxHashSet<_> = allowed_lts
 +            .iter()
 +            .filter_map(|lt| match lt {
 +                RefLt::Named(def_id) => Some(cx.tcx.item_name(def_id.to_def_id())),
 +                _ => None,
 +            })
 +            .collect();
 +        for lt in input_visitor.nested_elision_site_lts {
 +            if let RefLt::Named(def_id) = lt {
 +                if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
 +                    return None;
 +                }
 +            }
 +        }
 +        for lt in output_visitor.nested_elision_site_lts {
 +            if let RefLt::Named(def_id) = lt {
 +                if allowed_lts.contains(&cx.tcx.item_name(def_id.to_def_id())) {
 +                    return None;
 +                }
 +            }
 +        }
 +    }
 +
 +    // A lifetime can be newly elided if:
 +    // - It occurs only once among the inputs.
 +    // - If there are multiple input lifetimes, then the newly elided lifetime does not occur among the
 +    //   outputs (because eliding such an lifetime would create an ambiguity).
 +    let elidable_lts = named_lifetime_occurrences(&input_lts)
 +        .into_iter()
 +        .filter_map(|(def_id, occurrences)| {
 +            if occurrences == 1 && (input_lts.len() == 1 || !output_lts.contains(&RefLt::Named(def_id))) {
 +                Some((
 +                    def_id,
 +                    input_visitor
 +                        .lifetime_generic_arg_spans
 +                        .get(&def_id)
 +                        .or_else(|| output_visitor.lifetime_generic_arg_spans.get(&def_id))
 +                        .copied(),
 +                ))
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    if elidable_lts.is_empty() {
 +        None
 +    } else {
 +        Some(elidable_lts)
 +    }
 +}
 +
 +fn allowed_lts_from(tcx: TyCtxt<'_>, named_generics: &[GenericParam<'_>]) -> FxHashSet<RefLt> {
 +    let mut allowed_lts = FxHashSet::default();
 +    for par in named_generics.iter() {
 +        if let GenericParamKind::Lifetime { .. } = par.kind {
 +            allowed_lts.insert(RefLt::Named(tcx.hir().local_def_id(par.hir_id)));
 +        }
 +    }
 +    allowed_lts.insert(RefLt::Unnamed);
 +    allowed_lts.insert(RefLt::Static);
 +    allowed_lts
 +}
 +
 +/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve
 +/// relative order.
 +#[must_use]
 +fn named_lifetime_occurrences(lts: &[RefLt]) -> Vec<(LocalDefId, usize)> {
 +    let mut occurrences = Vec::new();
 +    for lt in lts {
 +        if let &RefLt::Named(curr_def_id) = lt {
 +            if let Some(pair) = occurrences
 +                .iter_mut()
 +                .find(|(prev_def_id, _)| *prev_def_id == curr_def_id)
 +            {
 +                pair.1 += 1;
 +            } else {
 +                occurrences.push((curr_def_id, 1));
 +            }
 +        }
 +    }
 +    occurrences
 +}
 +
 +/// A visitor usable for `rustc_front::visit::walk_ty()`.
 +struct RefVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    lts: Vec<RefLt>,
 +    lifetime_generic_arg_spans: FxHashMap<LocalDefId, Span>,
 +    nested_elision_site_lts: Vec<RefLt>,
 +    unelided_trait_object_lifetime: bool,
 +}
 +
 +impl<'a, 'tcx> RefVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            lts: Vec::new(),
 +            lifetime_generic_arg_spans: FxHashMap::default(),
 +            nested_elision_site_lts: Vec::new(),
 +            unelided_trait_object_lifetime: false,
 +        }
 +    }
 +
 +    fn record(&mut self, lifetime: &Option<Lifetime>) {
 +        if let Some(ref lt) = *lifetime {
 +            if lt.is_static() {
 +                self.lts.push(RefLt::Static);
 +            } else if lt.is_anonymous() {
 +                // Fresh lifetimes generated should be ignored.
 +                self.lts.push(RefLt::Unnamed);
 +            } else if let LifetimeName::Param(def_id) = lt.res {
 +                self.lts.push(RefLt::Named(def_id));
 +            }
 +        } else {
 +            self.lts.push(RefLt::Unnamed);
 +        }
 +    }
 +
 +    fn all_lts(&self) -> Vec<RefLt> {
 +        self.lts
 +            .iter()
 +            .chain(self.nested_elision_site_lts.iter())
 +            .cloned()
 +            .collect::<Vec<_>>()
 +    }
 +
 +    fn abort(&self) -> bool {
 +        self.unelided_trait_object_lifetime
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> {
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        self.record(&Some(*lifetime));
 +    }
 +
 +    fn visit_poly_trait_ref(&mut self, poly_tref: &'tcx PolyTraitRef<'tcx>) {
 +        let trait_ref = &poly_tref.trait_ref;
 +        if let Some(id) = trait_ref.trait_def_id() && lang_items::FN_TRAITS.iter().any(|&item| {
 +            self.cx.tcx.lang_items().get(item) == Some(id)
 +        }) {
 +            let mut sub_visitor = RefVisitor::new(self.cx);
 +            sub_visitor.visit_trait_ref(trait_ref);
 +            self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
 +        } else {
 +            walk_poly_trait_ref(self, poly_tref);
 +        }
 +    }
 +
 +    fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
 +        match ty.kind {
 +            TyKind::OpaqueDef(item, bounds, _) => {
 +                let map = self.cx.tcx.hir();
 +                let item = map.item(item);
 +                let len = self.lts.len();
 +                walk_item(self, item);
 +                self.lts.truncate(len);
 +                self.lts.extend(bounds.iter().filter_map(|bound| match bound {
 +                    GenericArg::Lifetime(l) => Some(if let LifetimeName::Param(def_id) = l.res {
 +                        RefLt::Named(def_id)
 +                    } else {
 +                        RefLt::Unnamed
 +                    }),
 +                    _ => None,
 +                }));
 +            },
 +            TyKind::BareFn(&BareFnTy { decl, .. }) => {
 +                let mut sub_visitor = RefVisitor::new(self.cx);
 +                sub_visitor.visit_fn_decl(decl);
 +                self.nested_elision_site_lts.append(&mut sub_visitor.all_lts());
 +            },
-         // 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),
-         }
++            TyKind::TraitObject(bounds, lt, _) => {
 +                if !lt.is_elided() {
 +                    self.unelided_trait_object_lifetime = true;
 +                }
 +                for bound in bounds {
 +                    self.visit_poly_trait_ref(bound);
 +                }
 +            },
 +            _ => walk_ty(self, ty),
 +        }
 +    }
 +
 +    fn visit_generic_arg(&mut self, generic_arg: &'tcx GenericArg<'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);
 +        }
++        walk_generic_arg(self, generic_arg);
 +    }
 +}
 +
 +/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
 +/// reason about elision.
 +fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool {
 +    for predicate in generics.predicates {
 +        match *predicate {
 +            WherePredicate::RegionPredicate(..) => return true,
 +            WherePredicate::BoundPredicate(ref pred) => {
 +                // a predicate like F: Trait or F: for<'a> Trait<'a>
 +                let mut visitor = RefVisitor::new(cx);
 +                // walk the type F, it may not contain LT refs
 +                walk_ty(&mut visitor, pred.bounded_ty);
 +                if !visitor.all_lts().is_empty() {
 +                    return true;
 +                }
 +                // if the bounds define new lifetimes, they are fine to occur
 +                let allowed_lts = allowed_lts_from(cx.tcx, pred.bound_generic_params);
 +                // now walk the bounds
 +                for bound in pred.bounds.iter() {
 +                    walk_param_bound(&mut visitor, bound);
 +                }
 +                // and check that all lifetimes are allowed
 +                if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
 +                    return true;
 +                }
 +            },
 +            WherePredicate::EqPredicate(ref pred) => {
 +                let mut visitor = RefVisitor::new(cx);
 +                walk_ty(&mut visitor, pred.lhs_ty);
 +                walk_ty(&mut visitor, pred.rhs_ty);
 +                if !visitor.lts.is_empty() {
 +                    return true;
 +                }
 +            },
 +        }
 +    }
 +    false
 +}
 +
 +struct LifetimeChecker<'cx, 'tcx, F> {
 +    cx: &'cx LateContext<'tcx>,
 +    map: FxHashMap<Symbol, Span>,
 +    phantom: std::marker::PhantomData<F>,
 +}
 +
 +impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
 +    fn new(cx: &'cx LateContext<'tcx>, map: FxHashMap<Symbol, Span>) -> LifetimeChecker<'cx, 'tcx, F> {
 +        Self {
 +            cx,
 +            map,
 +            phantom: std::marker::PhantomData,
 +        }
 +    }
 +}
 +
 +impl<'cx, 'tcx, F> Visitor<'tcx> for LifetimeChecker<'cx, 'tcx, F>
 +where
 +    F: NestedFilter<'tcx>,
 +{
 +    type Map = rustc_middle::hir::map::Map<'tcx>;
 +    type NestedFilter = F;
 +
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        self.map.remove(&lifetime.ident.name);
 +    }
 +
 +    fn visit_generic_param(&mut self, param: &'tcx GenericParam<'_>) {
 +        // don't actually visit `<'a>` or `<'a: 'b>`
 +        // we've already visited the `'a` declarations and
 +        // don't want to spuriously remove them
 +        // `'b` in `'a: 'b` is useless unless used elsewhere in
 +        // a non-lifetime bound
 +        if let GenericParamKind::Type { .. } = param.kind {
 +            walk_generic_param(self, param);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) {
 +    let hs = generics
 +        .params
 +        .iter()
 +        .filter_map(|par| match par.kind {
 +            GenericParamKind::Lifetime {
 +                kind: LifetimeParamKind::Explicit,
 +            } => Some((par.name.ident().name, par.span)),
 +            _ => None,
 +        })
 +        .collect();
 +    let mut checker = LifetimeChecker::<hir_nested_filter::None>::new(cx, hs);
 +
 +    walk_generics(&mut checker, generics);
 +    walk_fn_decl(&mut checker, func);
 +
 +    for &v in checker.map.values() {
 +        span_lint(
 +            cx,
 +            EXTRA_UNUSED_LIFETIMES,
 +            v,
 +            "this lifetime isn't used in the function definition",
 +        );
 +    }
 +}
 +
 +fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'_>) {
 +    let hs = impl_
 +        .generics
 +        .params
 +        .iter()
 +        .filter_map(|par| match par.kind {
 +            GenericParamKind::Lifetime {
 +                kind: LifetimeParamKind::Explicit,
 +            } => Some((par.name.ident().name, par.span)),
 +            _ => None,
 +        })
 +        .collect();
 +    let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, hs);
 +
 +    walk_generics(&mut checker, impl_.generics);
 +    if let Some(ref trait_ref) = impl_.of_trait {
 +        walk_trait_ref(&mut checker, trait_ref);
 +    }
 +    walk_ty(&mut checker, impl_.self_ty);
 +    for item in impl_.items {
 +        walk_impl_item_ref(&mut checker, item);
 +    }
 +
 +    for &v in checker.map.values() {
 +        span_lint(cx, EXTRA_UNUSED_LIFETIMES, v, "this lifetime isn't used in the impl");
 +    }
 +}
 +
 +struct BodyLifetimeChecker {
 +    lifetimes_used_in_body: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BodyLifetimeChecker {
 +    // for lifetimes as parameters of generics
 +    fn visit_lifetime(&mut self, lifetime: &'tcx Lifetime) {
 +        if !lifetime.is_anonymous() && lifetime.ident.name != kw::StaticLifetime {
 +            self.lifetimes_used_in_body = true;
 +        }
 +    }
 +}
index 6655c92b1da8da05a2f2f983bbe9fd8e28eaf439,0000000000000000000000000000000000000000..462d73cf0b974ff8675b306763e8c5108446cb1f
mode 100644,000000..100644
--- /dev/null
@@@ -1,146 -1,0 +1,146 @@@
- use clippy_utils::{get_parent_expr, meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +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};
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of `std::mem::size_of::<T>() * 8` when
 +    /// `T::BITS` is available.
 +    ///
 +    /// ### Why is this bad?
 +    /// Can be written as the shorter `T::BITS`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// std::mem::size_of::<usize>() * 8;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// usize::BITS as usize;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub MANUAL_BITS,
 +    style,
 +    "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`"
 +}
 +
 +#[derive(Clone)]
 +pub struct ManualBits {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualBits {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::MANUAL_BITS) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualBits {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if !self.msrv.meets(msrvs::MANUAL_BITS) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
 +            if let BinOpKind::Mul = &bin_op.node;
 +            if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
 +            if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
 +            if let ExprKind::Lit(lit) = &other_expr.kind;
 +            if let LitKind::Int(8, _) = lit.node;
 +            then {
 +                let mut app = Applicability::MachineApplicable;
 +                let ty_snip = snippet_with_applicability(cx, real_ty.span, "..", &mut app);
 +                let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MANUAL_BITS,
 +                    expr.span,
 +                    "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
 +                    "consider using",
 +                    sugg,
 +                    app,
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn get_one_size_of_ty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr1: &'tcx Expr<'_>,
 +    expr2: &'tcx Expr<'_>,
 +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> {
 +    match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) {
 +        (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)),
 +        (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)),
 +        _ => None,
 +    }
 +}
 +
 +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
 +    if_chain! {
 +        if let ExprKind::Call(count_func, _func_args) = expr.kind;
 +        if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
 +
 +        if let QPath::Resolved(_, count_func_path) = count_func_qpath;
 +        if let Some(segment_zero) = count_func_path.segments.get(0);
 +        if let Some(args) = segment_zero.args;
 +        if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
 +
 +        if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
 +        then {
 +            cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {
 +    if let Some(parent_expr) = get_parent_expr(cx, expr) {
 +        if is_ty_conversion(parent_expr) {
 +            return base_sugg;
 +        }
 +
 +        // These expressions have precedence over casts, the suggestion therefore
 +        // needs to be wrapped into parentheses
 +        match parent_expr.kind {
 +            ExprKind::Unary(..) | ExprKind::AddrOf(..) | ExprKind::MethodCall(..) => {
 +                return format!("({base_sugg} as usize)");
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    format!("{base_sugg} as usize")
 +}
 +
 +fn is_ty_conversion(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Cast(..) = expr.kind {
 +        true
 +    } else if let ExprKind::MethodCall(path, _, [], _) = expr.kind
 +        && path.ident.name == rustc_span::sym::try_into
 +    {
 +        // This is only called for `usize` which implements `TryInto`. Therefore,
 +        // we don't have to check here if `self` implements the `TryInto` trait.
 +        true
 +    } else {
 +        false
 +    }
 +}
index 02dc8755dd61c9e86f23bbe209066680d964d5f0,0000000000000000000000000000000000000000..bb6d628af3b5061f35c3a19e7df8498481c8ad5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,717 -1,0 +1,714 @@@
- use rustc_semver::RustcVersion;
++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 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;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span};
 +use std::ops::Deref;
 +
-     msrv: Option<RustcVersion>,
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Identifies good opportunities for a clamp function from std or core, and suggests using it.
 +    ///
 +    /// ### Why is this bad?
 +    /// clamp is much shorter, easier to read, and doesn't use any control flow.
 +    ///
 +    /// ### Known issue(s)
 +    /// If the clamped variable is NaN this suggestion will cause the code to propagate NaN
 +    /// rather than returning either `max` or `min`.
 +    ///
 +    /// `clamp` functions will panic if `max < min`, `max.is_nan()`, or `min.is_nan()`.
 +    /// Some may consider panicking in these situations to be desirable, but it also may
 +    /// introduce panicking where there wasn't any before.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// if input > max {
 +    ///     max
 +    /// } else if input < min {
 +    ///     min
 +    /// } else {
 +    ///     input
 +    /// }
 +    /// # ;
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// input.max(min).min(max)
 +    /// # ;
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// match input {
 +    ///     x if x > max => max,
 +    ///     x if x < min => min,
 +    ///     x => x,
 +    /// }
 +    /// # ;
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// let mut x = input;
 +    /// if x < min { x = min; }
 +    /// if x > max { x = max; }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let (input, min, max) = (0, -2, 1);
 +    /// input.clamp(min, max)
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub MANUAL_CLAMP,
 +    complexity,
 +    "using a clamp pattern instead of the clamp function"
 +}
 +impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]);
 +
 +pub struct ManualClamp {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualClamp {
-         if !meets_msrv(self.msrv, msrvs::CLAMP) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct ClampSuggestion<'tcx> {
 +    params: InputMinMax<'tcx>,
 +    span: Span,
 +    make_assignment: Option<&'tcx Expr<'tcx>>,
 +    hir_with_ignore_attr: Option<HirId>,
 +}
 +
 +#[derive(Debug)]
 +struct InputMinMax<'tcx> {
 +    input: &'tcx Expr<'tcx>,
 +    min: &'tcx Expr<'tcx>,
 +    max: &'tcx Expr<'tcx>,
 +    is_float: bool,
 +}
 +
 +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() {
 +            let suggestion = is_if_elseif_else_pattern(cx, expr)
 +                .or_else(|| is_max_min_pattern(cx, expr))
 +                .or_else(|| is_call_max_min_pattern(cx, expr))
 +                .or_else(|| is_match_pattern(cx, expr))
 +                .or_else(|| is_if_elseif_pattern(cx, expr));
 +            if let Some(suggestion) = suggestion {
 +                emit_suggestion(cx, &suggestion);
 +            }
 +        }
 +    }
 +
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
++        if !self.msrv.meets(msrvs::CLAMP) {
 +            return;
 +        }
 +        for suggestion in is_two_if_pattern(cx, block) {
 +            emit_suggestion(cx, &suggestion);
 +        }
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn emit_suggestion<'tcx>(cx: &LateContext<'tcx>, suggestion: &ClampSuggestion<'tcx>) {
 +    let ClampSuggestion {
 +        params: InputMinMax {
 +            input,
 +            min,
 +            max,
 +            is_float,
 +        },
 +        span,
 +        make_assignment,
 +        hir_with_ignore_attr,
 +    } = suggestion;
 +    let input = Sugg::hir(cx, input, "..").maybe_par();
 +    let min = Sugg::hir(cx, min, "..");
 +    let max = Sugg::hir(cx, max, "..");
 +    let semicolon = if make_assignment.is_some() { ";" } else { "" };
 +    let assignment = if let Some(assignment) = make_assignment {
 +        let assignment = Sugg::hir(cx, assignment, "..");
 +        format!("{assignment} = ")
 +    } else {
 +        String::new()
 +    };
 +    let suggestion = format!("{assignment}{input}.clamp({min}, {max}){semicolon}");
 +    let msg = "clamp-like pattern without using clamp function";
 +    let lint_builder = |d: &mut Diagnostic| {
 +        d.span_suggestion(*span, "replace with clamp", suggestion, Applicability::MaybeIncorrect);
 +        if *is_float {
 +            d.note("clamp will panic if max < min, min.is_nan(), or max.is_nan()")
 +                .note("clamp returns NaN if the input is NaN");
 +        } else {
 +            d.note("clamp will panic if max < min");
 +        }
 +    };
 +    if let Some(hir_id) = hir_with_ignore_attr {
 +        span_lint_hir_and_then(cx, MANUAL_CLAMP, *hir_id, *span, msg, lint_builder);
 +    } else {
 +        span_lint_and_then(cx, MANUAL_CLAMP, *span, msg, lint_builder);
 +    }
 +}
 +
 +#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 +enum TypeClampability {
 +    Float,
 +    Ord,
 +}
 +
 +impl TypeClampability {
 +    fn is_clampable<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<TypeClampability> {
 +        if ty.is_floating_point() {
 +            Some(TypeClampability::Float)
 +        } else if cx
 +            .tcx
 +            .get_diagnostic_item(sym::Ord)
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +        {
 +            Some(TypeClampability::Ord)
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn is_float(self) -> bool {
 +        matches!(self, TypeClampability::Float)
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min, max) = (0, -3, 12);
 +///
 +/// if input < min {
 +///     min
 +/// } else if input > max {
 +///     max
 +/// } else {
 +///     input
 +/// }
 +/// # ;
 +/// ```
 +fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let Some(If {
 +        cond,
 +        then,
 +        r#else: Some(else_if),
 +    }) = If::hir(expr)
 +    && let Some(If {
 +        cond: else_if_cond,
 +        then: else_if_then,
 +        r#else: Some(else_body),
 +    }) = If::hir(peel_blocks(else_if))
 +    {
 +        let params = is_clamp_meta_pattern(
 +            cx,
 +            &BinaryOp::new(peel_blocks(cond))?,
 +            &BinaryOp::new(peel_blocks(else_if_cond))?,
 +            peel_blocks(then),
 +            peel_blocks(else_if_then),
 +            None,
 +        )?;
 +        // Contents of the else should be the resolved input.
 +        if !eq_expr_value(cx, params.input, peel_blocks(else_body)) {
 +            return None;
 +        }
 +        Some(ClampSuggestion {
 +            params,
 +            span: expr.span,
 +            make_assignment: None,
 +            hir_with_ignore_attr: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min_value, max_value) = (0, -3, 12);
 +///
 +/// input.max(min_value).min(max_value)
 +/// # ;
 +/// ```
 +fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = &expr.kind
 +        && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord))
 +        && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind
 +        && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || is_trait_method(cx, receiver, sym::Ord))
 +    {
 +        let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point();
 +        let (min, max) = match (seg_first.ident.as_str(), seg_second.ident.as_str()) {
 +            ("min", "max") => (arg_second, arg_first),
 +            ("max", "min") => (arg_first, arg_second),
 +            _ => return None,
 +        };
 +        Some(ClampSuggestion {
 +            params: InputMinMax { input, min, max, is_float },
 +            span: expr.span,
 +            make_assignment: None,
 +            hir_with_ignore_attr: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min_value, max_value) = (0, -3, 12);
 +/// # use std::cmp::{max, min};
 +/// min(max(input, min_value), max_value)
 +/// # ;
 +/// ```
 +fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    fn segment<'tcx>(cx: &LateContext<'_>, func: &Expr<'tcx>) -> Option<FunctionType<'tcx>> {
 +        match func.kind {
 +            ExprKind::Path(QPath::Resolved(None, path)) => {
 +                let id = path.res.opt_def_id()?;
 +                match cx.tcx.get_diagnostic_name(id) {
 +                    Some(sym::cmp_min) => Some(FunctionType::CmpMin),
 +                    Some(sym::cmp_max) => Some(FunctionType::CmpMax),
 +                    _ if is_diag_trait_item(cx, id, sym::Ord) => {
 +                        Some(FunctionType::OrdOrFloat(path.segments.last().expect("infallible")))
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            ExprKind::Path(QPath::TypeRelative(ty, seg)) => {
 +                matches!(path_res(cx, ty), Res::PrimTy(PrimTy::Float(_))).then(|| FunctionType::OrdOrFloat(seg))
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    enum FunctionType<'tcx> {
 +        CmpMin,
 +        CmpMax,
 +        OrdOrFloat(&'tcx PathSegment<'tcx>),
 +    }
 +
 +    fn check<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        outer_fn: &'tcx Expr<'tcx>,
 +        inner_call: &'tcx Expr<'tcx>,
 +        outer_arg: &'tcx Expr<'tcx>,
 +        span: Span,
 +    ) -> Option<ClampSuggestion<'tcx>> {
 +        if let ExprKind::Call(inner_fn, [first, second]) = &inner_call.kind
 +            && let Some(inner_seg) = segment(cx, inner_fn)
 +            && let Some(outer_seg) = segment(cx, outer_fn)
 +        {
 +            let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) {
 +                (true, false) => (second, first),
 +                (false, true) => (first, second),
 +                _ => return None,
 +            };
 +            let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point();
 +            let (min, max) = match (inner_seg, outer_seg) {
 +                (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg),
 +                (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg),
 +                (FunctionType::OrdOrFloat(first_segment), FunctionType::OrdOrFloat(second_segment)) => {
 +                    match (first_segment.ident.as_str(), second_segment.ident.as_str()) {
 +                        ("min", "max") => (outer_arg, inner_arg),
 +                        ("max", "min") => (inner_arg, outer_arg),
 +                        _ => return None,
 +                    }
 +                }
 +                _ => return None,
 +            };
 +            Some(ClampSuggestion {
 +                params: InputMinMax { input, min, max, is_float },
 +                span,
 +                make_assignment: None,
 +                hir_with_ignore_attr: None,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    if let ExprKind::Call(outer_fn, [first, second]) = &expr.kind {
 +        check(cx, outer_fn, first, second, expr.span).or_else(|| check(cx, outer_fn, second, first, expr.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min, max) = (0, -3, 12);
 +///
 +/// match input {
 +///     input if input > max => max,
 +///     input if input < min => min,
 +///     input => input,
 +/// }
 +/// # ;
 +/// ```
 +fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let ExprKind::Match(value, [first_arm, second_arm, last_arm], rustc_hir::MatchSource::Normal) = &expr.kind {
 +        // Find possible min/max branches
 +        let minmax_values = |a: &'tcx Arm<'tcx>| {
 +            if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
 +            && let Some(Guard::If(e)) = a.guard {
 +                Some((e, var_hir_id, a.body))
 +            } else {
 +                None
 +            }
 +        };
 +        let (first, first_hir_id, first_expr) = minmax_values(first_arm)?;
 +        let (second, second_hir_id, second_expr) = minmax_values(second_arm)?;
 +        let first = BinaryOp::new(first)?;
 +        let second = BinaryOp::new(second)?;
 +        if let PatKind::Binding(_, binding, _, None) = &last_arm.pat.kind
 +            && path_to_local_id(peel_blocks_with_stmt(last_arm.body), *binding)
 +            && last_arm.guard.is_none()
 +        {
 +            // Proceed as normal
 +        } else {
 +            return None;
 +        }
 +        if let Some(params) = is_clamp_meta_pattern(
 +            cx,
 +            &first,
 +            &second,
 +            first_expr,
 +            second_expr,
 +            Some((*first_hir_id, *second_hir_id)),
 +        ) {
 +            return Some(ClampSuggestion {
 +                params: InputMinMax {
 +                    input: value,
 +                    min: params.min,
 +                    max: params.max,
 +                    is_float: params.is_float,
 +                },
 +                span: expr.span,
 +                make_assignment: None,
 +                hir_with_ignore_attr: None,
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (input, min, max) = (0, -3, 12);
 +///
 +/// let mut x = input;
 +/// if x < min { x = min; }
 +/// if x > max { x = max; }
 +/// ```
 +fn is_two_if_pattern<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Vec<ClampSuggestion<'tcx>> {
 +    block_stmt_with_last(block)
 +        .tuple_windows()
 +        .filter_map(|(maybe_set_first, maybe_set_second)| {
 +            if let StmtKind::Expr(first_expr) = *maybe_set_first
 +                && let StmtKind::Expr(second_expr) = *maybe_set_second
 +                && let Some(If { cond: first_cond, then: first_then, r#else: None }) = If::hir(first_expr)
 +                && let Some(If { cond: second_cond, then: second_then, r#else: None }) = If::hir(second_expr)
 +                && let ExprKind::Assign(
 +                    maybe_input_first_path,
 +                    maybe_min_max_first,
 +                    _
 +                ) = peel_blocks_with_stmt(first_then).kind
 +                && let ExprKind::Assign(
 +                    maybe_input_second_path,
 +                    maybe_min_max_second,
 +                    _
 +                ) = peel_blocks_with_stmt(second_then).kind
 +                && eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path)
 +                && let Some(first_bin) = BinaryOp::new(first_cond)
 +                && let Some(second_bin) = BinaryOp::new(second_cond)
 +                && let Some(input_min_max) = is_clamp_meta_pattern(
 +                    cx,
 +                    &first_bin,
 +                    &second_bin,
 +                    maybe_min_max_first,
 +                    maybe_min_max_second,
 +                    None
 +                )
 +            {
 +                Some(ClampSuggestion {
 +                    params: InputMinMax {
 +                        input: maybe_input_first_path,
 +                        min: input_min_max.min,
 +                        max: input_min_max.max,
 +                        is_float: input_min_max.is_float,
 +                    },
 +                    span: first_expr.span.to(second_expr.span),
 +                    make_assignment: Some(maybe_input_first_path),
 +                    hir_with_ignore_attr: Some(first_expr.hir_id()),
 +                })
 +            } else {
 +                None
 +            }
 +        })
 +        .collect()
 +}
 +
 +/// Targets patterns like
 +///
 +/// ```
 +/// # let (mut input, min, max) = (0, -3, 12);
 +///
 +/// if input < min {
 +///     input = min;
 +/// } else if input > max {
 +///     input = max;
 +/// }
 +/// ```
 +fn is_if_elseif_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<ClampSuggestion<'tcx>> {
 +    if let Some(If {
 +        cond,
 +        then,
 +        r#else: Some(else_if),
 +    }) = If::hir(expr)
 +        && let Some(If {
 +            cond: else_if_cond,
 +            then: else_if_then,
 +            r#else: None,
 +        }) = If::hir(peel_blocks(else_if))
 +        && let ExprKind::Assign(
 +            maybe_input_first_path,
 +            maybe_min_max_first,
 +            _
 +        ) = peel_blocks_with_stmt(then).kind
 +        && let ExprKind::Assign(
 +            maybe_input_second_path,
 +            maybe_min_max_second,
 +            _
 +        ) = peel_blocks_with_stmt(else_if_then).kind
 +    {
 +        let params = is_clamp_meta_pattern(
 +            cx,
 +            &BinaryOp::new(peel_blocks(cond))?,
 +            &BinaryOp::new(peel_blocks(else_if_cond))?,
 +            peel_blocks(maybe_min_max_first),
 +            peel_blocks(maybe_min_max_second),
 +            None,
 +        )?;
 +        if !eq_expr_value(cx, maybe_input_first_path, maybe_input_second_path) {
 +            return None;
 +        }
 +        Some(ClampSuggestion {
 +            params,
 +            span: expr.span,
 +            make_assignment: Some(maybe_input_first_path),
 +            hir_with_ignore_attr: None,
 +        })
 +    } else {
 +        None
 +    }
 +}
 +
 +/// `ExprKind::Binary` but more narrowly typed
 +#[derive(Debug, Clone, Copy)]
 +struct BinaryOp<'tcx> {
 +    op: BinOpKind,
 +    left: &'tcx Expr<'tcx>,
 +    right: &'tcx Expr<'tcx>,
 +}
 +
 +impl<'tcx> BinaryOp<'tcx> {
 +    fn new(e: &'tcx Expr<'tcx>) -> Option<BinaryOp<'tcx>> {
 +        match &e.kind {
 +            ExprKind::Binary(op, left, right) => Some(BinaryOp {
 +                op: op.node,
 +                left,
 +                right,
 +            }),
 +            _ => None,
 +        }
 +    }
 +
 +    fn flip(&self) -> Self {
 +        Self {
 +            op: match self.op {
 +                BinOpKind::Le => BinOpKind::Ge,
 +                BinOpKind::Lt => BinOpKind::Gt,
 +                BinOpKind::Ge => BinOpKind::Le,
 +                BinOpKind::Gt => BinOpKind::Lt,
 +                other => other,
 +            },
 +            left: self.right,
 +            right: self.left,
 +        }
 +    }
 +}
 +
 +/// The clamp meta pattern is a pattern shared between many (but not all) patterns.
 +/// In summary, this pattern consists of two if statements that meet many criteria,
 +/// - binary operators that are one of [`>`, `<`, `>=`, `<=`].
 +/// - Both binary statements must have a shared argument
 +///     - Which can appear on the left or right side of either statement
 +///     - The binary operators must define a finite range for the shared argument. To put this in
 +///       the terms of Rust `std` library, the following ranges are acceptable
 +///         - `Range`
 +///         - `RangeInclusive`
 +///       And all other range types are not accepted. For the purposes of `clamp` it's irrelevant
 +///       whether the range is inclusive or not, the output is the same.
 +/// - The result of each if statement must be equal to the argument unique to that if statement. The
 +///   result can not be the shared argument in either case.
 +fn is_clamp_meta_pattern<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    first_bin: &BinaryOp<'tcx>,
 +    second_bin: &BinaryOp<'tcx>,
 +    first_expr: &'tcx Expr<'tcx>,
 +    second_expr: &'tcx Expr<'tcx>,
 +    // This parameters is exclusively for the match pattern.
 +    // It exists because the variable bindings used in that pattern
 +    // refer to the variable bound in the match arm, not the variable
 +    // bound outside of it. Fortunately due to context we know this has to
 +    // be the input variable, not the min or max.
 +    input_hir_ids: Option<(HirId, HirId)>,
 +) -> Option<InputMinMax<'tcx>> {
 +    fn check<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        first_bin: &BinaryOp<'tcx>,
 +        second_bin: &BinaryOp<'tcx>,
 +        first_expr: &'tcx Expr<'tcx>,
 +        second_expr: &'tcx Expr<'tcx>,
 +        input_hir_ids: Option<(HirId, HirId)>,
 +        is_float: bool,
 +    ) -> Option<InputMinMax<'tcx>> {
 +        match (&first_bin.op, &second_bin.op) {
 +            (BinOpKind::Ge | BinOpKind::Gt, BinOpKind::Le | BinOpKind::Lt) => {
 +                let (min, max) = (second_expr, first_expr);
 +                let refers_to_input = match input_hir_ids {
 +                    Some((first_hir_id, second_hir_id)) => {
 +                        path_to_local_id(peel_blocks(first_bin.left), first_hir_id)
 +                            && path_to_local_id(peel_blocks(second_bin.left), second_hir_id)
 +                    },
 +                    None => eq_expr_value(cx, first_bin.left, second_bin.left),
 +                };
 +                (refers_to_input
 +                    && eq_expr_value(cx, first_bin.right, first_expr)
 +                    && eq_expr_value(cx, second_bin.right, second_expr))
 +                .then_some(InputMinMax {
 +                    input: first_bin.left,
 +                    min,
 +                    max,
 +                    is_float,
 +                })
 +            },
 +            _ => None,
 +        }
 +    }
 +    // First filter out any expressions with side effects
 +    let exprs = [
 +        first_bin.left,
 +        first_bin.right,
 +        second_bin.left,
 +        second_bin.right,
 +        first_expr,
 +        second_expr,
 +    ];
 +    let clampability = TypeClampability::is_clampable(cx, cx.typeck_results().expr_ty(first_expr))?;
 +    let is_float = clampability.is_float();
 +    if exprs.iter().any(|e| peel_blocks(e).can_have_side_effects()) {
 +        return None;
 +    }
 +    if !(is_ord_op(first_bin.op) && is_ord_op(second_bin.op)) {
 +        return None;
 +    }
 +    let cases = [
 +        (*first_bin, *second_bin),
 +        (first_bin.flip(), second_bin.flip()),
 +        (first_bin.flip(), *second_bin),
 +        (*first_bin, second_bin.flip()),
 +    ];
 +
 +    cases.into_iter().find_map(|(first, second)| {
 +        check(cx, &first, &second, first_expr, second_expr, input_hir_ids, is_float).or_else(|| {
 +            check(
 +                cx,
 +                &second,
 +                &first,
 +                second_expr,
 +                first_expr,
 +                input_hir_ids.map(|(l, r)| (r, l)),
 +                is_float,
 +            )
 +        })
 +    })
 +}
 +
 +fn block_stmt_with_last<'tcx>(block: &'tcx Block<'tcx>) -> impl Iterator<Item = MaybeBorrowedStmtKind<'tcx>> {
 +    block
 +        .stmts
 +        .iter()
 +        .map(|s| MaybeBorrowedStmtKind::Borrowed(&s.kind))
 +        .chain(
 +            block
 +                .expr
 +                .as_ref()
 +                .map(|e| MaybeBorrowedStmtKind::Owned(StmtKind::Expr(e))),
 +        )
 +}
 +
 +fn is_ord_op(op: BinOpKind) -> bool {
 +    matches!(op, BinOpKind::Ge | BinOpKind::Gt | BinOpKind::Le | BinOpKind::Lt)
 +}
 +
 +/// Really similar to Cow, but doesn't have a `Clone` requirement.
 +#[derive(Debug)]
 +enum MaybeBorrowedStmtKind<'a> {
 +    Borrowed(&'a StmtKind<'a>),
 +    Owned(StmtKind<'a>),
 +}
 +
 +impl<'a> Clone for MaybeBorrowedStmtKind<'a> {
 +    fn clone(&self) -> Self {
 +        match self {
 +            Self::Borrowed(t) => Self::Borrowed(t),
 +            Self::Owned(StmtKind::Expr(e)) => Self::Owned(StmtKind::Expr(e)),
 +            Self::Owned(_) => unreachable!("Owned should only ever contain a StmtKind::Expr."),
 +        }
 +    }
 +}
 +
 +impl<'a> Deref for MaybeBorrowedStmtKind<'a> {
 +    type Target = StmtKind<'a>;
 +
 +    fn deref(&self) -> &Self::Target {
 +        match self {
 +            Self::Borrowed(t) => t,
 +            Self::Owned(t) => t,
 +        }
 +    }
 +}
index bb8c142f8e4680e3b1ccea794cedc942f903d5e3,0000000000000000000000000000000000000000..5ab049d8d133fd34629227da15fbfef84c255915
mode 100644,000000..100644
--- /dev/null
@@@ -1,158 -1,0 +1,155 @@@
- use rustc_semver::RustcVersion;
++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 clippy_utils::{
-     diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
- };
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{def_id::DefId, sym};
 +
-     msrv: Option<RustcVersion>,
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Suggests to use dedicated built-in methods,
 +    /// `is_ascii_(lowercase|uppercase|digit)` for checking on corresponding ascii range
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the built-in functions is more readable and makes it
 +    /// clear that it's not a specific subset of characters, but all
 +    /// ASCII (lowercase|uppercase|digit) characters.
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     assert!(matches!('x', 'a'..='z'));
 +    ///     assert!(matches!(b'X', b'A'..=b'Z'));
 +    ///     assert!(matches!('2', '0'..='9'));
 +    ///     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     assert!('x'.is_ascii_lowercase());
 +    ///     assert!(b'X'.is_ascii_uppercase());
 +    ///     assert!('2'.is_ascii_digit());
 +    ///     assert!('x'.is_ascii_alphabetic());
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub MANUAL_IS_ASCII_CHECK,
 +    style,
 +    "use dedicated method to check ascii range"
 +}
 +impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]);
 +
 +pub struct ManualIsAsciiCheck {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualIsAsciiCheck {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +#[derive(Debug, PartialEq)]
 +enum CharRange {
 +    /// 'a'..='z' | b'a'..=b'z'
 +    LowerChar,
 +    /// 'A'..='Z' | b'A'..=b'Z'
 +    UpperChar,
 +    /// AsciiLower | AsciiUpper
 +    FullChar,
 +    /// '0..=9'
 +    Digit,
 +    Otherwise,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-         if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) {
++        if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) {
 +            return;
 +        }
 +
++        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
 +            return;
 +        }
 +
 +        let Some(macro_call) = root_macro_call(expr.span) else { return };
 +
 +        if is_matches_macro(cx, macro_call.def_id) {
 +            if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
 +                let range = check_pat(&arm.pat.kind);
 +
 +                if let Some(sugg) = match range {
 +                    CharRange::UpperChar => Some("is_ascii_uppercase"),
 +                    CharRange::LowerChar => Some("is_ascii_lowercase"),
 +                    CharRange::FullChar => Some("is_ascii_alphabetic"),
 +                    CharRange::Digit => Some("is_ascii_digit"),
 +                    CharRange::Otherwise => None,
 +                } {
 +                    let default_snip = "..";
 +                    // `snippet_with_applicability` may set applicability to `MaybeIncorrect` for
 +                    // macro span, so we check applicability manually by comparing `recv` is not default.
 +                    let recv = snippet(cx, recv.span, default_snip);
 +
 +                    let applicability = if recv == default_snip {
 +                        Applicability::HasPlaceholders
 +                    } else {
 +                        Applicability::MachineApplicable
 +                    };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        MANUAL_IS_ASCII_CHECK,
 +                        macro_call.span,
 +                        "manual check for common ascii range",
 +                        "try",
 +                        format!("{recv}.{sugg}()"),
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
 +    match pat_kind {
 +        PatKind::Or(pats) => {
 +            let ranges = pats.iter().map(|p| check_pat(&p.kind)).collect::<Vec<_>>();
 +
 +            if ranges.len() == 2 && ranges.contains(&CharRange::UpperChar) && ranges.contains(&CharRange::LowerChar) {
 +                CharRange::FullChar
 +            } else {
 +                CharRange::Otherwise
 +            }
 +        },
 +        PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end),
 +        _ => CharRange::Otherwise,
 +    }
 +}
 +
 +fn check_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange {
 +    if let ExprKind::Lit(start_lit) = &start.kind
 +        && let ExprKind::Lit(end_lit) = &end.kind {
 +        match (&start_lit.node, &end_lit.node) {
 +            (Char('a'), Char('z')) | (Byte(b'a'), Byte(b'z')) => CharRange::LowerChar,
 +            (Char('A'), Char('Z')) | (Byte(b'A'), Byte(b'Z')) => CharRange::UpperChar,
 +            (Char('0'), Char('9')) | (Byte(b'0'), Byte(b'9')) => CharRange::Digit,
 +            _ => CharRange::Otherwise,
 +        }
 +    } else {
 +        CharRange::Otherwise
 +    }
 +}
 +
 +fn is_matches_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
 +    if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
 +        return sym::matches_macro == name;
 +    }
 +
 +    false
 +}
index 1846596fa4c8e259ab6f0ffb580807a47de5381b,0000000000000000000000000000000000000000..874d36ca9f4e378704019c8ae971c046c68d898c
mode 100644,000000..100644
--- /dev/null
@@@ -1,297 -1,0 +1,295 @@@
- use clippy_utils::source::snippet_opt;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::IfLetOrMatch;
- use clippy_utils::{meets_msrv, msrvs, peel_blocks};
++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 rustc_semver::RustcVersion;
 +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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +use serde::Deserialize;
 +use std::ops::ControlFlow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Warn of cases where `let...else` could be used
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// `let...else` provides a standard construct for this pattern
 +    /// that people can easily recognize. It's also more compact.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// # let w = Some(0);
 +    /// let v = if let Some(v) = w { v } else { return };
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// # #![feature(let_else)]
 +    /// # fn main () {
 +    /// # let w = Some(0);
 +    /// let Some(v) = w else { return };
 +    /// # }
 +    /// ```
 +    #[clippy::version = "1.67.0"]
 +    pub MANUAL_LET_ELSE,
 +    pedantic,
 +    "manual implementation of a let...else statement"
 +}
 +
 +pub struct ManualLetElse {
-     pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self {
++    msrv: Msrv,
 +    matches_behaviour: MatchLintBehaviour,
 +}
 +
 +impl ManualLetElse {
 +    #[must_use]
-             if meets_msrv(self.msrv, msrvs::LET_ELSE);
++    pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self {
 +        Self {
 +            msrv,
 +            matches_behaviour,
 +        }
 +    }
 +}
 +
 +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! {
-             let app = Applicability::HasPlaceholders;
++            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;
 +            if local.els.is_none();
 +            if local.ty.is_none();
 +            if init.span.ctxt() == stmt.span.ctxt();
 +            if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init);
 +            then {
 +                if_let_or_match
 +            } else {
 +                return;
 +            }
 +        };
 +
 +        match if_let_or_match {
 +            IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => if_chain! {
 +                if expr_is_simple_identity(let_pat, if_then);
 +                if let Some(if_else) = if_else;
 +                if expr_diverges(cx, if_else);
 +                then {
 +                    emit_manual_let_else(cx, stmt.span, if_let_expr, let_pat, if_else);
 +                }
 +            },
 +            IfLetOrMatch::Match(match_expr, arms, source) => {
 +                if self.matches_behaviour == MatchLintBehaviour::Never {
 +                    return;
 +                }
 +                if source != MatchSource::Normal {
 +                    return;
 +                }
 +                // Any other number than two arms doesn't (neccessarily)
 +                // have a trivial mapping to let else.
 +                if arms.len() != 2 {
 +                    return;
 +                }
 +                // Guards don't give us an easy mapping either
 +                if arms.iter().any(|arm| arm.guard.is_some()) {
 +                    return;
 +                }
 +                let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
 +                let diverging_arm_opt = arms
 +                    .iter()
 +                    .enumerate()
 +                    .find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
 +                let Some((idx, diverging_arm)) = diverging_arm_opt else { return; };
 +                let pat_arm = &arms[1 - idx];
 +                if !expr_is_simple_identity(pat_arm.pat, pat_arm.body) {
 +                    return;
 +                }
 +
 +                emit_manual_let_else(cx, stmt.span, match_expr, pat_arm.pat, diverging_arm.body);
 +            },
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: &Pat<'_>, else_body: &Expr<'_>) {
 +    span_lint_and_then(
 +        cx,
 +        MANUAL_LET_ELSE,
 +        span,
 +        "this could be rewritten as `let...else`",
 +        |diag| {
 +            // This is far from perfect, for example there needs to be:
 +            // * mut additions for the bindings
 +            // * renamings of the bindings
 +            // * unused binding collision detection with existing ones
 +            // * putting patterns with at the top level | inside ()
 +            // for this to be machine applicable.
-             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 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);
 +
++            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);
 +        },
 +    );
 +}
 +
 +fn expr_diverges(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
 +    fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
 +        if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) {
 +            return ty.is_never();
 +        }
 +        false
 +    }
 +    // We can't just call is_never on expr and be done, because the type system
 +    // sometimes coerces the ! type to something different before we can get
 +    // our hands on it. So instead, we do a manual search. We do fall back to
 +    // is_never in some places when there is no better alternative.
 +    for_each_expr(expr, |ex| {
 +        match ex.kind {
 +            ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()),
 +            ExprKind::Call(call, _) => {
 +                if is_never(cx, ex) || is_never(cx, call) {
 +                    return ControlFlow::Break(());
 +                }
 +                ControlFlow::Continue(Descend::Yes)
 +            },
 +            ExprKind::MethodCall(..) => {
 +                if is_never(cx, ex) {
 +                    return ControlFlow::Break(());
 +                }
 +                ControlFlow::Continue(Descend::Yes)
 +            },
 +            ExprKind::If(if_expr, if_then, if_else) => {
 +                let else_diverges = if_else.map_or(false, |ex| expr_diverges(cx, ex));
 +                let diverges = expr_diverges(cx, if_expr) || (else_diverges && expr_diverges(cx, if_then));
 +                if diverges {
 +                    return ControlFlow::Break(());
 +                }
 +                ControlFlow::Continue(Descend::No)
 +            },
 +            ExprKind::Match(match_expr, match_arms, _) => {
 +                let diverges = expr_diverges(cx, match_expr)
 +                    || match_arms.iter().all(|arm| {
 +                        let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(cx, g.body()));
 +                        guard_diverges || expr_diverges(cx, arm.body)
 +                    });
 +                if diverges {
 +                    return ControlFlow::Break(());
 +                }
 +                ControlFlow::Continue(Descend::No)
 +            },
 +
 +            // Don't continue into loops or labeled blocks, as they are breakable,
 +            // and we'd have to start checking labels.
 +            ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No),
 +
 +            // Default: descend
 +            _ => ControlFlow::Continue(Descend::Yes),
 +        }
 +    })
 +    .is_some()
 +}
 +
 +fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool {
 +    // Check whether the pattern contains any bindings, as the
 +    // binding might potentially be used in the body.
 +    // TODO: only look for *used* bindings.
 +    let mut has_bindings = false;
 +    pat.each_binding_or_first(&mut |_, _, _, _| has_bindings = true);
 +    if has_bindings {
 +        return false;
 +    }
 +
 +    // If we shouldn't check the types, exit early.
 +    if !check_types {
 +        return true;
 +    }
 +
 +    // Check whether any possibly "unknown" patterns are included,
 +    // because users might not know which values some enum has.
 +    // Well-known enums are excepted, as we assume people know them.
 +    // We do a deep check, to be able to disallow Err(En::Foo(_))
 +    // for usage of the En::Foo variant, as we disallow En::Foo(_),
 +    // but we allow Err(_).
 +    let typeck_results = cx.typeck_results();
 +    let mut has_disallowed = false;
 +    pat.walk_always(|pat| {
 +        // Only do the check if the type is "spelled out" in the pattern
 +        if !matches!(
 +            pat.kind,
 +            PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..)
 +        ) {
 +            return;
 +        };
 +        let ty = typeck_results.pat_ty(pat);
 +        // Option and Result are allowed, everything else isn't.
 +        if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) {
 +            has_disallowed = true;
 +        }
 +    });
 +    !has_disallowed
 +}
 +
 +/// Checks if the passed block is a simple identity referring to bindings created by the pattern
 +fn expr_is_simple_identity(pat: &'_ Pat<'_>, expr: &'_ Expr<'_>) -> bool {
 +    // We support patterns with multiple bindings and tuples, like:
 +    //   let ... = if let (Some(foo), bar) = g() { (foo, bar) } else { ... }
 +    let peeled = peel_blocks(expr);
 +    let paths = match peeled.kind {
 +        ExprKind::Tup(exprs) | ExprKind::Array(exprs) => exprs,
 +        ExprKind::Path(_) => std::slice::from_ref(peeled),
 +        _ => return false,
 +    };
 +    let mut pat_bindings = FxHashSet::default();
 +    pat.each_binding_or_first(&mut |_ann, _hir_id, _sp, ident| {
 +        pat_bindings.insert(ident);
 +    });
 +    if pat_bindings.len() < paths.len() {
 +        return false;
 +    }
 +    for path in paths {
 +        if_chain! {
 +            if let ExprKind::Path(QPath::Resolved(_ty, path)) = path.kind;
 +            if let [path_seg] = path.segments;
 +            then {
 +                if !pat_bindings.remove(&path_seg.ident) {
 +                    return false;
 +                }
 +            } else {
 +                return false;
 +            }
 +        }
 +    }
 +    true
 +}
 +
 +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize)]
 +pub enum MatchLintBehaviour {
 +    AllTypes,
 +    WellKnownTypes,
 +    Never,
 +}
index 4877cee0cc1ea8ea8bc3b575cb38272e84a94b17,0000000000000000000000000000000000000000..bca193be9e711a520e3e4774c0c55a6af72559b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,221 -1,0 +1,221 @@@
- use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +use rustc_ast::ast::{self, VisibilityKind};
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::{DefId, LocalDefId};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of the non-exhaustive pattern.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the #[non_exhaustive] attribute expresses better the intent
 +    /// and allows possible optimizations when applied to enums.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    ///     _c: (),
 +    /// }
 +    ///
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    ///     #[doc(hidden)]
 +    ///     _C,
 +    /// }
 +    ///
 +    /// struct T(pub i32, pub i32, ());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[non_exhaustive]
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// struct T(pub i32, pub i32);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_NON_EXHAUSTIVE,
 +    style,
 +    "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 +}
 +
 +#[expect(clippy::module_name_repetitions)]
 +pub struct ManualNonExhaustiveStruct {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualNonExhaustiveStruct {
 +    #[must_use]
-     msrv: Option<RustcVersion>,
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 +
 +#[expect(clippy::module_name_repetitions)]
 +pub struct ManualNonExhaustiveEnum {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
 +    potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 +}
 +
 +impl ManualNonExhaustiveEnum {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self {
 +            msrv,
 +            constructed_enum_variants: FxHashSet::default(),
 +            potential_enums: Vec::new(),
 +        }
 +    }
 +}
 +
 +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;
 +        }
 +
 +        if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
 +            let (fields, delimiter) = match variant_data {
 +                ast::VariantData::Struct(fields, _) => (&**fields, '{'),
 +                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
 +                ast::VariantData::Unit(_) => return,
 +            };
 +            if fields.len() <= 1 {
 +                return;
 +            }
 +            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
 +                VisibilityKind::Public => None,
 +                VisibilityKind::Inherited => Some(Ok(f)),
 +                VisibilityKind::Restricted { .. } => Some(Err(())),
 +            });
 +            if let Some(Ok(field)) = iter.next()
 +                && iter.next().is_none()
 +                && field.ty.kind.is_unit()
 +                && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
 +            {
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_NON_EXHAUSTIVE,
 +                    item.span,
 +                    "this seems like a manual implementation of the non-exhaustive pattern",
 +                    |diag| {
 +                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
 +                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
 +                            && let Some(snippet) = snippet_opt(cx, header_span)
 +                        {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {snippet}"),
 +                                Applicability::Unspecified,
 +                            );
 +                        }
 +                        diag.span_help(field.span, "remove this field");
 +                    }
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
++        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Enum(def, _) = &item.kind
 +            && def.variants.len() > 1
 +        {
 +            let mut iter = def.variants.iter().filter_map(|v| {
 +                let id = cx.tcx.hir().local_def_id(v.hir_id);
 +                (matches!(v.data, hir::VariantData::Unit(..))
 +                    && v.ident.as_str().starts_with('_')
 +                    && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)))
 +                .then_some((id, v.span))
 +            });
 +            if let Some((id, span)) = iter.next()
 +                && iter.next().is_none()
 +            {
 +                self.potential_enums.push((item.owner_id.def_id, id, item.span, span));
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
 +            && let [.., name] = p.segments
 +            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
 +            && name.ident.as_str().starts_with('_')
 +        {
 +            let variant_id = cx.tcx.parent(id);
 +            let enum_id = cx.tcx.parent(variant_id);
 +
 +            self.constructed_enum_variants.insert((enum_id, variant_id));
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        for &(enum_id, _, enum_span, variant_span) in
 +            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
 +                !self
 +                    .constructed_enum_variants
 +                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
 +            })
 +        {
 +            let hir_id = cx.tcx.hir().local_def_id_to_hir_id(enum_id);
 +            span_lint_hir_and_then(
 +                cx,
 +                MANUAL_NON_EXHAUSTIVE,
 +                hir_id,
 +                enum_span,
 +                "this seems like a manual implementation of the non-exhaustive pattern",
 +                |diag| {
 +                    if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
 +                        && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
 +                        && let Some(snippet) = snippet_opt(cx, header_span)
 +                    {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {snippet}"),
 +                                Applicability::Unspecified,
 +                            );
 +                    }
 +                    diag.span_help(variant_span, "remove this variant");
 +                },
 +            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 6f25a2ed8e434f60b11025d4943f6127a9848f1c,0000000000000000000000000000000000000000..8d447c37150b8d2a01f095755e878dbf13740b08
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,123 @@@
- use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation
 +    /// of `x.rem_euclid(4)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's simpler and more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 24;
 +    /// let rem = ((x % 4) + 4) % 4;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: i32 = 24;
 +    /// let rem = x.rem_euclid(4);
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub MANUAL_REM_EUCLID,
 +    complexity,
 +    "manually reimplementing `rem_euclid`"
 +}
 +
 +pub struct ManualRemEuclid {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualRemEuclid {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +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 in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
++        if !self.msrv.meets(msrvs::REM_EUCLID) {
 +            return;
 +        }
 +
++        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
 +            return;
 +        }
 +
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Binary(op1, expr1, right) = expr.kind
 +            && op1.node == BinOpKind::Rem
 +            && let Some(const1) = check_for_unsigned_int_constant(cx, right)
 +            && let ExprKind::Binary(op2, left, right) = expr1.kind
 +            && op2.node == BinOpKind::Add
 +            && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
 +            && let ExprKind::Binary(op3, expr3, right) = expr2.kind
 +            && op3.node == BinOpKind::Rem
 +            && let Some(const3) = check_for_unsigned_int_constant(cx, right)
 +            // Also ensures the const is nonzero since zero can't be a divisor
 +            && const1 == const2 && const2 == const3
 +            && let Some(hir_id) = path_to_local(expr3)
 +            && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) {
 +                // Apply only to params or locals with annotated types
 +                match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +                    Some(Node::Param(..)) => (),
 +                    Some(Node::Local(local)) => {
 +                        let Some(ty) = local.ty else { return };
 +                        if matches!(ty.kind, TyKind::Infer) {
 +                            return;
 +                        }
 +                    }
 +                    _ => return,
 +                };
 +
 +                let mut app = Applicability::MachineApplicable;
 +                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
 +                span_lint_and_sugg(
 +                    cx,
 +                    MANUAL_REM_EUCLID,
 +                    expr.span,
 +                    "manual `rem_euclid` implementation",
 +                    "consider using",
 +                    format!("{rem_of}.rem_euclid({const1})"),
 +                    app,
 +                );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +// Checks if either the left or right expressions can be an unsigned int constant and returns that
 +// constant along with the other expression unchanged if so
 +fn check_for_either_unsigned_int_constant<'a>(
 +    cx: &'a LateContext<'_>,
 +    left: &'a Expr<'_>,
 +    right: &'a Expr<'_>,
 +) -> Option<(u128, &'a Expr<'a>)> {
 +    check_for_unsigned_int_constant(cx, left)
 +        .map(|int_const| (int_const, right))
 +        .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left)))
 +}
 +
 +fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
 +    let Some(int_const) = constant_full_int(cx, cx.typeck_results(), expr) else { return None };
 +    match int_const {
 +        FullInt::S(s) => s.try_into().ok(),
 +        FullInt::U(u) => Some(u),
 +    }
 +}
index d6438ca7fec2aca306b8be7c776eef7fef382b33,0000000000000000000000000000000000000000..c1e6c82487dc54d79b5e86185541108c149af35e
mode 100644,000000..100644
--- /dev/null
@@@ -1,224 -1,0 +1,224 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +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};
-     msrv: Option<RustcVersion>,
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::ExprKind::Assign;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +
 +const ACCEPTABLE_METHODS: [&[&str]; 4] = [
 +    &paths::HASHSET_ITER,
 +    &paths::BTREESET_ITER,
 +    &paths::SLICE_INTO,
 +    &paths::VEC_DEQUE_ITER,
 +];
 +const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 6] = [
 +    (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
 +    (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
 +    (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
 +    (sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
 +    (sym::Vec, None),
 +    (sym::VecDeque, None),
 +];
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for code to be replaced by `.retain()`.
 +    /// ### Why is this bad?
 +    /// `.retain()` is simpler and avoids needless allocation.
 +    /// ### Example
 +    /// ```rust
 +    /// let mut vec = vec![0, 1, 2];
 +    /// vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    /// vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut vec = vec![0, 1, 2];
 +    /// vec.retain(|x| x % 2 == 0);
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub MANUAL_RETAIN,
 +    perf,
 +    "`retain()` is simpler and the same functionalitys"
 +}
 +
 +pub struct ManualRetain {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualRetain {
 +    #[must_use]
-             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);
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualRetain {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if let Some(parent_expr) = get_parent_expr(cx, expr)
 +            && let Assign(left_expr, collect_expr, _) = &parent_expr.kind
 +            && let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind
 +            && seg.args.is_none()
 +            && 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) {
-     msrv: Option<RustcVersion>,
++            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);
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_into_iter(
 +    cx: &LateContext<'_>,
 +    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)
 +        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
 +        && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &into_iter_expr.kind
 +        && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
 +        && Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn()
 +        && match_acceptable_type(cx, left_expr, msrv)
 +        && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
 +        suggest(cx, parent_expr, left_expr, target_expr);
 +    }
 +}
 +
 +fn check_iter(
 +    cx: &LateContext<'_>,
 +    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)
 +        && (match_def_path(cx, copied_def_id, &paths::CORE_ITER_COPIED)
 +            || match_def_path(cx, copied_def_id, &paths::CORE_ITER_CLONED))
 +        && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind
 +        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
 +        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
 +        && let hir::ExprKind::MethodCall(_, struct_expr, [], _) = &iter_expr.kind
 +        && let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
 +        && match_acceptable_def_path(cx, iter_expr_def_id)
 +        && match_acceptable_type(cx, left_expr, msrv)
 +        && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
 +        suggest(cx, parent_expr, left_expr, filter_expr);
 +    }
 +}
 +
 +fn check_to_owned(
 +    cx: &LateContext<'_>,
 +    parent_expr: &hir::Expr<'_>,
 +    left_expr: &hir::Expr<'_>,
 +    target_expr: &hir::Expr<'_>,
-     if meets_msrv(msrv,  msrvs::STRING_RETAIN)
++    msrv: &Msrv,
 +) {
- fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
++    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)
 +        && let hir::ExprKind::MethodCall(_, chars_expr, [_], _) = &filter_expr.kind
 +        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
 +        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
 +        && let hir::ExprKind::MethodCall(_, str_expr, [], _) = &chars_expr.kind
 +        && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id)
 +        && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
 +        && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
 +        && is_type_lang_item(cx, ty, hir::LangItem::String)
 +        && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) {
 +        suggest(cx, parent_expr, left_expr, filter_expr);
 +    }
 +}
 +
 +fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
 +    if let hir::ExprKind::MethodCall(_, _, [closure], _) = filter_expr.kind
 +        && let hir::ExprKind::Closure(&hir::Closure { body, ..}) = closure.kind
 +        && let filter_body = cx.tcx.hir().body(body)
 +        && let [filter_params] = filter_body.params
 +        && let Some(sugg) = match filter_params.pat.kind {
 +            hir::PatKind::Binding(_, _, filter_param_ident, None) => {
 +                Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, "..")))
 +            },
 +            hir::PatKind::Tuple([key_pat, value_pat], _) => {
 +                make_sugg(cx, key_pat, value_pat, left_expr, filter_body)
 +            },
 +            hir::PatKind::Ref(pat, _) => {
 +                match pat.kind {
 +                    hir::PatKind::Binding(_, _, filter_param_ident, None) => {
 +                        Some(format!("{}.retain(|{filter_param_ident}| {})", snippet(cx, left_expr.span, ".."), snippet(cx, filter_body.value.span, "..")))
 +                    },
 +                    _ => None
 +                }
 +            },
 +            _ => None
 +        } {
 +        span_lint_and_sugg(
 +            cx,
 +            MANUAL_RETAIN,
 +            parent_expr.span,
 +            "this expression can be written more simply using `.retain()`",
 +            "consider calling `.retain()` instead",
 +            sugg,
 +            Applicability::MachineApplicable
 +        );
 +    }
 +}
 +
 +fn make_sugg(
 +    cx: &LateContext<'_>,
 +    key_pat: &rustc_hir::Pat<'_>,
 +    value_pat: &rustc_hir::Pat<'_>,
 +    left_expr: &hir::Expr<'_>,
 +    filter_body: &hir::Body<'_>,
 +) -> Option<String> {
 +    match (&key_pat.kind, &value_pat.kind) {
 +        (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Binding(_, _, value_param_ident, None)) => {
 +            Some(format!(
 +                "{}.retain(|{key_param_ident}, &mut {value_param_ident}| {})",
 +                snippet(cx, left_expr.span, ".."),
 +                snippet(cx, filter_body.value.span, "..")
 +            ))
 +        },
 +        (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Wild) => Some(format!(
 +            "{}.retain(|{key_param_ident}, _| {})",
 +            snippet(cx, left_expr.span, ".."),
 +            snippet(cx, filter_body.value.span, "..")
 +        )),
 +        (hir::PatKind::Wild, hir::PatKind::Binding(_, _, value_param_ident, None)) => Some(format!(
 +            "{}.retain(|_, &mut {value_param_ident}| {})",
 +            snippet(cx, left_expr.span, ".."),
 +            snippet(cx, filter_body.value.span, "..")
 +        )),
 +        _ => None,
 +    }
 +}
 +
 +fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> bool {
 +    ACCEPTABLE_METHODS
 +        .iter()
 +        .any(|&method| match_def_path(cx, collect_def_id, method))
 +}
 +
-             && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
++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| msrv.meets(acceptable_msrv))
 +    })
 +}
index 0976940afac355fcc9f9b02f9796cf4262023018,0000000000000000000000000000000000000000..de166b9765f4194b6ff22e909c8a370c3200146d
mode 100644,000000..100644
--- /dev/null
@@@ -1,251 -1,0 +1,251 @@@
- use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths};
 +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 rustc_semver::RustcVersion;
++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;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::BinOpKind;
 +use rustc_hir::{BorrowKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing using
 +    /// the pattern's length.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `str:strip_{prefix,suffix}` is safer and may have better performance as there is no
 +    /// slicing which may panic and the compiler does not need to insert this panic code. It is
 +    /// also sometimes more readable as it removes the need for duplicating or storing the pattern
 +    /// used by `str::{starts,ends}_with` and in the slicing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = "hello, world!";
 +    /// if s.starts_with("hello, ") {
 +    ///     assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let s = "hello, world!";
 +    /// if let Some(end) = s.strip_prefix("hello, ") {
 +    ///     assert_eq!(end.to_uppercase(), "WORLD!");
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub MANUAL_STRIP,
 +    complexity,
 +    "suggests using `strip_{prefix,suffix}` over `str::{starts,ends}_with` and slicing"
 +}
 +
 +pub struct ManualStrip {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl ManualStrip {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualStrip => [MANUAL_STRIP]);
 +
 +#[derive(Clone, Copy, Debug, Eq, PartialEq)]
 +enum StripKind {
 +    Prefix,
 +    Suffix,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
 +            if let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind;
 +            if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
 +            if let ExprKind::Path(target_path) = &target_arg.kind;
 +            then {
 +                let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
 +                    StripKind::Prefix
 +                } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
 +                    StripKind::Suffix
 +                } else {
 +                    return;
 +                };
 +                let target_res = cx.qpath_res(target_path, target_arg.hir_id);
 +                if target_res == Res::Err {
 +                    return;
 +                };
 +
 +                if_chain! {
 +                    if let Res::Local(hir_id) = target_res;
 +                    if let Some(used_mutably) = mutated_variables(then, cx);
 +                    if used_mutably.contains(&hir_id);
 +                    then {
 +                        return;
 +                    }
 +                }
 +
 +                let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
 +                if !strippings.is_empty() {
 +
 +                    let kind_word = match strip_kind {
 +                        StripKind::Prefix => "prefix",
 +                        StripKind::Suffix => "suffix",
 +                    };
 +
 +                    let test_span = expr.span.until(then.span);
 +                    span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| {
 +                        diag.span_note(test_span, &format!("the {kind_word} was tested here"));
 +                        multispan_sugg(
 +                            diag,
 +                            &format!("try using the `strip_{kind_word}` method"),
 +                            vec![(test_span,
 +                                  format!("if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
 +                                          snippet(cx, target_arg.span, ".."),
 +                                          snippet(cx, pattern.span, "..")))]
 +                            .into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
 +                        );
 +                    });
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
 +fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(_, arg, [], _) = expr.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if match_def_path(cx, method_def_id, &paths::STR_LEN);
 +        then {
 +            Some(arg)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +// Returns the length of the `expr` if it's a constant string or char.
 +fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
 +    let (value, _) = constant(cx, cx.typeck_results(), expr)?;
 +    match value {
 +        Constant::Str(value) => Some(value.len() as u128),
 +        Constant::Char(value) => Some(value.len_utf8() as u128),
 +        _ => None,
 +    }
 +}
 +
 +// Tests if `expr` equals the length of the pattern.
 +fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
 +    if let ExprKind::Lit(Spanned {
 +        node: LitKind::Int(n, _),
 +        ..
 +    }) = expr.kind
 +    {
 +        constant_length(cx, pattern).map_or(false, |length| length == n)
 +    } else {
 +        len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
 +    }
 +}
 +
 +// Tests if `expr` is a `&str`.
 +fn is_ref_str(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    match cx.typeck_results().expr_ty_adjusted(expr).kind() {
 +        ty::Ref(_, ty, _) => ty.is_str(),
 +        _ => false,
 +    }
 +}
 +
 +// Removes the outer `AddrOf` expression if needed.
 +fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
 +    if let ExprKind::AddrOf(BorrowKind::Ref, _, unref) = &expr.kind {
 +        unref
 +    } else {
 +        expr
 +    }
 +}
 +
 +// Find expressions where `target` is stripped using the length of `pattern`.
 +// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
 +// method.
 +fn find_stripping<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    strip_kind: StripKind,
 +    target: Res,
 +    pattern: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) -> Vec<Span> {
 +    struct StrippingFinder<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        strip_kind: StripKind,
 +        target: Res,
 +        pattern: &'tcx Expr<'tcx>,
 +        results: Vec<Span>,
 +    }
 +
 +    impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
 +        fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
 +            if_chain! {
 +                if is_ref_str(self.cx, ex);
 +                let unref = peel_ref(ex);
 +                if let ExprKind::Index(indexed, index) = &unref.kind;
 +                if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index);
 +                if let ExprKind::Path(path) = &indexed.kind;
 +                if self.cx.qpath_res(path, ex.hir_id) == self.target;
 +                then {
 +                    match (self.strip_kind, start, end) {
 +                        (StripKind::Prefix, Some(start), None) => {
 +                            if eq_pattern_length(self.cx, self.pattern, start) {
 +                                self.results.push(ex.span);
 +                                return;
 +                            }
 +                        },
 +                        (StripKind::Suffix, None, Some(end)) => {
 +                            if_chain! {
 +                                if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind;
 +                                if let Some(left_arg) = len_arg(self.cx, left);
 +                                if let ExprKind::Path(left_path) = &left_arg.kind;
 +                                if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target;
 +                                if eq_pattern_length(self.cx, self.pattern, right);
 +                                then {
 +                                    self.results.push(ex.span);
 +                                    return;
 +                                }
 +                            }
 +                        },
 +                        _ => {}
 +                    }
 +                }
 +            }
 +
 +            walk_expr(self, ex);
 +        }
 +    }
 +
 +    let mut finder = StrippingFinder {
 +        cx,
 +        strip_kind,
 +        target,
 +        pattern,
 +        results: vec![],
 +    };
 +    walk_expr(&mut finder, expr);
 +    finder.results
 +}
index 7d8171ead89e10787dee90e48d11a7e33b527a6a,0000000000000000000000000000000000000000..7b15a307fecf47d66a50a013117e83eabd28602c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1174 -1,0 +1,1172 @@@
- use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs};
 +mod collapsible_match;
 +mod infallible_destructuring_match;
 +mod manual_filter;
 +mod manual_map;
 +mod manual_unwrap_or;
 +mod manual_utils;
 +mod match_as_ref;
 +mod match_bool;
 +mod match_like_matches;
 +mod match_on_vec_items;
 +mod match_ref_pats;
 +mod match_same_arms;
 +mod match_single_binding;
 +mod match_str_case_mismatch;
 +mod match_wild_enum;
 +mod match_wild_err_arm;
 +mod needless_match;
 +mod overlapping_arms;
 +mod redundant_pattern_match;
 +mod rest_pat_in_fully_bound_struct;
 +mod significant_drop_in_scrutinee;
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, SpanData, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with a single arm where an `if let`
 +    /// will usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn bar(stool: &str) {}
 +    /// # let x = Some("abc");
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn bar(stool: &str) {}
 +    /// # let x = Some("abc");
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_MATCH,
 +    style,
 +    "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with two arms where an `if let else` will
 +    /// usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Known problems
 +    /// Personal style preferences may differ.
 +    ///
 +    /// ### Example
 +    /// Using `match`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => bar(&other_ref),
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `if let` with `else`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// } else {
 +    ///     bar(&other_ref);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_MATCH_ELSE,
 +    pedantic,
 +    "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where all arms match a reference,
 +    /// suggesting to remove the reference and deref the matched expression
 +    /// instead. It also checks for `if let &foo = bar` blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// It just makes the code less readable. That reference
 +    /// destructuring adds nothing to the code.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// match x {
 +    ///     &A(ref y) => foo(y),
 +    ///     &B => bar(),
 +    ///     _ => frob(&x),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// match *x {
 +    ///     A(ref y) => foo(y),
 +    ///     B => bar(),
 +    ///     _ => frob(x),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_REF_PATS,
 +    style,
 +    "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where match expression is a `bool`. It
 +    /// suggests to replace the expression with an `if...else` block.
 +    ///
 +    /// ### Why is this bad?
 +    /// It makes the code less readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// match condition {
 +    ///     true => foo(),
 +    ///     false => bar(),
 +    /// }
 +    /// ```
 +    /// Use if/else instead:
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// if condition {
 +    ///     foo();
 +    /// } else {
 +    ///     bar();
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_BOOL,
 +    pedantic,
 +    "a `match` on a boolean expression instead of an `if..else` block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for overlapping match arms.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is likely to be an error and if not, makes the code
 +    /// less obvious.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 5;
 +    /// match x {
 +    ///     1..=10 => println!("1 ... 10"),
 +    ///     5..=15 => println!("5 ... 15"),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_OVERLAPPING_ARM,
 +    style,
 +    "a `match` with overlapping arms"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arm which matches all errors with `Err(_)`
 +    /// and take drastic actions like `panic!`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is generally a bad practice, similar to
 +    /// catching all exceptions in java with `catch(Exception)`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Result<i32, &str> = Ok(3);
 +    /// match x {
 +    ///     Ok(_) => println!("ok"),
 +    ///     Err(_) => panic!("err"),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_WILD_ERR_ARM,
 +    pedantic,
 +    "a `match` with `Err(_)` arm and take drastic actions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for match which is used to add a reference to an
 +    /// `Option` value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `as_ref()` or `as_mut()` instead is shorter.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Option<()> = None;
 +    ///
 +    /// let r: Option<&()> = match x {
 +    ///     None => None,
 +    ///     Some(ref v) => Some(v),
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: Option<()> = None;
 +    ///
 +    /// let r: Option<&()> = x.as_ref();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_AS_REF,
 +    complexity,
 +    "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches using `_`.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may be incorrect if guards exhaustively cover some
 +    /// variants, and also may not use correct path to enum if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A(usize), B(usize) }
 +    /// # let x = Foo::B(1);
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # enum Foo { A(usize), B(usize) }
 +    /// # let x = Foo::B(1);
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     Foo::B(_) => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub WILDCARD_ENUM_MATCH_ARM,
 +    restriction,
 +    "a wildcard enum match arm using `_`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches for a single variant.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may not use correct path to enum
 +    /// if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A, B, C }
 +    /// # let x = Foo::B;
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # enum Foo { A, B, C }
 +    /// # let x = Foo::B;
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     Foo::C => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    pedantic,
 +    "a wildcard enum match for a single variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard pattern used with others patterns in same match arm.
 +    ///
 +    /// ### Why is this bad?
 +    /// Wildcard pattern already covers any other pattern as it will match anyway.
 +    /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let s = "foo";
 +    /// match s {
 +    ///     "a" => {},
 +    ///     "bar" | _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let s = "foo";
 +    /// match s {
 +    ///     "a" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub WILDCARD_IN_OR_PATTERNS,
 +    complexity,
 +    "a wildcard pattern used with others patterns in same match arm"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches being used to destructure a single-variant enum
 +    /// or tuple struct where a `let` will suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `let` doesn't nest, whereas a `match` does.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    ///
 +    /// let data = match wrapper {
 +    ///     Wrapper::Data(i) => i,
 +    /// };
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    /// let Wrapper::Data(data) = wrapper;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INFALLIBLE_DESTRUCTURING_MATCH,
 +    style,
 +    "a `match` statement with a single infallible arm instead of a `let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for useless match that binds to only one value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    ///  Suggested replacements may be incorrect when `match`
 +    /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = 1;
 +    /// # let b = 2;
 +    /// match (a, b) {
 +    ///     (c, d) => {
 +    ///         // useless match
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let a = 1;
 +    /// # let b = 2;
 +    /// let (c, d) = (a, b);
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub MATCH_SINGLE_BINDING,
 +    complexity,
 +    "a match with a single binding instead of using `let` statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
 +    ///
 +    /// ### Why is this bad?
 +    /// Correctness and readability. It's like having a wildcard pattern after
 +    /// matching all enum variants explicitly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A { a: i32 }
 +    /// let a = A { a: 5 };
 +    ///
 +    /// match a {
 +    ///     A { a: 5, .. } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A { a: i32 }
 +    /// # let a = A { a: 5 };
 +    /// match a {
 +    ///     A { a: 5 } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    restriction,
 +    "a match on a struct that binds all fields but still uses the wildcard pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lint for redundant pattern matching over `Result`, `Option`,
 +    /// `std::task::Poll` or `std::net::IpAddr`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more concise and clear to just use the proper
 +    /// utility function
 +    ///
 +    /// ### Known problems
 +    /// This will change the drop order for the matched type. Both `if let` and
 +    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
 +    /// value before entering the block. For most types this change will not matter, but for a few
 +    /// types this will not be an acceptable change (e.g. locks). See the
 +    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
 +    /// drop order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if let Ok(_) = Ok::<i32, i32>(42) {}
 +    /// if let Err(_) = Err::<i32, i32>(42) {}
 +    /// if let None = None::<()> {}
 +    /// if let Some(_) = Some(42) {}
 +    /// if let Poll::Pending = Poll::Pending::<()> {}
 +    /// if let Poll::Ready(_) = Poll::Ready(42) {}
 +    /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
 +    /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
 +    /// match Ok::<i32, i32>(42) {
 +    ///     Ok(_) => true,
 +    ///     Err(_) => false,
 +    /// };
 +    /// ```
 +    ///
 +    /// The more idiomatic use would be:
 +    ///
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if Ok::<i32, i32>(42).is_ok() {}
 +    /// if Err::<i32, i32>(42).is_err() {}
 +    /// if None::<()>.is_none() {}
 +    /// if Some(42).is_some() {}
 +    /// if Poll::Pending::<()>.is_pending() {}
 +    /// if Poll::Ready(42).is_ready() {}
 +    /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
 +    /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
 +    /// Ok::<i32, i32>(42).is_ok();
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub REDUNDANT_PATTERN_MATCHING,
 +    style,
 +    "use the proper utility function avoiding an `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match`  or `if let` expressions producing a
 +    /// `bool` that could be written using `matches!`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    /// This lint falsely triggers, if there are arms with
 +    /// `cfg` attributes that remove an arm evaluating to `false`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(5);
 +    ///
 +    /// let a = match x {
 +    ///     Some(0) => true,
 +    ///     _ => false,
 +    /// };
 +    ///
 +    /// let a = if let Some(0) = x {
 +    ///     true
 +    /// } else {
 +    ///     false
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(5);
 +    /// let a = matches!(x, Some(0));
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MATCH_LIKE_MATCHES_MACRO,
 +    style,
 +    "a match that could be written with the matches! macro"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` with identical arm bodies.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error. If arm bodies
 +    /// are the same on purpose, you can factor them
 +    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
 +    ///
 +    /// ### Known problems
 +    /// False positive possible with order dependent `match`
 +    /// (see issue
 +    /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => bar(), // <= oops
 +    /// }
 +    /// ```
 +    ///
 +    /// This should probably be
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => baz(), // <= fixed
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo:
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar | Baz => bar(), // <= shows the intent better
 +    ///     Quz => quz(),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_SAME_ARMS,
 +    pedantic,
 +    "`match` with identical arm bodies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
 +    /// when function signatures are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// This `match` block does nothing and might not be what the coder intended.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     match result {
 +    ///         Ok(val) => Ok(val),
 +    ///         Err(err) => Err(err),
 +    ///     }
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     if let Some(val) = option {
 +    ///         Some(val)
 +    ///     } else {
 +    ///         None
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be replaced as
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     result
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     option
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub NEEDLESS_MATCH,
 +    complexity,
 +    "`match` or match-like `if let` that are unnecessary"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
 +    /// without adding any branches.
 +    ///
 +    /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
 +    /// cases where merging would most likely make the code more readable.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is unnecessarily verbose and complex.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn func(opt: Option<Result<u64, String>>) {
 +    ///     let n = match opt {
 +    ///         Some(n) => match n {
 +    ///             Ok(n) => n,
 +    ///             _ => return,
 +    ///         }
 +    ///         None => return,
 +    ///     };
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn func(opt: Option<Result<u64, String>>) {
 +    ///     let n = match opt {
 +    ///         Some(Ok(n)) => n,
 +    ///         _ => return,
 +    ///     };
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub COLLAPSIBLE_MATCH,
 +    style,
 +    "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Concise code helps focusing on behavior instead of boilerplate.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// match foo {
 +    ///     Some(v) => v,
 +    ///     None => 1,
 +    /// };
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.unwrap_or(1);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_UNWRAP_OR,
 +    complexity,
 +    "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match vec[idx]` or `match vec[n..m]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This can panic at runtime.
 +    ///
 +    /// ### Example
 +    /// ```rust, no_run
 +    /// let arr = vec![0, 1, 2, 3];
 +    /// let idx = 1;
 +    ///
 +    /// match arr[idx] {
 +    ///     0 => println!("{}", 0),
 +    ///     1 => println!("{}", 3),
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust, no_run
 +    /// let arr = vec![0, 1, 2, 3];
 +    /// let idx = 1;
 +    ///
 +    /// match arr.get(idx) {
 +    ///     Some(0) => println!("{}", 0),
 +    ///     Some(1) => println!("{}", 3),
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MATCH_ON_VEC_ITEMS,
 +    pedantic,
 +    "matching on vector elements can panic"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
 +    ///
 +    /// ### Why is this bad?
 +    /// The arm is unreachable, which is likely a mistake
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let text = "Foo";
 +    /// match &*text.to_ascii_lowercase() {
 +    ///     "foo" => {},
 +    ///     "Bar" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let text = "Foo";
 +    /// match &*text.to_ascii_lowercase() {
 +    ///     "foo" => {},
 +    ///     "bar" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub MATCH_STR_CASE_MISMATCH,
 +    correctness,
 +    "creation of a case altering match expression with non-compliant arms"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for temporaries returned from function calls in a match scrutinee that have the
 +    /// `clippy::has_significant_drop` attribute.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
 +    /// an important side-effect, such as unlocking a mutex, making it important for users to be
 +    /// able to accurately understand their lifetimes. When a temporary is returned in a function
 +    /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
 +    /// be surprising.
 +    ///
 +    /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
 +    /// function call that returns a `MutexGuard` and then tries to lock again in one of the match
 +    /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
 +    /// the match block and thus will not unlock.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # use std::sync::Mutex;
 +    /// # struct State {}
 +    /// # impl State {
 +    /// #     fn foo(&self) -> bool {
 +    /// #         true
 +    /// #     }
 +    /// #     fn bar(&self) {}
 +    /// # }
 +    /// let mutex = Mutex::new(State {});
 +    ///
 +    /// match mutex.lock().unwrap().foo() {
 +    ///     true => {
 +    ///         mutex.lock().unwrap().bar(); // Deadlock!
 +    ///     }
 +    ///     false => {}
 +    /// };
 +    ///
 +    /// println!("All done!");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # struct State {}
 +    /// # impl State {
 +    /// #     fn foo(&self) -> bool {
 +    /// #         true
 +    /// #     }
 +    /// #     fn bar(&self) {}
 +    /// # }
 +    /// let mutex = Mutex::new(State {});
 +    ///
 +    /// let is_foo = mutex.lock().unwrap().foo();
 +    /// match is_foo {
 +    ///     true => {
 +    ///         mutex.lock().unwrap().bar();
 +    ///     }
 +    ///     false => {}
 +    /// };
 +    ///
 +    /// println!("All done!");
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    nursery,
 +    "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Err(x)?`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `?` operator is designed to allow calls that
 +    /// can fail to be easily chained. For example, `foo()?.bar()` or
 +    /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
 +    /// always return), it is more clear to write `return Err(x)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(fail: bool) -> Result<i32, String> {
 +    ///     if fail {
 +    ///       Err("failed")?;
 +    ///     }
 +    ///     Ok(0)
 +    /// }
 +    /// ```
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// fn foo(fail: bool) -> Result<i32, String> {
 +    ///     if fail {
 +    ///       return Err("failed".into());
 +    ///     }
 +    ///     Ok(0)
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.38.0"]
 +    pub TRY_ERR,
 +    restriction,
 +    "return errors explicitly rather than hiding them behind a `?`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `match` which could be implemented using `map`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the `map` method is clearer and more concise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// match Some(0) {
 +    ///     Some(x) => Some(x + 1),
 +    ///     None => None,
 +    /// };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// Some(0).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub MANUAL_MAP,
 +    style,
 +    "reimplementation of `map`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `match` which could be implemented using `filter`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the `filter` method is clearer and more concise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// match Some(0) {
 +    ///     Some(x) => if x % 2 == 0 {
 +    ///                     Some(x)
 +    ///                } else {
 +    ///                     None
 +    ///                 },
 +    ///     None => None,
 +    /// };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// Some(0).filter(|&x| x % 2 == 0);
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub MANUAL_FILTER,
 +    complexity,
 +    "reimplentation of `filter`"
 +}
 +
 +#[derive(Default)]
 +pub struct Matches {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +    infallible_destructuring_match_linted: bool,
 +}
 +
 +impl Matches {
 +    #[must_use]
-                     if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
-                         && match_like_matches::check_match(cx, expr, ex, arms))
-                     {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self {
 +            msrv,
 +            ..Matches::default()
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Matches => [
 +    SINGLE_MATCH,
 +    MATCH_REF_PATS,
 +    MATCH_BOOL,
 +    SINGLE_MATCH_ELSE,
 +    MATCH_OVERLAPPING_ARM,
 +    MATCH_WILD_ERR_ARM,
 +    MATCH_AS_REF,
 +    WILDCARD_ENUM_MATCH_ARM,
 +    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    WILDCARD_IN_OR_PATTERNS,
 +    MATCH_SINGLE_BINDING,
 +    INFALLIBLE_DESTRUCTURING_MATCH,
 +    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    REDUNDANT_PATTERN_MATCHING,
 +    MATCH_LIKE_MATCHES_MACRO,
 +    MATCH_SAME_ARMS,
 +    NEEDLESS_MATCH,
 +    COLLAPSIBLE_MATCH,
 +    MANUAL_UNWRAP_OR,
 +    MATCH_ON_VEC_ITEMS,
 +    MATCH_STR_CASE_MISMATCH,
 +    SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    TRY_ERR,
 +    MANUAL_MAP,
 +    MANUAL_FILTER,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Matches {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +        let from_expansion = expr.span.from_expansion();
 +
 +        if let ExprKind::Match(ex, arms, source) = expr.kind {
 +            if source == MatchSource::Normal && !is_span_match(cx, expr.span) {
 +                return;
 +            }
 +            if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
 +                significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
 +            }
 +
 +            collapsible_match::check_match(cx, arms);
 +            if !from_expansion {
 +                // These don't depend on a relationship between multiple arms
 +                match_wild_err_arm::check(cx, ex, arms);
 +                wild_in_or_pats::check(cx, arms);
 +            }
 +
 +            if source == MatchSource::TryDesugar {
 +                try_err::check(cx, expr, ex);
 +            }
 +
 +            if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
 +                if source == MatchSource::Normal {
-                     if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
++                    if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) {
 +                        match_same_arms::check(cx, arms);
 +                    }
 +
 +                    redundant_pattern_match::check_match(cx, expr, ex, arms);
 +                    single_match::check(cx, ex, arms, expr);
 +                    match_bool::check(cx, ex, arms, expr);
 +                    overlapping_arms::check(cx, ex, arms);
 +                    match_wild_enum::check(cx, ex, arms);
 +                    match_as_ref::check(cx, ex, arms, expr);
 +                    needless_match::check_match(cx, ex, arms, expr);
 +                    match_on_vec_items::check(cx, ex);
 +                    match_str_case_mismatch::check(cx, ex, arms);
 +
 +                    if !in_constant(cx, expr.hir_id) {
 +                        manual_unwrap_or::check(cx, expr, ex, arms);
 +                        manual_map::check_match(cx, expr, ex, arms);
 +                        manual_filter::check_match(cx, ex, arms, expr);
 +                    }
 +
 +                    if self.infallible_destructuring_match_linted {
 +                        self.infallible_destructuring_match_linted = false;
 +                    } else {
 +                        match_single_binding::check(cx, ex, arms, expr);
 +                    }
 +                }
 +                match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
 +            }
 +        } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
 +            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 self.msrv.meets(msrvs::MATCHES_MACRO) {
 +                        match_like_matches::check_if_let(
 +                            cx,
 +                            expr,
 +                            if_let.let_pat,
 +                            if_let.let_expr,
 +                            if_let.if_then,
 +                            else_expr,
 +                        );
 +                    }
 +                    if !in_constant(cx, expr.hir_id) {
 +                        manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr);
 +                        manual_filter::check_if_let(
 +                            cx,
 +                            expr,
 +                            if_let.let_pat,
 +                            if_let.let_expr,
 +                            if_let.if_then,
 +                            else_expr,
 +                        );
 +                    }
 +                }
 +                redundant_pattern_match::check_if_let(
 +                    cx,
 +                    expr,
 +                    if_let.let_pat,
 +                    if_let.let_expr,
 +                    if_let.if_else.is_some(),
 +                );
 +                needless_match::check_if_let(cx, expr, &if_let);
 +            }
 +        } else if !from_expansion {
 +            redundant_pattern_match::check(cx, expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 +        self.infallible_destructuring_match_linted |=
 +            local.els.is_none() && infallible_destructuring_match::check(cx, local);
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        rest_pat_in_fully_bound_struct::check(cx, pat);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Checks if there are any arms with a `#[cfg(..)]` attribute.
 +fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
 +    let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else {
 +        // Shouldn't happen, but treat this as though a `cfg` attribute were found
 +        return true;
 +    };
 +
 +    let start = scrutinee_span.hi();
 +    let mut arm_spans = arms.iter().map(|arm| {
 +        let data = arm.span.data();
 +        (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi))
 +    });
 +    let end = e.span.hi();
 +
 +    // Walk through all the non-code space before each match arm. The space trailing the final arm is
 +    // handled after the `try_fold` e.g.
 +    //
 +    // match foo {
 +    // _________^-                      everything between the scrutinee and arm1
 +    //|    arm1 => (),
 +    //|---^___________^                 everything before arm2
 +    //|    #[cfg(feature = "enabled")]
 +    //|    arm2 => some_code(),
 +    //|---^____________________^        everything before arm3
 +    //|    // some comment about arm3
 +    //|    arm3 => some_code(),
 +    //|---^____________________^        everything after arm3
 +    //|    #[cfg(feature = "disabled")]
 +    //|    arm4 = some_code(),
 +    //|};
 +    //|^
 +    let found = arm_spans.try_fold(start, |start, range| {
 +        let Some((end, next_start)) = range else {
 +            // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute were
 +            // found.
 +            return Err(());
 +        };
 +        let span = SpanData {
 +            lo: start,
 +            hi: end,
 +            ctxt: SyntaxContext::root(),
 +            parent: None,
 +        }
 +        .span();
 +        (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(())
 +    });
 +    match found {
 +        Ok(start) => {
 +            let span = SpanData {
 +                lo: start,
 +                hi: end,
 +                ctxt: SyntaxContext::root(),
 +                parent: None,
 +            }
 +            .span();
 +            span_contains_cfg(cx, span)
 +        },
 +        Err(()) => true,
 +    }
 +}
 +
 +/// Checks if the given span contains a `#[cfg(..)]` attribute
 +fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
 +    let Some(snip) = snippet_opt(cx, s) else {
 +        // Assume true. This would require either an invalid span, or one which crosses file boundaries.
 +        return true;
 +    };
 +    let mut pos = 0usize;
 +    let mut iter = tokenize(&snip).map(|t| {
 +        let start = pos;
 +        pos += t.len as usize;
 +        (t.kind, start..pos)
 +    });
 +
 +    // Search for the token sequence [`#`, `[`, `cfg`]
 +    while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
 +        let mut iter = iter.by_ref().skip_while(|(t, _)| {
 +            matches!(
 +                t,
 +                TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
 +            )
 +        });
 +        if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
 +            && matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg")
 +        {
 +            return true;
 +        }
 +    }
 +    false
 +}
index c6cba81d8718972e89d436f83a3b4ef2facd37e2,0000000000000000000000000000000000000000..704c34c32bf743b39842571202e1114e5b28a750
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,142 @@@
- use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
-         if match_def_path(cx, def.did(), &paths::POLL);
++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;
 +use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::{hygiene, sym};
 +
 +use super::TRY_ERR;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>) {
 +    // Looks for a structure like this:
 +    // match ::std::ops::Try::into_result(Err(5)) {
 +    //     ::std::result::Result::Err(err) =>
 +    //         #[allow(unreachable_code)]
 +    //         return ::std::ops::Try::from_error(::std::convert::From::from(err)),
 +    //     ::std::result::Result::Ok(val) =>
 +    //         #[allow(unreachable_code)]
 +    //         val,
 +    // };
 +    if_chain! {
 +        if let ExprKind::Call(match_fun, [try_arg, ..]) = scrutinee.kind;
 +        if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
 +        if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..));
 +        if let ExprKind::Call(err_fun, [err_arg, ..]) = try_arg.kind;
 +        if is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr);
 +        if let Some(return_ty) = find_return_type(cx, &expr.kind);
 +        then {
 +            let prefix;
 +            let suffix;
 +            let err_ty;
 +
 +            if let Some(ty) = result_error_type(cx, return_ty) {
 +                prefix = "Err(";
 +                suffix = ")";
 +                err_ty = ty;
 +            } else if let Some(ty) = poll_result_error_type(cx, return_ty) {
 +                prefix = "Poll::Ready(Err(";
 +                suffix = "))";
 +                err_ty = ty;
 +            } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
 +                prefix = "Poll::Ready(Some(Err(";
 +                suffix = ")))";
 +                err_ty = ty;
 +            } else {
 +                return;
 +            };
 +
 +            let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
 +            let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
 +            let mut applicability = Applicability::MachineApplicable;
 +            let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
 +            let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
 +                "" // already returns
 +            } else {
 +                "return "
 +            };
 +            let suggestion = if err_ty == expr_err_ty {
 +                format!("{ret_prefix}{prefix}{origin_snippet}{suffix}")
 +            } else {
 +                format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}")
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                TRY_ERR,
 +                expr.span,
 +                "returning an `Err(_)` with the `?` operator",
 +                "try this",
 +                suggestion,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +/// Finds function return type by examining return expressions in match arms.
 +fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
 +    if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
 +        for arm in arms.iter() {
 +            if let ExprKind::Ret(Some(ret)) = arm.body.kind {
 +                return Some(cx.typeck_results().expr_ty(ret));
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +/// Extracts the error type from Result<T, E>.
 +fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
 +    if_chain! {
 +        if let ty::Adt(_, subst) = ty.kind();
 +        if is_type_diagnostic_item(cx, ty, sym::Result);
 +        then {
 +            Some(subst.type_at(1))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Extracts the error type from Poll<Result<T, E>>.
 +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();
 +        if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did());
 +        then {
 +            Some(ready_subst.type_at(1))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Extracts the error type from Poll<Option<Result<T, E>>>.
 +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 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();
 +        if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did());
 +        let some_ty = ready_subst.type_at(0);
 +
 +        if let ty::Adt(some_def, some_subst) = some_ty.kind();
 +        if cx.tcx.is_diagnostic_item(sym::Result, some_def.did());
 +        then {
 +            Some(some_subst.type_at(1))
 +        } else {
 +            None
 +        }
 +    }
 +}
index 0c4d9f100f7a966f7c3bade76f7e3843b3518f70,0000000000000000000000000000000000000000..35024ec1224f04053e5d6ac01c71c2c20b8c5a00
mode 100644,000000..100644
--- /dev/null
@@@ -1,258 -1,0 +1,258 @@@
- use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res};
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `mem::replace()` on an `Option` with
 +    /// `None`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` already has the method `take()` for
 +    /// taking its current value (Some(..) or None) and replacing it with
 +    /// `None`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::mem;
 +    ///
 +    /// let mut an_option = Some(0);
 +    /// let replaced = mem::replace(&mut an_option, None);
 +    /// ```
 +    /// Is better expressed with:
 +    /// ```rust
 +    /// let mut an_option = Some(0);
 +    /// let taken = an_option.take();
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MEM_REPLACE_OPTION_WITH_NONE,
 +    style,
 +    "replacing an `Option` with `None` instead of `take()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `mem::replace(&mut _, mem::uninitialized())`
 +    /// and `mem::replace(&mut _, mem::zeroed())`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This will lead to undefined behavior even if the
 +    /// value is overwritten later, because the uninitialized value may be
 +    /// observed in the case of a panic.
 +    ///
 +    /// ### Example
 +    /// ```
 +    /// use std::mem;
 +    ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
 +    ///
 +    /// #[allow(deprecated, invalid_value)]
 +    /// fn myfunc (v: &mut Vec<i32>) {
 +    ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
 +    ///     let new_v = may_panic(taken_v); // undefined behavior on panic
 +    ///     mem::forget(mem::replace(v, new_v));
 +    /// }
 +    /// ```
 +    ///
 +    /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
 +    /// at the cost of either lazily creating a replacement value or aborting
 +    /// on panic, to ensure that the uninitialized value cannot be observed.
 +    #[clippy::version = "1.39.0"]
 +    pub MEM_REPLACE_WITH_UNINIT,
 +    correctness,
 +    "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `std::mem::replace` on a value of type
 +    /// `T` with `T::default()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `std::mem` module already has the method `take` to
 +    /// take the current value and replace it with the default value of that type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut text = String::from("foo");
 +    /// let replaced = std::mem::replace(&mut text, String::default());
 +    /// ```
 +    /// Is better expressed with:
 +    /// ```rust
 +    /// let mut text = String::from("foo");
 +    /// let taken = std::mem::take(&mut text);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub MEM_REPLACE_WITH_DEFAULT,
 +    style,
 +    "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
 +}
 +
 +impl_lint_pass!(MemReplace =>
 +    [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
 +
 +fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    // Check that second argument is `Option::None`
 +    if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
 +        // Since this is a late pass (already type-checked),
 +        // and we already know that the second argument is an
 +        // `Option`, we do not need to check the first
 +        // argument's type. All that's left is to get
 +        // replacee's path.
 +        let replaced_path = match dest.kind {
 +            ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
 +                if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
 +                    replaced_path
 +                } else {
 +                    return;
 +                }
 +            },
 +            ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
 +            _ => return,
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            MEM_REPLACE_OPTION_WITH_NONE,
 +            expr_span,
 +            "replacing an `Option` with `None`",
 +            "consider `Option::take()` instead",
 +            format!(
 +                "{}.take()",
 +                snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    if_chain! {
 +        // check if replacement is mem::MaybeUninit::uninit().assume_init()
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id);
 +        if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MEM_REPLACE_WITH_UNINIT,
 +                expr_span,
 +                "replacing with `mem::MaybeUninit::uninit().assume_init()`",
 +                "consider using",
 +                format!(
 +                    "std::ptr::read({})",
 +                    snippet_with_applicability(cx, dest.span, "", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +            return;
 +        }
 +    }
 +
 +    if_chain! {
 +        if let ExprKind::Call(repl_func, []) = src.kind;
 +        if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        then {
 +            if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
 +                let mut applicability = Applicability::MachineApplicable;
 +                span_lint_and_sugg(
 +                    cx,
 +                    MEM_REPLACE_WITH_UNINIT,
 +                    expr_span,
 +                    "replacing with `mem::uninitialized()`",
 +                    "consider using",
 +                    format!(
 +                        "std::ptr::read({})",
 +                        snippet_with_applicability(cx, dest.span, "", &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +            } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
 +                    !cx.typeck_results().expr_ty(src).is_primitive() {
 +                span_lint_and_help(
 +                    cx,
 +                    MEM_REPLACE_WITH_UNINIT,
 +                    expr_span,
 +                    "replacing with `mem::zeroed()`",
 +                    None,
 +                    "consider using a default value or the `take_mut` crate instead",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    // disable lint for primitives
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(src);
 +    if is_non_aggregate_primitive_type(expr_type) {
 +        return;
 +    }
 +    // disable lint for Option since it is covered in another lint
 +    if is_res_lang_ctor(cx, path_res(cx, src), OptionNone) {
 +        return;
 +    }
 +    if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
 +        span_lint_and_then(
 +            cx,
 +            MEM_REPLACE_WITH_DEFAULT,
 +            expr_span,
 +            "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
 +            |diag| {
 +                if !expr_span.from_expansion() {
 +                    let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
 +
 +                    diag.span_suggestion(
 +                        expr_span,
 +                        "consider using",
 +                        suggestion,
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            },
 +        );
 +    }
 +}
 +
 +pub struct MemReplace {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl MemReplace {
 +    #[must_use]
-                 if meets_msrv(self.msrv, msrvs::MEM_TAKE) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MemReplace {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // Check that `expr` is a call to `mem::replace()`
 +            if let ExprKind::Call(func, [dest, src]) = expr.kind;
 +            if let ExprKind::Path(ref func_qpath) = func.kind;
 +            if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
 +            if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
 +            then {
 +                check_replace_option_with_none(cx, src, dest, expr.span);
 +                check_replace_with_uninit(cx, src, dest, expr.span);
++                if self.msrv.meets(msrvs::MEM_TAKE) {
 +                    check_replace_with_default(cx, src, dest, expr.span);
 +                }
 +            }
 +        }
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
index e9aeab2d5b62e37180d67dc5bc64b75b12fa8666,0000000000000000000000000000000000000000..4e6ec61f6a83dab5b14f96a8c8920001a071e822
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,45 @@@
- use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +use rustc_errors::Applicability;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
- pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) {
 +use rustc_span::{sym, Span};
 +
 +use super::CLONED_INSTEAD_OF_COPIED;
 +
-             if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) =>
++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 is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_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) && msrv.meets(msrvs::ITERATOR_COPIED) => {
 +            match get_iterator_item_ty(cx, recv_ty) {
 +                // <T as Iterator>::Item
 +                Some(ty) => ty,
 +                _ => return,
 +            }
 +        },
 +        _ => return,
 +    };
 +    match inner_ty.kind() {
 +        // &T where T: Copy
 +        ty::Ref(_, ty, _) if is_copy(cx, *ty) => {},
 +        _ => return,
 +    };
 +    span_lint_and_sugg(
 +        cx,
 +        CLONED_INSTEAD_OF_COPIED,
 +        span,
 +        "used `cloned` where `copied` could be used instead",
 +        "try",
 +        "copied".into(),
 +        Applicability::MachineApplicable,
 +    );
 +}
index 720d9a68c85ee81bf43e2f34ed7fe7042415110b,0000000000000000000000000000000000000000..ae03da0d3f9ce3296a8ee70f8712b5ffc8587e44
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,53 @@@
- use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_span::{sym, Span};
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    _expr: &rustc_hir::Expr<'_>,
 +    recv: &rustc_hir::Expr<'_>,
-         if meets_msrv(msrv, msrvs::EXPECT_ERR);
 +    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 msrv.meets(msrvs::EXPECT_ERR);
 +
 +        // Grabs the `Result<T, E>` type
 +        let result_type = cx.typeck_results().expr_ty(recv);
 +        // Tests if the T type in a `Result<T, E>` is not None
 +        if let Some(data_type) = get_data_type(cx, result_type);
 +        // Tests if the T type in a `Result<T, E>` implements debug
 +        if has_debug_impl(cx, data_type);
 +
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                ERR_EXPECT,
 +                err_span.to(expect_span),
 +                "called `.err().expect()` on a `Result` value",
 +                "try",
 +                "expect_err".to_string(),
 +                Applicability::MachineApplicable
 +        );
 +        }
 +    };
 +}
 +
 +/// Given a `Result<T, E>` type, return its data (`T`).
 +fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
 +    match ty.kind() {
 +        ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(),
 +        _ => None,
 +    }
 +}
index ddf8a1f09b87d4b21b21704bc4bd1209a10600ad,0000000000000000000000000000000000000000..175e04f8ac061bd4d5d0305afa3c04bf01d24d44
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,42 @@@
- use clippy_utils::{is_trait_method, meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
-     msrv: Option<RustcVersion>,
 +use rustc_span::sym;
 +
 +use super::FILTER_MAP_NEXT;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    arg: &'tcx hir::Expr<'_>,
-         if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) {
++    msrv: &Msrv,
 +) {
 +    if is_trait_method(cx, expr, sym::Iterator) {
++        if !msrv.meets(msrvs::ITERATOR_FIND_MAP) {
 +            return;
 +        }
 +
 +        let msg = "called `filter_map(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
 +                   `.find_map(..)` instead";
 +        let filter_snippet = snippet(cx, arg.span, "..");
 +        if filter_snippet.lines().count() <= 1 {
 +            let iter_snippet = snippet(cx, recv.span, "..");
 +            span_lint_and_sugg(
 +                cx,
 +                FILTER_MAP_NEXT,
 +                expr.span,
 +                msg,
 +                "try this",
 +                format!("{iter_snippet}.find_map({filter_snippet})"),
 +                Applicability::MachineApplicable,
 +            );
 +        } else {
 +            span_lint(cx, FILTER_MAP_NEXT, expr.span, msg);
 +        }
 +    }
 +}
index 304024e80666f882e7eaff6bce448c734e1d3348,0000000000000000000000000000000000000000..301aff5ae6ac0ba21ebba1dbc8ee98d6af2d465f
mode 100644,000000..100644
--- /dev/null
@@@ -1,49 -1,0 +1,48 @@@
-     consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs,
-     source::snippet_with_applicability,
 +//! Lint for `c.is_digit(10)`
 +
 +use super::IS_DIGIT_ASCII_RADIX;
++use clippy_utils::msrvs::{self, Msrv};
 +use clippy_utils::{
- use rustc_semver::RustcVersion;
++    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;
-     msrv: Option<RustcVersion>,
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    self_arg: &'tcx Expr<'_>,
 +    radix: &'tcx Expr<'_>,
-     if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) {
++    msrv: &Msrv,
 +) {
++    if !msrv.meets(msrvs::IS_ASCII_DIGIT) {
 +        return;
 +    }
 +
 +    if !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_char() {
 +        return;
 +    }
 +
 +    if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
 +        let (num, replacement) = match radix_val {
 +            FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
 +            FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
 +            _ => return,
 +        };
 +        let mut applicability = Applicability::MachineApplicable;
 +
 +        span_lint_and_sugg(
 +            cx,
 +            IS_DIGIT_ASCII_RADIX,
 +            expr.span,
 +            &format!("use of `char::is_digit` with literal radix of {num}"),
 +            "try",
 +            format!(
 +                "{}.{replacement}()",
 +                snippet_with_applicability(cx, self_arg.span, "..", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
index 6bc783c6d505af108a508067f772a9fefcda5103,0000000000000000000000000000000000000000..52cc1e0464bf331cd3f624cfff75d725e5af9773
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,115 @@@
- use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
 +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 rustc_semver::RustcVersion;
++use clippy_utils::{is_diag_trait_item, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty;
 +use rustc_middle::ty::adjustment::Adjust;
- pub(super) fn check(
-     cx: &LateContext<'_>,
-     e: &hir::Expr<'_>,
-     recv: &hir::Expr<'_>,
-     arg: &hir::Expr<'_>,
-     msrv: Option<RustcVersion>,
- ) {
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span};
 +
 +use super::MAP_CLONE;
 +
- fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, 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)
 +            .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
 +            || is_diag_trait_item(cx, method_id, sym::Iterator);
 +        if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
 +        then {
 +            let closure_body = cx.tcx.hir().body(body);
 +            let closure_expr = peel_blocks(closure_body.value);
 +            match closure_body.params[0].pat.kind {
 +                hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
 +                    hir::BindingAnnotation::NONE, .., name, None
 +                ) = inner.kind {
 +                    if ident_eq(name, closure_expr) {
 +                        lint_explicit_closure(cx, e.span, recv.span, true, msrv);
 +                    }
 +                },
 +                hir::PatKind::Binding(hir::BindingAnnotation::NONE, .., name, None) => {
 +                    match closure_expr.kind {
 +                        hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
 +                            if ident_eq(name, inner) {
 +                                if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
 +                                    lint_explicit_closure(cx, e.span, recv.span, true, msrv);
 +                                }
 +                            }
 +                        },
 +                        hir::ExprKind::MethodCall(method, obj, [], _) => if_chain! {
 +                            if ident_eq(name, obj) && method.ident.name == sym::clone;
 +                            if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
 +                            if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
 +                            if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
 +                            // no autoderefs
 +                            if !cx.typeck_results().expr_adjustments(obj).iter()
 +                                .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
 +                            then {
 +                                let obj_ty = cx.typeck_results().expr_ty(obj);
 +                                if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
 +                                    if matches!(mutability, Mutability::Not) {
 +                                        let copy = is_copy(cx, *ty);
 +                                        lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
 +                                    }
 +                                } else {
 +                                    lint_needless_cloning(cx, e.span, recv.span);
 +                                }
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
 +    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
 +        path.segments.len() == 1 && path.segments[0].ident == name
 +    } else {
 +        false
 +    }
 +}
 +
 +fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
 +    span_lint_and_sugg(
 +        cx,
 +        MAP_CLONE,
 +        root.trim_start(receiver).unwrap(),
 +        "you are needlessly cloning iterator elements",
 +        "remove the `map` call",
 +        String::new(),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
-     let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
++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 && 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")
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        MAP_CLONE,
 +        replace,
 +        message,
 +        &format!("consider calling the dedicated `{sugg_method}` method"),
 +        format!(
 +            "{}.{sugg_method}()",
 +            snippet_with_applicability(cx, root, "..", &mut applicability),
 +        ),
 +        applicability,
 +    );
 +}
index 74fdead216b0a5f7c3df156b51c21cf83a1a59ac,0000000000000000000000000000000000000000..3122f72ee9155ad932c50eb9e721663a0d74d1b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,78 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
-     msrv: Option<RustcVersion>,
 +use rustc_span::symbol::sym;
 +
 +use super::MAP_UNWRAP_OR;
 +
 +/// lint use of `map().unwrap_or_else()` for `Option`s and `Result`s
 +/// Return true if lint triggered
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    map_arg: &'tcx hir::Expr<'_>,
 +    unwrap_arg: &'tcx hir::Expr<'_>,
-     if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) {
++    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 && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) {
 +        return false;
 +    }
 +
 +    if is_option || is_result {
 +        // Don't make a suggestion that may fail to compile due to mutably borrowing
 +        // the same variable twice.
 +        let map_mutated_vars = mutated_variables(recv, cx);
 +        let unwrap_mutated_vars = mutated_variables(unwrap_arg, cx);
 +        if let (Some(map_mutated_vars), Some(unwrap_mutated_vars)) = (map_mutated_vars, unwrap_mutated_vars) {
 +            if map_mutated_vars.intersection(&unwrap_mutated_vars).next().is_some() {
 +                return false;
 +            }
 +        } else {
 +            return false;
 +        }
 +
 +        // lint message
 +        let msg = if is_option {
 +            "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"
 +        } else {
 +            "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"
 +        };
 +        // get snippets for args to map() and unwrap_or_else()
 +        let map_snippet = snippet(cx, map_arg.span, "..");
 +        let unwrap_snippet = snippet(cx, unwrap_arg.span, "..");
 +        // lint, with note if neither arg is > 1 line and both map() and
 +        // unwrap_or_else() have the same span
 +        let multiline = map_snippet.lines().count() > 1 || unwrap_snippet.lines().count() > 1;
 +        let same_span = map_arg.span.ctxt() == unwrap_arg.span.ctxt();
 +        if same_span && !multiline {
 +            let var_snippet = snippet(cx, recv.span, "..");
 +            span_lint_and_sugg(
 +                cx,
 +                MAP_UNWRAP_OR,
 +                expr.span,
 +                msg,
 +                "try this",
 +                format!("{var_snippet}.map_or_else({unwrap_snippet}, {map_snippet})"),
 +                Applicability::MachineApplicable,
 +            );
 +            return true;
 +        } else if same_span && multiline {
 +            span_lint(cx, MAP_UNWRAP_OR, expr.span, msg);
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
index 38165ab4fb26f4053a256c012a933307538b8452,0000000000000000000000000000000000000000..d2913680cbb742a11db2439aa668aff4d0a979c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,3996 -1,0 +1,3996 @@@
- use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
 +mod bind_instead_of_map;
 +mod bytecount;
 +mod bytes_count_to_len;
 +mod bytes_nth;
 +mod case_sensitive_file_extension_comparisons;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod collapsible_str_replace;
 +mod err_expect;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_first;
 +mod get_last_with_len;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod is_digit_ascii_radix;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_kv_map;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_on_single_or_empty_collections;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
 +mod manual_ok_or;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
 +mod map_clone;
 +mod map_collect_result_unit;
 +mod map_err_ignore;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod mut_mutex_lock;
 +mod needless_collect;
 +mod needless_option_as_deref;
 +mod needless_option_take;
 +mod no_effect_replace;
 +mod obfuscated_if_else;
 +mod ok_expect;
 +mod open_options;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
 +mod path_buf_push_overwrite;
 +mod range_zip_with_len;
 +mod repeat_once;
 +mod search_is_some;
 +mod seek_from_current;
 +mod seek_to_start_instead_of_rewind;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod stable_sort_primitive;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod suspicious_to_owned;
 +mod uninit_assumed_init;
 +mod unit_hash;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
 +mod unnecessary_sort_by;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod vec_resize_to_zero;
 +mod verbose_file_reads;
 +mod wrong_self_convention;
 +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 rustc_semver::RustcVersion;
++use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind};
 +use rustc_hir_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};
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive calls to `str::replace` (2 or more)
 +    /// that can be collapsed into a single call.
 +    ///
 +    /// ### Why is this bad?
 +    /// Consecutive `str::replace` calls scan the string multiple times
 +    /// with repetitive code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let hello = "hesuo worpd"
 +    ///     .replace('s', "l")
 +    ///     .replace("u", "l")
 +    ///     .replace('p', "l");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l");
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub COLLAPSIBLE_STR_REPLACE,
 +    perf,
 +    "collapse consecutive calls to str::replace (2 or more) into a single call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
 +    /// of them will be consumed.
 +    ///
 +    /// ### Known Problems
 +    /// This `lint` removes the side of effect of cloning items in the iterator.
 +    /// A code that relies on that side-effect could fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().cloned().take(10);
 +    /// vec.iter().cloned().last();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().take(10).cloned();
 +    /// vec.iter().last().cloned();
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub ITER_OVEREAGER_CLONED,
 +    perf,
 +    "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub FLAT_MAP_OPTION,
 +    pedantic,
 +    "used `flat_map` where `filter_map` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.unwrap();
 +    /// result.unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("more helpful message");
 +    /// result.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// If [expect_used](#expect_used) is enabled, instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("one");
 +    /// result.expect("one");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub EXPECT_USED,
 +    restriction,
 +    "using `.expect()` on `Result` or `Option`, which might be better handled"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods that should live in a trait
 +    /// implementation of a `std` trait (see [llogiq's blog
 +    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
 +    /// information) instead of an inherent implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// Implementing the traits improve ergonomics for users of
 +    /// the code, often with very little cost. Also people seeing a `mul(...)`
 +    /// method
 +    /// may expect `*` to work equally, so you should have good reason to disappoint
 +    /// them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn add(&self, other: &X) -> X {
 +    ///         // ..
 +    /// # X
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHOULD_IMPLEMENT_TRAIT,
 +    style,
 +    "defining a method that should be implementing a std trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with certain name prefixes and which
 +    /// doesn't match how self is taken. The actual rules are:
 +    ///
 +    /// |Prefix |Postfix     |`self` taken                   | `self` type  |
 +    /// |-------|------------|-------------------------------|--------------|
 +    /// |`as_`  | none       |`&self` or `&mut self`         | any          |
 +    /// |`from_`| none       | none                          | any          |
 +    /// |`into_`| none       |`self`                         | any          |
 +    /// |`is_`  | none       |`&mut self` or `&self` or none | any          |
 +    /// |`to_`  | `_mut`     |`&mut self`                    | any          |
 +    /// |`to_`  | not `_mut` |`self`                         | `Copy`       |
 +    /// |`to_`  | not `_mut` |`&self`                        | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_SELF_CONVENTION,
 +    style,
 +    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.ok().expect("why did I do this again?");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.expect("why did I do this again?");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OK_EXPECT,
 +    style,
 +    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.err().expect()` calls on the `Result` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
 +    ///
 +    /// ### Example
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.err().expect("Testing err().expect()");
 +    /// ```
 +    /// Use instead:
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.expect_err("Testing expect_err");
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub ERR_EXPECT,
 +    style,
 +    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map(|a| a + 1).unwrap_or(0);
 +    /// result.map(|a| a + 1).unwrap_or_else(some_function);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map_or(0, |a| a + 1);
 +    /// result.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    /// let opt = Some(5);
 +    ///
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![vec![1]];
 +    /// # let opt = Some(5);
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// opt.and_then(|x| Some(x * 2));
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MAP_FLATTEN,
 +    complexity,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// "hello world".find("world").is_none();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// # #[allow(unused)]
 +    /// !"hello world".contains("world");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
 +    /// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
 +    /// etc. instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called. This is only bad if it allocates or
 +    /// does some non-trivial amount of work.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will change the
 +    /// semantic of the program, but you shouldn't rely on that.
 +    ///
 +    /// The lint also cannot figure out whether the function you call is
 +    /// actually expensive to call or not.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::from("empty"));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(|| String::from("empty"));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OR_FUN_CALL,
 +    nursery,
 +    "using any `*or` method with a function call, which suggests `*or_else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.or(…).unwrap()` calls to Options and Results.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `.unwrap_or(…)` instead for clarity.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # type Error = &'static str;
 +    /// # let result: Result<&str, Error> = Err("error");
 +    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.or(Some(fallback)).unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # let result: Result<&str, &str> = Err("error");
 +    /// let value = result.unwrap_or(fallback);
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.unwrap_or(fallback);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub OR_THEN_UNWRAP,
 +    complexity,
 +    "checks for `.or(…).unwrap()` calls to Options and Results."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// x.clone();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// # let x = Rc::new(1);
 +    /// Rc::clone(&x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// _.split("x");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// _.split('x');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.into_iter()` is simpler with better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let mut foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.drain(..).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.into_iter().collect();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ITER_WITH_DRAIN,
 +    nursery,
 +    "replace `.drain(..)` with `.into_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(x.len() - 1)` instead of
 +    /// `x.last()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.last()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// Note that using `x[x.len() - 1]` is semantically different from
 +    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
 +    /// accesses, while `x.get()` and `x.last()` will return `None`.
 +    ///
 +    /// There is another lint (get_unwrap) that covers the case of using
 +    /// `x.get(index).unwrap()` instead of `x[index]`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.get(x.len() - 1);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.last();
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub GET_LAST_WITH_LEN,
 +    complexity,
 +    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.extend(b.drain(..));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.append(&mut b);
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
 +    "using vec.append(&mut vec) to move the full range of a vector to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).any(|x| x > 2);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
 +    /// specifically it checks if the closure provided is only performing one of the
 +    /// find or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).find(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).find_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1).next();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_FIND_MAP,
 +    complexity,
 +    "using `find_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).into_iter();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or its aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".bytes().nth(3);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".as_bytes().get(3);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.iter().count();
 +    /// &some_vec[..].iter().count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.len();
 +    /// &some_vec[..].len();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
 +    /// itself, without taking ownership of the `Cow` contents (i.e.
 +    /// it's equivalent to calling `Cow::clone`).
 +    /// The similarly named `into_owned` method, on the other hand,
 +    /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
 +    /// into a `Cow::Owned`.
 +    ///
 +    /// Given the potential ambiguity, consider replacing `to_owned`
 +    /// with `clone` for better readability or, if getting a `Cow::Owned`
 +    /// was the original intent, using `into_owned` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.to_owned();
 +    /// assert!(matches!(data, Cow::Borrowed(_)))
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let data = cow.clone();
 +    /// assert!(matches!(data, Cow::Borrowed(_)))
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # use std::borrow::Cow;
 +    /// let s = "Hello world!";
 +    /// let cow = Cow::Borrowed(s);
 +    ///
 +    /// let _data: String = cow.into_owned();
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub SUSPICIOUS_TO_OWNED,
 +    suspicious,
 +    "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn(2, _)`
 +    ///
 +    /// ### Why is this bad?
 +    /// `split_once` is both clearer in intent and slightly more efficient.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.splitn(2, '=').next_tuple()?;
 +    /// let value = s.splitn(2, '=').nth(1)?;
 +    ///
 +    /// let mut parts = s.splitn(2, '=');
 +    /// let key = parts.next()?;
 +    /// let value = parts.next()?;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.split_once('=')?;
 +    /// let value = s.split_once('=')?.1;
 +    ///
 +    /// let (key, value) = s.split_once('=')?;
 +    /// ```
 +    ///
 +    /// ### Limitations
 +    /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
 +    /// in two separate `let` statements that immediately follow the `splitn()`
 +    #[clippy::version = "1.57.0"]
 +    pub MANUAL_SPLIT_ONCE,
 +    complexity,
 +    "replace `.splitn(2, pat)` with `.split_once(pat)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
 +    /// ### Why is this bad?
 +    /// The function `split` is simpler and there is no performance difference in these cases, considering
 +    /// that both functions return a lazy iterator.
 +    /// ### Example
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.splitn(3, '=').next().unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.split('=').next().unwrap();
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub NEEDLESS_SPLITN,
 +    complexity,
 +    "usages of `str::splitn` that can be replaced with `str::split`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
 +    /// and other `to_owned`-like functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// The unnecessary calls result in useless allocations.
 +    ///
 +    /// ### Known problems
 +    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
 +    /// owned copy of a resource and the resource is later used mutably. See
 +    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy().to_string());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub UNNECESSARY_TO_OWNED,
 +    perf,
 +    "unnecessary calls to `to_owned`-like functions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.collect::<String>()` is more concise and might be more performant
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
 +    /// println!("{}", output);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
 +    /// println!("{}", output);
 +    /// ```
 +    /// ### Known problems
 +    /// While `.collect::<String>()` is sometimes more performant, there are cases where
 +    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
 +    /// will prevent loop unrolling and will result in a negative performance impact.
 +    ///
 +    /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
 +    /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_JOIN,
 +    pedantic,
 +    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
 +    /// for example, `Option<&T>::as_deref()` returns the same type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code and improving readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a;
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub NEEDLESS_OPTION_AS_DEREF,
 +    complexity,
 +    "no-op use of `deref` or `deref_mut` method to `Option`."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
 +    /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
 +    /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
 +    ///
 +    /// ### Why is this bad?
 +    /// `is_digit(..)` is slower and requires specifying the radix.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_digit(10);
 +    /// c.is_digit(16);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_ascii_digit();
 +    /// c.is_ascii_hexdigit();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub IS_DIGIT_ASCII_RADIX,
 +    style,
 +    "use of `char::is_digit(..)` with literal radix of 10 or 16"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `take` function after `as_ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code. `take` writes `None` to its argument.
 +    /// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref().take();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub NEEDLESS_OPTION_TAKE,
 +    complexity,
 +    "using `.as_ref().take()` on a temporary value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `replace` statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's either a mistake or confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "1234".replace("12", "12");
 +    /// "1234".replacen("12", "12", 1);
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub NO_EFFECT_REPLACE,
 +    suspicious,
 +    "replace with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `.then_some(..).unwrap_or(..)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This can be written more clearly with `if .. else ..`
 +    ///
 +    /// ### Limitations
 +    /// This lint currently only looks for usages of
 +    /// `.then_some(..).unwrap_or(..)`, but will be expanded
 +    /// to account for similar patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = true;
 +    /// x.then_some("a").unwrap_or("b");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = true;
 +    /// if x { "a" } else { "b" };
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub OBFUSCATED_IF_ELSE,
 +    style,
 +    "use of `.then_some(..).unwrap_or(..)` can be written \
 +    more clearly with `if .. else ..`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// It is simpler to use the once function from the standard library:
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// let a = [123].iter();
 +    /// let b = Some(123).into_iter();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::iter;
 +    /// let a = iter::once(&123);
 +    /// let b = iter::once(123);
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// The type of the resulting iterator might become incompatible with its usage
 +    #[clippy::version = "1.65.0"]
 +    pub ITER_ON_SINGLE_ITEMS,
 +    nursery,
 +    "Iterator for array of length 1"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// It is simpler to use the empty function from the standard library:
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// use std::{slice, option};
 +    /// let a: slice::Iter<i32> = [].iter();
 +    /// let f: option::IntoIter<i32> = None.into_iter();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::iter;
 +    /// let a: iter::Empty<i32> = iter::empty();
 +    /// let b: iter::Empty<i32> = iter::empty();
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// The type of the resulting iterator might become incompatible with its usage
 +    #[clippy::version = "1.65.0"]
 +    pub ITER_ON_EMPTY_COLLECTIONS,
 +    nursery,
 +    "Iterator for empty array"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for naive byte counts
 +    ///
 +    /// ### Why is this bad?
 +    /// The [`bytecount`](https://crates.io/crates/bytecount)
 +    /// crate has methods to count your bytes faster, especially for large slices.
 +    ///
 +    /// ### Known problems
 +    /// If you have predominantly small slices, the
 +    /// `bytecount::count(..)` method may actually be slower. However, if you can
 +    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
 +    /// faster in those cases.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1_u8];
 +    /// let count = vec.iter().filter(|x| **x == 0u8).count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let vec = vec![1_u8];
 +    /// let count = bytecount::count(&vec, 0u8);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NAIVE_BYTECOUNT,
 +    pedantic,
 +    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It checks for `str::bytes().count()` and suggests replacing it with
 +    /// `str::len()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `str::bytes().count()` is longer and may not be as performant as using
 +    /// `str::len()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "hello".bytes().count();
 +    /// String::from("hello").bytes().count();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// "hello".len();
 +    /// String::from("hello").len();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub BYTES_COUNT_TO_LEN,
 +    complexity,
 +    "Using `bytes().count()` when `len()` performs the same functionality"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `ends_with` with possible file extensions
 +    /// and suggests to use a case-insensitive approach instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn is_rust_file(filename: &str) -> bool {
 +    ///     filename.ends_with(".rs")
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn is_rust_file(filename: &str) -> bool {
 +    ///     let filename = std::path::Path::new(filename);
 +    ///     filename.extension()
 +    ///         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    pedantic,
 +    "Checks for calls to ends_with with case-sensitive file extensions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(0)` instead of
 +    /// `x.first()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.first()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let first_element = x.get(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let first_element = x.first();
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub GET_FIRST,
 +    style,
 +    "Using `x.get(0)` when `x.first()` is simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Finds patterns that reimplement `Option::ok_or`.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Concise code helps focusing on behavior instead of boilerplate.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.map_or(Err("error"), |v| Ok(v));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let foo: Option<i32> = None;
 +    /// foo.ok_or("error");
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_OK_OR,
 +    pedantic,
 +    "finds patterns that can be encoded more concisely with `Option::ok_or`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `map(|x| x.clone())` or
 +    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
 +    /// and suggests `cloned()` or `copied()` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![42, 43];
 +    /// let y = x.iter();
 +    /// let z = y.map(|i| *i);
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// let x = vec![42, 43];
 +    /// let y = x.iter();
 +    /// let z = y.cloned();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MAP_CLONE,
 +    style,
 +    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map_err(|_| Some::Enum)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
 +    ///
 +    /// ### Example
 +    /// Before:
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// #[derive(Debug)]
 +    /// enum Error {
 +    ///     Indivisible,
 +    ///     Remainder(u8),
 +    /// }
 +    ///
 +    /// impl fmt::Display for Error {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         match self {
 +    ///             Error::Indivisible => write!(f, "could not divide input by three"),
 +    ///             Error::Remainder(remainder) => write!(
 +    ///                 f,
 +    ///                 "input is not divisible by three, remainder = {}",
 +    ///                 remainder
 +    ///             ),
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for Error {}
 +    ///
 +    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
 +    ///     input
 +    ///         .parse::<i32>()
 +    ///         .map_err(|_| Error::Indivisible)
 +    ///         .map(|v| v % 3)
 +    ///         .and_then(|remainder| {
 +    ///             if remainder == 0 {
 +    ///                 Ok(())
 +    ///             } else {
 +    ///                 Err(Error::Remainder(remainder as u8))
 +    ///             }
 +    ///         })
 +    /// }
 +    ///  ```
 +    ///
 +    ///  After:
 +    ///  ```rust
 +    /// use std::{fmt, num::ParseIntError};
 +    ///
 +    /// #[derive(Debug)]
 +    /// enum Error {
 +    ///     Indivisible(ParseIntError),
 +    ///     Remainder(u8),
 +    /// }
 +    ///
 +    /// impl fmt::Display for Error {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         match self {
 +    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
 +    ///             Error::Remainder(remainder) => write!(
 +    ///                 f,
 +    ///                 "input is not divisible by three, remainder = {}",
 +    ///                 remainder
 +    ///             ),
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for Error {
 +    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
 +    ///         match self {
 +    ///             Error::Indivisible(source) => Some(source),
 +    ///             _ => None,
 +    ///         }
 +    ///     }
 +    /// }
 +    ///
 +    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
 +    ///     input
 +    ///         .parse::<i32>()
 +    ///         .map_err(Error::Indivisible)
 +    ///         .map(|v| v % 3)
 +    ///         .and_then(|remainder| {
 +    ///             if remainder == 0 {
 +    ///                 Ok(())
 +    ///             } else {
 +    ///                 Err(Error::Remainder(remainder as u8))
 +    ///             }
 +    ///         })
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub MAP_ERR_IGNORE,
 +    restriction,
 +    "`map_err` should not ignore the original error"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `&mut Mutex::lock` calls
 +    ///
 +    /// ### Why is this bad?
 +    /// `Mutex::lock` is less efficient than
 +    /// calling `Mutex::get_mut`. In addition you also have a statically
 +    /// guarantee that the mutex isn't locked, instead of just a runtime
 +    /// guarantee.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let mut value = value_mutex.lock().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::sync::{Arc, Mutex};
 +    ///
 +    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
 +    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
 +    ///
 +    /// let value = value_mutex.get_mut().unwrap();
 +    /// *value += 1;
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MUT_MUTEX_LOCK,
 +    style,
 +    "`&mut Mutex::lock` does unnecessary locking"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for duplicate open options as well as combinations
 +    /// that make no sense.
 +    ///
 +    /// ### Why is this bad?
 +    /// In the best case, the code will be harder to read than
 +    /// necessary. I don't know the worst case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::fs::OpenOptions;
 +    ///
 +    /// OpenOptions::new().read(true).truncate(true);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NONSENSICAL_OPEN_OPTIONS,
 +    correctness,
 +    "nonsensical combination of options for opening a file"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
 +    /// calls on `PathBuf` that can cause overwrites.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `push` with a root path at the start can overwrite the
 +    /// previous defined path.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::path::PathBuf;
 +    ///
 +    /// let mut x = PathBuf::from("/foo");
 +    /// x.push("/bar");
 +    /// assert_eq!(x, PathBuf::from("/bar"));
 +    /// ```
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// use std::path::PathBuf;
 +    ///
 +    /// let mut x = PathBuf::from("/foo");
 +    /// x.push("bar");
 +    /// assert_eq!(x, PathBuf::from("/foo/bar"));
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub PATH_BUF_PUSH_OVERWRITE,
 +    nursery,
 +    "calling `push` with file system root on `PathBuf` can overwrite it"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for zipping a collection with the range of
 +    /// `0.._.len()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is better expressed with `.enumerate()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// let _ = x.iter().zip(0..x.len());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// let _ = x.iter().enumerate();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_ZIP_WITH_LEN,
 +    complexity,
 +    "zipping iterator with a range when `enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
 +    /// - `.to_string()` for `str`
 +    /// - `.clone()` for `String`
 +    /// - `.to_vec()` for `slice`
 +    ///
 +    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
 +    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
 +    ///
 +    /// ### Why is this bad?
 +    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
 +    /// the string is the intention behind this, `clone()` should be used.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = String::from("hello world").repeat(1);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = String::from("hello world").clone();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub REPEAT_ONCE,
 +    complexity,
 +    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// When sorting primitive values (integers, bools, chars, as well
 +    /// as arrays, slices, and tuples of such items), it is typically better to
 +    /// use an unstable sort than a stable sort.
 +    ///
 +    /// ### Why is this bad?
 +    /// Typically, using a stable sort consumes more memory and cpu cycles.
 +    /// Because values which compare equal are identical, preserving their
 +    /// relative order (the guarantee that a stable sort provides) means
 +    /// nothing, while the extra costs still apply.
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// As pointed out in
 +    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
 +    /// a stable sort can instead be significantly faster for certain scenarios
 +    /// (eg. when a sorted vector is extended with new data and resorted).
 +    ///
 +    /// For more information and benchmarking results, please refer to the
 +    /// issue linked above.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut vec = vec![2, 1, 3];
 +    /// vec.sort();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut vec = vec![2, 1, 3];
 +    /// vec.sort_unstable();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub STABLE_SORT_PRIMITIVE,
 +    pedantic,
 +    "use of sort() when sort_unstable() is equivalent"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `().hash(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::hash::Hash;
 +    /// # use std::collections::hash_map::DefaultHasher;
 +    /// # enum Foo { Empty, WithValue(u8) }
 +    /// # use Foo::*;
 +    /// # let mut state = DefaultHasher::new();
 +    /// # let my_enum = Foo::Empty;
 +    /// match my_enum {
 +    ///       Empty => ().hash(&mut state),
 +    ///       WithValue(x) => x.hash(&mut state),
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::hash::Hash;
 +    /// # use std::collections::hash_map::DefaultHasher;
 +    /// # enum Foo { Empty, WithValue(u8) }
 +    /// # use Foo::*;
 +    /// # let mut state = DefaultHasher::new();
 +    /// # let my_enum = Foo::Empty;
 +    /// match my_enum {
 +    ///       Empty => 0_u8.hash(&mut state),
 +    ///       WithValue(x) => x.hash(&mut state),
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNIT_HASH,
 +    correctness,
 +    "hashing a unit value, which does nothing"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects uses of `Vec::sort_by` passing in a closure
 +    /// which compares the two arguments, either directly or indirectly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
 +    /// possible) than to use `Vec::sort_by` and a more complicated
 +    /// closure.
 +    ///
 +    /// ### Known problems
 +    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
 +    /// imported by a use statement, then it will need to be added manually.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by_key(|a| a.foo());
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub UNNECESSARY_SORT_BY,
 +    complexity,
 +    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds occurrences of `Vec::resize(0, an_int)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably an argument inversion mistake.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// vec!(1, 2, 3, 4, 5).clear()
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub VEC_RESIZE_TO_ZERO,
 +    correctness,
 +    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of File::read_to_end and File::read_to_string.
 +    ///
 +    /// ### Why is this bad?
 +    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
 +    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # use std::io::Read;
 +    /// # use std::fs::File;
 +    /// let mut f = File::open("foo.txt").unwrap();
 +    /// let mut bytes = Vec::new();
 +    /// f.read_to_end(&mut bytes).unwrap();
 +    /// ```
 +    /// Can be written more concisely as
 +    /// ```rust,no_run
 +    /// # use std::fs;
 +    /// let mut bytes = fs::read("foo.txt").unwrap();
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub VERBOSE_FILE_READS,
 +    restriction,
 +    "use of `File::read_to_end` or `File::read_to_string`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for iterating a map (`HashMap` or `BTreeMap`) and
 +    /// ignoring either the keys or values.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Readability. There are `keys` and `values` methods that
 +    /// can be used to express that we only need the keys or the values.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```
 +    /// # use std::collections::HashMap;
 +    /// let map: HashMap<u32, u32> = HashMap::new();
 +    /// let values = map.iter().map(|(_, value)| value).collect::<Vec<_>>();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```
 +    /// # use std::collections::HashMap;
 +    /// let map: HashMap<u32, u32> = HashMap::new();
 +    /// let values = map.values().collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub ITER_KV_MAP,
 +    complexity,
 +    "iterating on map using `iter` when `keys` or `values` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks an argument of `seek` method of `Seek` trait
 +    /// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Readability. Use dedicated method.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust,no_run
 +    /// use std::fs::File;
 +    /// use std::io::{self, Write, Seek, SeekFrom};
 +    ///
 +    /// fn main() -> io::Result<()> {
 +    ///     let mut f = File::create("foo.txt")?;
 +    ///     f.write_all(b"Hello")?;
 +    ///     eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?);
 +    ///
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,no_run
 +    /// use std::fs::File;
 +    /// use std::io::{self, Write, Seek, SeekFrom};
 +    ///
 +    /// fn main() -> io::Result<()> {
 +    ///     let mut f = File::create("foo.txt")?;
 +    ///     f.write_all(b"Hello")?;
 +    ///     eprintln!("Written {} bytes", f.stream_position()?);
 +    ///
 +    ///     Ok(())
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub SEEK_FROM_CURRENT,
 +    complexity,
 +    "use dedicated method for seek from current position"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for jumps to the start of a stream that implements `Seek`
 +    /// and uses the `seek` method providing `Start` as parameter.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// Readability. There is a specific method that was implemented for
 +    /// this exact scenario.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::io;
 +    /// fn foo<T: io::Seek>(t: &mut T) {
 +    ///     t.seek(io::SeekFrom::Start(0));
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::io;
 +    /// fn foo<T: io::Seek>(t: &mut T) {
 +    ///     t.rewind();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.66.0"]
 +    pub SEEK_TO_START_INSTEAD_OF_REWIND,
 +    complexity,
 +    "jumping to the start of stream using `seek` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions collecting an iterator when collect
 +    /// is not needed.
 +    ///
 +    /// ### Why is this bad?
 +    /// `collect` causes the allocation of a new data structure,
 +    /// when this allocation may not be needed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iterator = vec![1].into_iter();
 +    /// let len = iterator.clone().collect::<Vec<_>>().len();
 +    /// // should be
 +    /// let len = iterator.count();
 +    /// ```
 +    #[clippy::version = "1.30.0"]
 +    pub NEEDLESS_COLLECT,
 +    nursery,
 +    "collecting an iterator when collect is not needed"
 +}
 +
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
-         msrv: Option<RustcVersion>,
++    msrv: Msrv,
 +    allow_expect_in_tests: bool,
 +    allow_unwrap_in_tests: bool,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(
 +        avoid_breaking_exported_api: bool,
-                 unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv);
++        msrv: Msrv,
 +        allow_expect_in_tests: bool,
 +        allow_unwrap_in_tests: bool,
 +    ) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    OR_THEN_UNWRAP,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    COLLAPSIBLE_STR_REPLACE,
 +    ITER_OVEREAGER_CLONED,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    GET_LAST_WITH_LEN,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    ITER_WITH_DRAIN,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    UNNECESSARY_FIND_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_TO_OWNED,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
 +    EXTEND_WITH_DRAIN,
 +    MANUAL_SPLIT_ONCE,
 +    NEEDLESS_SPLITN,
 +    UNNECESSARY_TO_OWNED,
 +    UNNECESSARY_JOIN,
 +    ERR_EXPECT,
 +    NEEDLESS_OPTION_AS_DEREF,
 +    IS_DIGIT_ASCII_RADIX,
 +    NEEDLESS_OPTION_TAKE,
 +    NO_EFFECT_REPLACE,
 +    OBFUSCATED_IF_ELSE,
 +    ITER_ON_SINGLE_ITEMS,
 +    ITER_ON_EMPTY_COLLECTIONS,
 +    NAIVE_BYTECOUNT,
 +    BYTES_COUNT_TO_LEN,
 +    CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    GET_FIRST,
 +    MANUAL_OK_OR,
 +    MAP_CLONE,
 +    MAP_ERR_IGNORE,
 +    MUT_MUTEX_LOCK,
 +    NONSENSICAL_OPEN_OPTIONS,
 +    PATH_BUF_PUSH_OVERWRITE,
 +    RANGE_ZIP_WITH_LEN,
 +    REPEAT_ONCE,
 +    STABLE_SORT_PRIMITIVE,
 +    UNIT_HASH,
 +    UNNECESSARY_SORT_BY,
 +    VEC_RESIZE_TO_ZERO,
 +    VERBOSE_FILE_READS,
 +    ITER_KV_MAP,
 +    SEEK_FROM_CURRENT,
 +    SEEK_TO_START_INSTEAD_OF_REWIND,
 +    NEEDLESS_COLLECT,
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(
 +    recv: &'tcx hir::Expr<'tcx>,
 +) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span, Span)> {
 +    if let ExprKind::MethodCall(path, receiver, args, call_span) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) && !receiver.span.from_expansion() {
 +            let name = path.ident.name.as_str();
 +            return Some((name, receiver, args, path.ident.span, call_span));
 +        }
 +    }
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        self.check_methods(cx, expr);
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
 +                let method_span = method_call.ident.span;
 +                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
 +                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
 +                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);
-                 ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, 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 {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id()).def_id;
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.owner_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind {
 +            let method_sig = cx.tcx.fn_sig(impl_item.owner_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +            let first_arg_ty_opt = method_sig.inputs().iter().next().copied();
 +            // if this impl block implements a trait, lint in trait definition instead
 +            if !implements_trait && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) {
 +                // check missing trait implementations
 +                for method_config in &TRAIT_METHODS {
 +                    if name == method_config.method_name
 +                        && sig.decl.inputs.len() == method_config.param_count
 +                        && method_config.output_type.matches(&sig.decl.output)
 +                        // in case there is no first arg, since we already have checked the number of arguments
 +                        // it's should be always true
 +                        && first_arg_ty_opt.map_or(true, |first_arg_ty| method_config
 +                            .self_kind.matches(cx, self_ty, first_arg_ty)
 +                            )
 +                        && fn_header_equals(method_config.fn_header, sig.header)
 +                        && method_config.lifetime_param_cond(impl_item)
 +                    {
 +                        span_lint_and_help(
 +                            cx,
 +                            SHOULD_IMPLEMENT_TRAIT,
 +                            impl_item.span,
 +                            &format!(
 +                                "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                method_config.method_name, method_config.trait_name, method_config.method_name
 +                            ),
 +                            None,
 +                            &format!(
 +                                "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                method_config.trait_name
 +                            ),
 +                        );
 +                    }
 +                }
 +            }
 +
 +            if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                    && cx.effective_visibilities.is_exported(impl_item.owner_id.def_id))
 +                    && let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next()
 +                    && let Some(first_arg_ty) = first_arg_ty_opt
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        name,
 +                        self_ty,
 +                        first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id())
 +                    .self_ty()
 +                    .skip_binder();
 +                wrong_self_convention::check(
 +                    cx,
 +                    item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true,
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.owner_id.to_def_id())
 +                .self_ty()
 +                .skip_binder();
 +            if !ret_ty.contains(self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +impl Methods {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((name, recv, args, span, call_span)) = method_call(expr) {
 +            match (name, args) {
 +                ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                    zst_offset::check(cx, expr, recv);
 +                },
 +                ("and_then", [arg]) => {
 +                    let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                    let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                    if !biom_option_linted && !biom_result_linted {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                    }
 +                },
 +                ("as_deref" | "as_deref_mut", []) => {
 +                    needless_option_as_deref::check(cx, expr, recv, name);
 +                },
 +                ("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),
-                             if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
++                ("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) {
 +                        Some((name @ ("cloned" | "copied"), recv2, [], _, _)) => {
 +                            iter_cloned_collect::check(cx, name, expr, recv2);
 +                        },
 +                        Some(("map", m_recv, [m_arg], _, _)) => {
 +                            map_collect_result_unit::check(cx, expr, m_recv, m_arg);
 +                        },
 +                        Some(("take", take_self_arg, [take_arg], _, _)) => {
-                     Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
++                            if self.msrv.meets(msrvs::STR_REPEAT) {
 +                                manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                },
 +                ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
 +                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
 +                    Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _, _)) => {
 +                        iter_count::check(cx, expr, recv2, name2);
 +                    },
 +                    Some(("map", _, [arg], _, _)) => suspicious_map::check(cx, expr, recv, arg),
 +                    Some(("filter", recv2, [arg], _, _)) => bytecount::check(cx, expr, recv2, arg),
 +                    Some(("bytes", recv2, [], _, _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
 +                    _ => {},
 +                },
 +                ("drain", [arg]) => {
 +                    iter_with_drain::check(cx, expr, recv, span, arg);
 +                },
 +                ("ends_with", [arg]) => {
 +                    if let ExprKind::MethodCall(.., span) = expr.kind {
 +                        case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
 +                    }
 +                },
 +                ("expect", [_]) => match method_call(recv) {
 +                    Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
-                 ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
++                    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),
 +                ("extend", [arg]) => {
 +                    string_extend_chars::check(cx, expr, recv, arg);
 +                    extend_with_drain::check(cx, expr, recv, arg);
 +                },
 +                ("filter_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                    filter_map_identity::check(cx, expr, arg, span);
 +                },
 +                ("find_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                },
 +                ("flat_map", [arg]) => {
 +                    flat_map_identity::check(cx, expr, arg, span);
 +                    flat_map_option::check(cx, expr, arg, span);
 +                },
 +                ("flatten", []) => match method_call(recv) {
 +                    Some(("map", recv, [map_arg], map_span, _)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
 +                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
 +                    _ => {},
 +                },
 +                ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +                ("for_each", [_]) => {
 +                    if let Some(("inspect", _, [_], span2, _)) = method_call(recv) {
 +                        inspect_for_each::check(cx, expr, span2);
 +                    }
 +                },
 +                ("get", [arg]) => {
 +                    get_first::check(cx, expr, recv, arg);
 +                    get_last_with_len::check(cx, expr, recv, arg);
 +                },
 +                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +                ("hash", [arg]) => {
 +                    unit_hash::check(cx, expr, recv, arg);
 +                },
 +                ("is_file", []) => filetype_is_file::check(cx, expr, recv),
-                         map_clone::check(cx, expr, recv, m_arg, 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", []) => {
 +                    iter_on_single_or_empty_collections::check(cx, expr, name, recv);
 +                },
 +                ("join", [join_arg]) => {
 +                    if let Some(("collect", _, _, span, _)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", []) | ("skip", [_]) => {
 +                    if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("lock", []) => {
 +                    mut_mutex_lock::check(cx, expr, recv, span);
 +                },
 +                (name @ ("map" | "map_err"), [m_arg]) => {
 +                    if name == "map" {
-                             ("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),
++                        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);
 +                        }
 +                    } else {
 +                        map_err_ignore::check(cx, expr, m_arg);
 +                    }
 +                    if let Some((name, recv2, args, span2,_)) = method_call(recv) {
 +                        match (name, args) {
-                             ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, 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);
 +                            },
 +                            ("find", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
 +                            },
 +                            _ => {},
 +                        }
 +                    }
 +                    map_identity::check(cx, expr, recv, m_arg, name, span);
 +                },
 +                ("map_or", [def, map]) => {
 +                    option_map_or_none::check(cx, expr, recv, def, map);
 +                    manual_ok_or::check(cx, expr, recv, def, map);
 +                },
 +                ("next", []) => {
 +                    if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
 +                        match (name2, args2) {
 +                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                            ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
-                     if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
++                            ("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),
 +                            _ => {},
 +                        }
 +                    }
 +                },
 +                ("nth", [n_arg]) => match method_call(recv) {
 +                    Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                    Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                    Some(("iter", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                    Some(("iter_mut", recv2, [], _, _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                    _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +                },
 +                ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +                ("open", [_]) => {
 +                    open_options::check(cx, expr, recv);
 +                },
 +                ("or_else", [arg]) => {
 +                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                    }
 +                },
 +                ("push", [arg]) => {
 +                    path_buf_push_overwrite::check(cx, expr, arg);
 +                },
 +                ("read_to_end", [_]) => {
 +                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
 +                },
 +                ("read_to_string", [_]) => {
 +                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
 +                },
 +                ("repeat", [arg]) => {
 +                    repeat_once::check(cx, expr, recv, arg);
 +                },
 +                (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
 +                    no_effect_replace::check(cx, expr, arg1, arg2);
 +
 +                    // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
 +                    if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
 +                        collapsible_str_replace::check(cx, expr, arg1, arg2);
 +                    }
 +                },
 +                ("resize", [count_arg, default_arg]) => {
 +                    vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
 +                },
 +                ("seek", [arg]) => {
-                     if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
++                    if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) {
 +                        seek_from_current::check(cx, expr, recv, arg);
 +                    }
-                         str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
++                    if self.msrv.meets(msrvs::SEEK_REWIND) {
 +                        seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
 +                    }
 +                },
 +                ("sort", []) => {
 +                    stable_sort_primitive::check(cx, expr, recv);
 +                },
 +                ("sort_by", [arg]) => {
 +                    unnecessary_sort_by::check(cx, expr, recv, arg, false);
 +                },
 +                ("sort_unstable_by", [arg]) => {
 +                    unnecessary_sort_by::check(cx, expr, recv, arg, true);
 +                },
 +                ("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);
-                     if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
++                        str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
 +                    }
 +                },
 +                ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                    }
 +                },
 +                ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +                ("take", [_arg]) => {
 +                    if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("take", []) => needless_option_take::check(cx, expr, recv),
 +                ("then", [arg]) => {
-                         if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
++                    if !self.msrv.meets(msrvs::BOOL_THEN_SOME) {
 +                        return;
 +                    }
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
 +                },
 +                ("to_owned", []) => {
 +                    if !suspicious_to_owned::check(cx, expr, recv) {
 +                        implicit_clone::check(cx, name, expr, recv);
 +                    }
 +                },
 +                ("to_os_string" | "to_path_buf" | "to_vec", []) => {
 +                    implicit_clone::check(cx, name, expr, recv);
 +                },
 +                ("unwrap", []) => {
 +                    match method_call(recv) {
 +                        Some(("get", recv, [get_arg], _, _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
 +                        Some(("get_mut", recv, [get_arg], _, _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
 +                        Some(("or", recv, [or_arg], or_span, _)) => {
 +                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                        },
 +                        _ => {},
 +                    }
 +                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
 +                },
 +                ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
 +                ("unwrap_or", [u_arg]) => match method_call(recv) {
 +                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
 +                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                    },
 +                    Some(("map", m_recv, [m_arg], span, _)) => {
 +                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                    },
 +                    Some(("then_some", t_recv, [t_arg], _, _)) => {
 +                        obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
 +                    },
 +                    _ => {},
 +                },
 +                ("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) => {},
 +                    _ => {
 +                        unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                        unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                    },
 +                },
 +                ("zip", [arg]) => {
 +                    if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind
 +                        && name.ident.name == sym::iter
 +                    {
 +                        range_zip_with_len::check(cx, expr, iter_recv, arg);
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span, _)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No, // When we want the first argument type to be different than `Self`
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_sym = match mutability {
 +                hir::Mutability::Not => sym::AsRef,
 +                hir::Mutability::Mut => sym::AsMut,
 +            };
 +
 +            let Some(trait_def_id) = cx.tcx.get_diagnostic_item(trait_sym) else {
 +                return false
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            !matches_value(cx, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
 +        }
 +
 +        match self {
 +            Self::Value => matches_value(cx, parent_ty, ty),
 +            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
 +            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
 +            Self::No => matches_none(cx, parent_ty, ty),
 +        }
 +    }
 +
 +    #[must_use]
 +    fn description(self) -> &'static str {
 +        match self {
 +            Self::Value => "`self` by value",
 +            Self::Ref => "`self` by reference",
 +            Self::RefMut => "`self` by mutable reference",
 +            Self::No => "no `self`",
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OutType {
 +    Unit,
 +    Bool,
 +    Any,
 +    Ref,
 +}
 +
 +impl OutType {
 +    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
 +        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
 +        match (self, ty) {
 +            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
 +            (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
 +            (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
 +            (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
 +            (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
 +    expected.constness == actual.constness
 +        && expected.unsafety == actual.unsafety
 +        && expected.asyncness == actual.asyncness
 +}
index e6eb64bcbde64ad0abd70e444e806a30ee51777d,0000000000000000000000000000000000000000..3e33f9193374e3a4c80b838624a9cddc162c30ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,120 @@@
- use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_span::sym;
 +
 +use super::OPTION_AS_REF_DEREF;
 +
 +/// lint use of `_.as_ref().map(Deref::deref)` for `Option`s
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    as_ref_recv: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +    is_mut: bool,
-     if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) {
++    msrv: &Msrv,
 +) {
++    if !msrv.meets(msrvs::OPTION_AS_DEREF) {
 +        return;
 +    }
 +
 +    let same_mutability = |m| (is_mut && m == &hir::Mutability::Mut) || (!is_mut && m == &hir::Mutability::Not);
 +
 +    let option_ty = cx.typeck_results().expr_ty(as_ref_recv);
 +    if !is_type_diagnostic_item(cx, option_ty, sym::Option) {
 +        return;
 +    }
 +
 +    let deref_aliases: [&[&str]; 8] = [
 +        &paths::DEREF_MUT_TRAIT_METHOD,
 +        &paths::CSTRING_AS_C_STR,
 +        &paths::OS_STRING_AS_OS_STR,
 +        &paths::PATH_BUF_AS_PATH,
 +        &paths::STRING_AS_STR,
 +        &paths::STRING_AS_MUT_STR,
 +        &paths::VEC_AS_SLICE,
 +        &paths::VEC_AS_MUT_SLICE,
 +    ];
 +
 +    let is_deref = match map_arg.kind {
 +        hir::ExprKind::Path(ref expr_qpath) => {
 +            cx.qpath_res(expr_qpath, map_arg.hir_id)
 +                .opt_def_id()
 +                .map_or(false, |fun_def_id| {
 +                    cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
 +                        || deref_aliases.iter().any(|path| match_def_path(cx, fun_def_id, path))
 +                })
 +        },
 +        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
 +            let closure_body = cx.tcx.hir().body(body);
 +            let closure_expr = peel_blocks(closure_body.value);
 +
 +            match &closure_expr.kind {
 +                hir::ExprKind::MethodCall(_, receiver, [], _) => {
 +                    if_chain! {
 +                        if path_to_local_id(receiver, closure_body.params[0].pat.hir_id);
 +                        let adj = cx
 +                            .typeck_results()
 +                            .expr_adjustments(receiver)
 +                            .iter()
 +                            .map(|x| &x.kind)
 +                            .collect::<Box<[_]>>();
 +                        if let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj;
 +                        then {
 +                            let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
 +                            cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
 +                                || deref_aliases.iter().any(|path| match_def_path(cx, method_did, path))
 +                        } else {
 +                            false
 +                        }
 +                    }
 +                },
 +                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, m, inner) if same_mutability(m) => {
 +                    if_chain! {
 +                        if let hir::ExprKind::Unary(hir::UnOp::Deref, inner1) = inner.kind;
 +                        if let hir::ExprKind::Unary(hir::UnOp::Deref, inner2) = inner1.kind;
 +                        then {
 +                            path_to_local_id(inner2, closure_body.params[0].pat.hir_id)
 +                        } else {
 +                            false
 +                        }
 +                    }
 +                },
 +                _ => false,
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_deref {
 +        let current_method = if is_mut {
 +            format!(".as_mut().map({})", snippet(cx, map_arg.span, ".."))
 +        } else {
 +            format!(".as_ref().map({})", snippet(cx, map_arg.span, ".."))
 +        };
 +        let method_hint = if is_mut { "as_deref_mut" } else { "as_deref" };
 +        let hint = format!("{}.{method_hint}()", snippet(cx, as_ref_recv.span, ".."));
 +        let suggestion = format!("try using {method_hint} instead");
 +
 +        let msg = format!(
 +            "called `{current_method}` on an Option value. This can be done more directly \
 +            by calling `{hint}` instead"
 +        );
 +        span_lint_and_sugg(
 +            cx,
 +            OPTION_AS_REF_DEREF,
 +            expr.span,
 +            &msg,
 +            &suggestion,
 +            hint,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 1acac59144cee8ce926daf8fdabfe5212918dae5,0000000000000000000000000000000000000000..3c01ce1fecd3a28a161625b72d63331405ff852a
mode 100644,000000..100644
--- /dev/null
@@@ -1,385 -1,0 +1,385 @@@
- use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
 +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 rustc_semver::RustcVersion;
++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;
 +use rustc_hir::{
 +    BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
-     msrv: Option<RustcVersion>,
 +use rustc_span::{sym, Span, Symbol, SyntaxContext};
 +
 +use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN};
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    method_name: &str,
 +    expr: &Expr<'_>,
 +    self_arg: &Expr<'_>,
 +    pat_arg: &Expr<'_>,
 +    count: u128,
-     let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE);
++    msrv: &Msrv,
 +) {
 +    if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() {
 +        return;
 +    }
 +
 +    let needless = |usage_kind| match usage_kind {
 +        IterUsageKind::Nth(n) => count > n + 1,
 +        IterUsageKind::NextTuple => count > 2,
 +    };
++    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),
 +        Some(usage) if manual => check_manual_split_once(cx, method_name, expr, self_arg, pat_arg, &usage),
 +        None if manual => {
 +            check_manual_split_once_indirect(cx, method_name, expr, self_arg, pat_arg);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +fn lint_needless(cx: &LateContext<'_>, method_name: &str, expr: &Expr<'_>, self_arg: &Expr<'_>, pat_arg: &Expr<'_>) {
 +    let mut app = Applicability::MachineApplicable;
 +    let r = if method_name == "splitn" { "" } else { "r" };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        NEEDLESS_SPLITN,
 +        expr.span,
 +        &format!("unnecessary use of `{r}splitn`"),
 +        "try this",
 +        format!(
 +            "{}.{r}split({})",
 +            snippet_with_context(cx, self_arg.span, expr.span.ctxt(), "..", &mut app).0,
 +            snippet_with_context(cx, pat_arg.span, expr.span.ctxt(), "..", &mut app).0,
 +        ),
 +        app,
 +    );
 +}
 +
 +fn check_manual_split_once(
 +    cx: &LateContext<'_>,
 +    method_name: &str,
 +    expr: &Expr<'_>,
 +    self_arg: &Expr<'_>,
 +    pat_arg: &Expr<'_>,
 +    usage: &IterUsage,
 +) {
 +    let ctxt = expr.span.ctxt();
 +    let (msg, reverse) = if method_name == "splitn" {
 +        ("manual implementation of `split_once`", false)
 +    } else {
 +        ("manual implementation of `rsplit_once`", true)
 +    };
 +
 +    let mut app = Applicability::MachineApplicable;
 +    let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
 +    let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
 +
 +    let sugg = match usage.kind {
 +        IterUsageKind::NextTuple => {
 +            if reverse {
 +                format!("{self_snip}.rsplit_once({pat_snip}).map(|(x, y)| (y, x))")
 +            } else {
 +                format!("{self_snip}.split_once({pat_snip})")
 +            }
 +        },
 +        IterUsageKind::Nth(1) => {
 +            let (r, field) = if reverse { ("r", 0) } else { ("", 1) };
 +
 +            match usage.unwrap_kind {
 +                Some(UnwrapKind::Unwrap) => {
 +                    format!("{self_snip}.{r}split_once({pat_snip}).unwrap().{field}")
 +                },
 +                Some(UnwrapKind::QuestionMark) => {
 +                    format!("{self_snip}.{r}split_once({pat_snip})?.{field}")
 +                },
 +                None => {
 +                    format!("{self_snip}.{r}split_once({pat_snip}).map(|x| x.{field})")
 +                },
 +            }
 +        },
 +        IterUsageKind::Nth(_) => return,
 +    };
 +
 +    span_lint_and_sugg(cx, MANUAL_SPLIT_ONCE, usage.span, msg, "try this", sugg, app);
 +}
 +
 +/// checks for
 +///
 +/// ```
 +/// let mut iter = "a.b.c".splitn(2, '.');
 +/// let a = iter.next();
 +/// let b = iter.next();
 +/// ```
 +fn check_manual_split_once_indirect(
 +    cx: &LateContext<'_>,
 +    method_name: &str,
 +    expr: &Expr<'_>,
 +    self_arg: &Expr<'_>,
 +    pat_arg: &Expr<'_>,
 +) -> Option<()> {
 +    let ctxt = expr.span.ctxt();
 +    let mut parents = cx.tcx.hir().parent_iter(expr.hir_id);
 +    if let (_, Node::Local(local)) = parents.next()?
 +        && let PatKind::Binding(BindingAnnotation::MUT, iter_binding_id, iter_ident, None) = local.pat.kind
 +        && let (iter_stmt_id, Node::Stmt(_)) = parents.next()?
 +        && let (_, Node::Block(enclosing_block)) = parents.next()?
 +
 +        && let mut stmts = enclosing_block
 +            .stmts
 +            .iter()
 +            .skip_while(|stmt| stmt.hir_id != iter_stmt_id)
 +            .skip(1)
 +
 +        && let first = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
 +        && let second = indirect_usage(cx, stmts.next()?, iter_binding_id, ctxt)?
 +        && first.unwrap_kind == second.unwrap_kind
 +        && first.name != second.name
 +        && !local_used_after_expr(cx, iter_binding_id, second.init_expr)
 +    {
 +        let (r, lhs, rhs) = if method_name == "splitn" {
 +            ("", first.name, second.name)
 +        } else {
 +            ("r", second.name, first.name)
 +        };
 +        let msg = format!("manual implementation of `{r}split_once`");
 +
 +        let mut app = Applicability::MachineApplicable;
 +        let self_snip = snippet_with_context(cx, self_arg.span, ctxt, "..", &mut app).0;
 +        let pat_snip = snippet_with_context(cx, pat_arg.span, ctxt, "..", &mut app).0;
 +
 +        span_lint_and_then(cx, MANUAL_SPLIT_ONCE, local.span, &msg, |diag| {
 +            diag.span_label(first.span, "first usage here");
 +            diag.span_label(second.span, "second usage here");
 +
 +            let unwrap = match first.unwrap_kind {
 +                UnwrapKind::Unwrap => ".unwrap()",
 +                UnwrapKind::QuestionMark => "?",
 +            };
 +            diag.span_suggestion_verbose(
 +                local.span,
 +                &format!("try `{r}split_once`"),
 +                format!("let ({lhs}, {rhs}) = {self_snip}.{r}split_once({pat_snip}){unwrap};"),
 +                app,
 +            );
 +
 +            let remove_msg = format!("remove the `{iter_ident}` usages");
 +            diag.span_suggestion(
 +                first.span,
 +                &remove_msg,
 +                "",
 +                app,
 +            );
 +            diag.span_suggestion(
 +                second.span,
 +                &remove_msg,
 +                "",
 +                app,
 +            );
 +        });
 +    }
 +
 +    Some(())
 +}
 +
 +#[derive(Debug)]
 +struct IndirectUsage<'a> {
 +    name: Symbol,
 +    span: Span,
 +    init_expr: &'a Expr<'a>,
 +    unwrap_kind: UnwrapKind,
 +}
 +
 +/// returns `Some(IndirectUsage)` for e.g.
 +///
 +/// ```ignore
 +/// let name = binding.next()?;
 +/// let name = binding.next().unwrap();
 +/// ```
 +fn indirect_usage<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    stmt: &Stmt<'tcx>,
 +    binding: HirId,
 +    ctxt: SyntaxContext,
 +) -> Option<IndirectUsage<'tcx>> {
 +    if let StmtKind::Local(&Local {
 +        pat: Pat {
 +            kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
 +            ..
 +        },
 +        init: Some(init_expr),
 +        hir_id: local_hir_id,
 +        ..
 +    }) = stmt.kind
 +    {
 +        let mut path_to_binding = None;
 +        let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
 +            if path_to_local_id(e, binding) {
 +                path_to_binding = Some(e);
 +            }
 +            ControlFlow::Continue(Descend::from(path_to_binding.is_none()))
 +        });
 +
 +        let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
 +        let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
 +
 +        let (parent_id, _) = parents.find(|(_, node)| {
 +            !matches!(
 +                node,
 +                Node::Expr(Expr {
 +                    kind: ExprKind::Match(.., MatchSource::TryDesugar),
 +                    ..
 +                })
 +            )
 +        })?;
 +
 +        if let IterUsage {
 +            kind: IterUsageKind::Nth(0),
 +            unwrap_kind: Some(unwrap_kind),
 +            ..
 +        } = iter_usage
 +        {
 +            if parent_id == local_hir_id {
 +                return Some(IndirectUsage {
 +                    name: ident.name,
 +                    span: stmt.span,
 +                    init_expr,
 +                    unwrap_kind,
 +                });
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum IterUsageKind {
 +    Nth(u128),
 +    NextTuple,
 +}
 +
 +#[derive(Debug, PartialEq, Eq)]
 +enum UnwrapKind {
 +    Unwrap,
 +    QuestionMark,
 +}
 +
 +#[derive(Debug)]
 +struct IterUsage {
 +    kind: IterUsageKind,
 +    unwrap_kind: Option<UnwrapKind>,
 +    span: Span,
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn parse_iter_usage<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ctxt: SyntaxContext,
 +    mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>,
 +) -> Option<IterUsage> {
 +    let (kind, span) = match iter.next() {
 +        Some((_, Node::Expr(e))) if e.span.ctxt() == ctxt => {
 +            let ExprKind::MethodCall(name, _, [args @ ..], _) = e.kind else {
 +                return None;
 +            };
 +            let did = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
 +            let iter_id = cx.tcx.get_diagnostic_item(sym::Iterator)?;
 +
 +            match (name.ident.as_str(), args) {
 +                ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
 +                ("next_tuple", []) => {
 +                    return if_chain! {
 +                        if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE);
 +                        if let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind();
 +                        if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did());
 +                        if let ty::Tuple(subs) = subs.type_at(0).kind();
 +                        if subs.len() == 2;
 +                        then {
 +                            Some(IterUsage {
 +                                kind: IterUsageKind::NextTuple,
 +                                span: e.span,
 +                                unwrap_kind: None
 +                            })
 +                        } else {
 +                            None
 +                        }
 +                    };
 +                },
 +                ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
 +                    if let Some((Constant::Int(idx), _)) = constant(cx, cx.typeck_results(), idx_expr) {
 +                        let span = if name.ident.as_str() == "nth" {
 +                            e.span
 +                        } else {
 +                            if_chain! {
 +                                if let Some((_, Node::Expr(next_expr))) = iter.next();
 +                                if let ExprKind::MethodCall(next_name, _, [], _) = next_expr.kind;
 +                                if next_name.ident.name == sym::next;
 +                                if next_expr.span.ctxt() == ctxt;
 +                                if let Some(next_id) = cx.typeck_results().type_dependent_def_id(next_expr.hir_id);
 +                                if cx.tcx.trait_of_item(next_id) == Some(iter_id);
 +                                then {
 +                                    next_expr.span
 +                                } else {
 +                                    return None;
 +                                }
 +                            }
 +                        };
 +                        (IterUsageKind::Nth(idx), span)
 +                    } else {
 +                        return None;
 +                    }
 +                },
 +                _ => return None,
 +            }
 +        },
 +        _ => return None,
 +    };
 +
 +    let (unwrap_kind, span) = if let Some((_, Node::Expr(e))) = iter.next() {
 +        match e.kind {
 +            ExprKind::Call(
 +                Expr {
 +                    kind: ExprKind::Path(QPath::LangItem(LangItem::TryTraitBranch, ..)),
 +                    ..
 +                },
 +                _,
 +            ) => {
 +                let parent_span = e.span.parent_callsite().unwrap();
 +                if parent_span.ctxt() == ctxt {
 +                    (Some(UnwrapKind::QuestionMark), parent_span)
 +                } else {
 +                    (None, span)
 +                }
 +            },
 +            _ if e.span.ctxt() != ctxt => (None, span),
 +            ExprKind::MethodCall(name, _, [], _)
 +                if name.ident.name == sym::unwrap
 +                    && cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
 +            {
 +                (Some(UnwrapKind::Unwrap), e.span)
 +            },
 +            _ => (None, span),
 +        }
 +    } else {
 +        (None, span)
 +    };
 +
 +    Some(IterUsage {
 +        kind,
 +        unwrap_kind,
 +        span,
 +    })
 +}
index 7ff13b95626ba89b22d603caa7fbda300a0aaef1,0000000000000000000000000000000000000000..17b0507682ae905b9e7fb438f76eef3022d317b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,508 -1,0 +1,509 @@@
- 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 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 rustc_middle::ty::EarlyBinder;
- use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
- use rustc_semver::RustcVersion;
++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};
 +use rustc_infer::infer::TyCtxtInferExt;
 +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_trait_selection::traits::{
-     query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause,
- };
- use std::cmp::max;
++use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 +use rustc_span::{sym, Symbol};
-     msrv: Option<RustcVersion>,
++use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
 +
 +use super::UNNECESSARY_TO_OWNED;
 +
 +pub fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'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);
 +        if args.is_empty();
 +        then {
 +            if is_cloned_or_copied(cx, method_name, method_def_id) {
 +                unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
 +            } else if is_to_owned_like(cx, expr, method_name, method_def_id) {
 +                // At this point, we know the call is of a `to_owned`-like function. The functions
 +                // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
 +                // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
 +                // argument in a `into_iter` call, or an argument in the call of some other function.
 +                if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
 +                    return;
 +                }
 +                if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
 +                    return;
 +                }
 +                check_other_call_arg(cx, expr, method_name, receiver);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +#[allow(clippy::too_many_lines)]
 +fn check_addr_of_expr(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +    receiver: &Expr<'_>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
 +        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
 +        if let
 +            // For matching uses of `Cow::from`
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching uses of arrays
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Pointer(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching everything else
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ] = adjustments[..];
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
 +        let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
 +        // Only flag cases satisfying at least one of the following three conditions:
 +        // * the referent and receiver types are distinct
 +        // * the referent/receiver type is a copyable array
 +        // * the method is `Cow::into_owned`
 +        // This restriction is to ensure there is no overlap between `redundant_clone` and this
 +        // lint. It also avoids the following false positive:
 +        //  https://github.com/rust-lang/rust-clippy/issues/8759
 +        //   Arrays are a bit of a corner case. Non-copyable arrays are handled by
 +        // `redundant_clone`, but copyable arrays are not.
 +        if *referent_ty != receiver_ty
 +            || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
 +            || is_cow_into_owned(cx, method_name, method_def_id);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNNECESSARY_TO_OWNED,
 +                    parent.span,
 +                    &format!("unnecessary use of `{method_name}`"),
 +                    "use",
 +                    format!(
 +                        "{:&>width$}{receiver_snippet}",
 +                        "",
 +                        width = n_target_refs - n_receiver_refs
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +                return true;
 +            }
 +            if_chain! {
 +                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
 +                if cx.get_associated_type(receiver_ty, deref_trait_id, "Target") == Some(target_ty);
 +                then {
 +                    if n_receiver_refs > 0 {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            parent.span,
 +                            &format!("unnecessary use of `{method_name}`"),
 +                            "use",
 +                            receiver_snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    } else {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            expr.span.with_lo(receiver.span.hi()),
 +                            &format!("unnecessary use of `{method_name}`"),
 +                            "remove this",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                    return true;
 +                }
 +            }
 +            if_chain! {
 +                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
 +                then {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNNECESSARY_TO_OWNED,
 +                        parent.span,
 +                        &format!("unnecessary use of `{method_name}`"),
 +                        "use",
 +                        format!("{receiver_snippet}.as_ref()"),
 +                        Applicability::MachineApplicable,
 +                    );
 +                    return true;
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +fn check_into_iter_call_arg(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
-             let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
++    msrv: &Msrv,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let Some(callee_def_id) = fn_def_id(cx, parent);
 +        if is_into_iter(cx, callee_def_id);
 +        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        let parent_ty = cx.typeck_results().expr_ty(parent);
 +        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
 +        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
 +                return true;
 +            }
-         if can_change_type(cx, maybe_arg, receiver_ty);
++            let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) {
 +                "copied"
 +            } else {
 +                "cloned"
 +            };
 +            // The next suggestion may be incorrect because the removal of the `to_owned`-like
 +            // function could cause the iterator to hold a reference to a resource that is used
 +            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                parent.span,
 +                &format!("unnecessary use of `{method_name}`"),
 +                "use",
 +                format!("{receiver_snippet}.iter().{cloned_or_copied}()"),
 +                Applicability::MaybeIncorrect,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
 +/// of a `to_owned`-like function is unnecessary.
 +fn check_other_call_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
 +        if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
 +        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +        if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id);
 +        if let Some(input) = fn_sig.inputs().get(i);
 +        let (input, n_refs) = peel_mid_ty_refs(*input);
 +        if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input);
 +        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
 +        if let [trait_predicate] = trait_predicates
 +            .iter()
 +            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
 +            .collect::<Vec<_>>()[..];
 +        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +        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 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)));
 +        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
 +        // `Target = T`.
-             }
++        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(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                maybe_arg.span,
 +                &format!("unnecessary use of `{method_name}`"),
 +                "use",
 +                format!("{:&>n_refs$}{receiver_snippet}", ""),
 +                Applicability::MachineApplicable,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
 +/// expression found (if any) along with the immediately prior expression.
 +fn skip_addr_of_ancestors<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mut expr: &'tcx Expr<'tcx>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
 +    while let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
 +            expr = parent;
 +        } else {
 +            return Some((parent, expr));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
 +/// `Substs`, and arguments.
 +fn get_callee_substs_and_args<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +) -> Option<(DefId, SubstsRef<'tcx>, Option<&'tcx Expr<'tcx>>, &'tcx [Expr<'tcx>])> {
 +    if_chain! {
 +        if let ExprKind::Call(callee, args) = expr.kind;
 +        let callee_ty = cx.typeck_results().expr_ty(callee);
 +        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
 +        then {
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            return Some((*callee_def_id, substs, None, args));
 +        }
 +    }
 +    if_chain! {
 +        if let ExprKind::MethodCall(_, recv, args, _) = expr.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        then {
 +            let substs = cx.typeck_results().node_substs(expr.hir_id);
 +            return Some((method_def_id, substs, Some(recv), args));
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
 +fn get_input_traits_and_projections<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    callee_def_id: DefId,
 +    input: Ty<'tcx>,
 +) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
 +    let mut trait_predicates = Vec::new();
 +    let mut projection_predicates = Vec::new();
 +    for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
 +        match predicate.kind().skip_binder() {
 +            PredicateKind::Clause(Clause::Trait(trait_predicate)) => {
 +                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);
 +                }
-                             if let PredicateKind::Clause(Clause::Trait(trait_predicate)) =  predicate.kind().skip_binder()
-                                 && trait_predicate.trait_ref.self_ty() == *param_ty {
-                                     true
-                                 } else {
++            },
++            _ => {},
 +        }
 +    }
 +    (trait_predicates, projection_predicates)
 +}
 +
 +fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
 +    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Stmt(_) => return true,
 +            Node::Block(..) => continue,
 +            Node::Item(item) => {
 +                if let ItemKind::Fn(_, _, body_id) = &item.kind
 +                && let output_ty = return_ty(cx, item.hir_id())
 +                && let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
 +                && Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
 +                    let fn_ctxt = FnCtxt::new(inherited, cx.param_env, item.hir_id());
 +                    fn_ctxt.can_coerce(ty, output_ty)
 +                }) {
 +                    if has_lifetime(output_ty) && has_lifetime(ty) {
 +                        return false;
 +                    }
 +                    let body = cx.tcx.hir().body(*body_id);
 +                    let body_expr = &body.value;
 +                    let mut count = 0;
 +                    return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
 +                }
 +            }
 +            Node::Expr(parent_expr) => {
 +                if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
 +                {
 +                    if Some(callee_def_id) == cx.tcx.lang_items().into_future_fn() {
 +                        return false;
 +                    }
 +
 +                    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +                    if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
 +                        && let Some(param_ty) = fn_sig.inputs().get(arg_index)
 +                        && let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
 +                    {
 +                        if fn_sig
 +                            .inputs()
 +                            .iter()
 +                            .enumerate()
 +                            .filter(|(i, _)| *i != arg_index)
 +                            .any(|(_, ty)| ty.contains(*param_ty))
 +                        {
 +                            return false;
 +                        }
 +
 +                        let mut trait_predicates = cx.tcx.param_env(callee_def_id)
 +                            .caller_bounds().iter().filter(|predicate| {
- fn is_to_owned_like<'a>(
-     cx: &LateContext<'a>,
-     call_expr: &Expr<'a>,
-     method_name: Symbol,
-     method_def_id: DefId,
- ) -> bool {
++                            if let PredicateKind::Clause(Clause::Trait(trait_predicate))
++                                    = predicate.kind().skip_binder()
++                                && trait_predicate.trait_ref.self_ty() == *param_ty
++                            {
++                                true
++                            } else {
 +                                false
 +                            }
 +                        });
 +
 +                        let new_subst = cx.tcx.mk_substs(
 +                            call_substs.iter()
 +                                .enumerate()
 +                                .map(|(i, t)|
 +                                     if i == (*param_index as usize) {
 +                                         GenericArg::from(ty)
 +                                     } else {
 +                                         t
 +                                     }));
 +
 +                        if trait_predicates.any(|predicate| {
 +                            let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
 +                            let obligation = Obligation::new(cx.tcx, ObligationCause::dummy(), cx.param_env, predicate);
 +                            !cx.tcx.infer_ctxt().build().predicate_must_hold_modulo_regions(&obligation)
 +                        }) {
 +                            return false;
 +                        }
 +
 +                        let output_ty = fn_sig.output();
 +                        if output_ty.contains(*param_ty) {
 +                            if let Ok(new_ty)  = cx.tcx.try_subst_and_normalize_erasing_regions(
 +                                new_subst, cx.param_env, output_ty) {
 +                                expr = parent_expr;
 +                                ty = new_ty;
 +                                continue;
 +                            }
 +                            return false;
 +                        }
 +
 +                        return true;
 +                    }
 +                } else if let ExprKind::Block(..) = parent_expr.kind {
 +                    continue;
 +                }
 +                return false;
 +            },
 +            _ => return false,
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn has_lifetime(ty: Ty<'_>) -> bool {
 +    ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_)))
 +}
 +
 +/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
 +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
 +        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
 +}
 +
 +/// 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 {
 +    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)
 +}
 +
 +/// Returns true if the named method is `Cow::into_owned`.
 +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
 +}
 +
 +/// Returns true if the named method is `ToString::to_string` and it's called on a type that
 +/// is string-like i.e. implements `AsRef<str>` or `Deref<Target = str>`.
 +fn is_to_string_on_string_like<'a>(
 +    cx: &LateContext<'_>,
 +    call_expr: &'a Expr<'a>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +) -> bool {
 +    if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
 +        return false;
 +    }
 +
 +    if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
 +        && let [generic_arg] = substs.as_slice()
 +        && let GenericArgKind::Type(ty) = generic_arg.unpack()
 +        && let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
 +        && let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
 +        && (cx.get_associated_type(ty, deref_trait_id, "Target") == Some(cx.tcx.types.str_) ||
 +            implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
 +            true
 +        } else {
 +            false
 +        }
 +}
index 71cc0d0a81cd2dd8bc909dd8780c79f857b919a0,0000000000000000000000000000000000000000..5bc04bc17fb4f00bdf2cba1c1d9db4b08e8991a4
mode 100644,000000..100644
--- /dev/null
@@@ -1,180 -1,0 +1,178 @@@
- use clippy_utils::{
-     fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method,
- };
 +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 rustc_semver::RustcVersion;
++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;
 +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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Suggests the use of `const` in functions and methods where possible.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not having the function const prevents callers of the function from being const as well.
 +    ///
 +    /// ### Known problems
 +    /// Const functions are currently still being worked on, with some features only being available
 +    /// on nightly. This lint does not consider all edge cases currently and the suggestions may be
 +    /// incorrect if you are using this lint on stable.
 +    ///
 +    /// Also, the lint only runs one pass over the code. Consider these two non-const functions:
 +    ///
 +    /// ```rust
 +    /// fn a() -> i32 {
 +    ///     0
 +    /// }
 +    /// fn b() -> i32 {
 +    ///     a()
 +    /// }
 +    /// ```
 +    ///
 +    /// When running Clippy, the lint will only suggest to make `a` const, because `b` at this time
 +    /// can't be const as it calls a non-const function. Making `a` const and running Clippy again,
 +    /// will suggest to make `b` const, too.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct Foo {
 +    /// #     random_number: usize,
 +    /// # }
 +    /// # impl Foo {
 +    /// fn new() -> Self {
 +    ///     Self { random_number: 42 }
 +    /// }
 +    /// # }
 +    /// ```
 +    ///
 +    /// Could be a const fn:
 +    ///
 +    /// ```rust
 +    /// # struct Foo {
 +    /// #     random_number: usize,
 +    /// # }
 +    /// # impl Foo {
 +    /// const fn new() -> Self {
 +    ///     Self { random_number: 42 }
 +    /// }
 +    /// # }
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub MISSING_CONST_FOR_FN,
 +    nursery,
 +    "Lint functions definitions that could be made `const fn`"
 +}
 +
 +impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]);
 +
 +pub struct MissingConstForFn {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl MissingConstForFn {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        _: &FnDecl<'_>,
 +        body: &Body<'tcx>,
 +        span: Span,
 +        hir_id: HirId,
 +    ) {
-         if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) {
++        if !self.msrv.meets(msrvs::CONST_IF_MATCH) {
 +            return;
 +        }
 +
 +        let def_id = cx.tcx.hir().local_def_id(hir_id);
 +
 +        if in_external_macro(cx.tcx.sess, span) || is_entrypoint_fn(cx, def_id.to_def_id()) {
 +            return;
 +        }
 +
 +        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
 +        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
 +            return;
 +        }
 +
 +        // Perform some preliminary checks that rule out constness on the Clippy side. This way we
 +        // can skip the actual const check and return early.
 +        match kind {
 +            FnKind::ItemFn(_, generics, header, ..) => {
 +                let has_const_generic_params = generics
 +                    .params
 +                    .iter()
 +                    .any(|param| matches!(param.kind, GenericParamKind::Const { .. }));
 +
 +                if already_const(header) || has_const_generic_params {
 +                    return;
 +                }
 +            },
 +            FnKind::Method(_, sig, ..) => {
 +                if trait_ref_of_method(cx, def_id).is_some()
 +                    || already_const(sig.header)
 +                    || method_accepts_droppable(cx, sig.decl.inputs)
 +                {
 +                    return;
 +                }
 +            },
 +            FnKind::Closure => return,
 +        }
 +
 +        // Const fns are not allowed as methods in a trait.
 +        {
 +            let parent = cx.tcx.hir().get_parent_item(hir_id).def_id;
 +            if parent != CRATE_DEF_ID {
 +                if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent) {
 +                    if let hir::ItemKind::Trait(..) = &item.kind {
 +                        return;
 +                    }
 +                }
 +            }
 +        }
 +
 +        if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) {
 +            return;
 +        }
 +
 +        let mir = cx.tcx.optimized_mir(def_id);
 +
++        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());
 +            }
 +        } else {
 +            span_lint(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`");
 +        }
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Returns true if any of the method parameters is a type that implements `Drop`. The method
 +/// can't be made const then, because `drop` can't be const-evaluated.
 +fn method_accepts_droppable(cx: &LateContext<'_>, param_tys: &[hir::Ty<'_>]) -> bool {
 +    // If any of the params are droppable, return true
 +    param_tys.iter().any(|hir_ty| {
 +        let ty_ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +        has_drop(cx, ty_ty)
 +    })
 +}
 +
 +// We don't have to lint on something that's already `const`
 +#[must_use]
 +fn already_const(header: hir::FnHeader) -> bool {
 +    header.constness == Constness::Const
 +}
index 75e12715458f4faf44f9605e6b22ce4470e36fb0,0000000000000000000000000000000000000000..2f0b7ce16e51b77541bdee6927dea53c98bdb18e
mode 100644,000000..100644
--- /dev/null
@@@ -1,344 -1,0 +1,348 @@@
- use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item};
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::ptr::get_spans;
 +use clippy_utils::source::{snippet, snippet_opt};
-                     Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred),
++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;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::{Applicability, Diagnostic};
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    BindingAnnotation, Body, FnDecl, GenericArg, HirId, Impl, ItemKind, Mutability, Node, PatKind, QPath, TyKind,
 +};
 +use rustc_hir::{HirIdMap, HirIdSet, LangItem};
 +use rustc_hir_typeck::expr_use_visitor as euv;
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::FakeReadCause;
 +use rustc_middle::ty::{self, TypeVisitable};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::kw;
 +use rustc_span::{sym, Span};
 +use rustc_target::spec::abi::Abi;
 +use rustc_trait_selection::traits;
 +use rustc_trait_selection::traits::misc::can_type_implement_copy;
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions taking arguments by value, but not
 +    /// consuming them in its
 +    /// body.
 +    ///
 +    /// ### Why is this bad?
 +    /// Taking arguments by reference is more flexible and can
 +    /// sometimes avoid
 +    /// unnecessary allocations.
 +    ///
 +    /// ### Known problems
 +    /// * This lint suggests taking an argument by reference,
 +    /// however sometimes it is better to let users decide the argument type
 +    /// (by using `Borrow` trait, for example), depending on how the function is used.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(v: Vec<i32>) {
 +    ///     assert_eq!(v.len(), 42);
 +    /// }
 +    /// ```
 +    /// should be
 +    /// ```rust
 +    /// fn foo(v: &[i32]) {
 +    ///     assert_eq!(v.len(), 42);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_PASS_BY_VALUE,
 +    pedantic,
 +    "functions taking arguments by value, but not consuming them in its body"
 +}
 +
 +declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);
 +
 +macro_rules! need {
 +    ($e: expr) => {
 +        if let Some(x) = $e {
 +            x
 +        } else {
 +            return;
 +        }
 +    };
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        hir_id: HirId,
 +    ) {
 +        if span.from_expansion() {
 +            return;
 +        }
 +
 +        match kind {
 +            FnKind::ItemFn(.., header) => {
 +                let attrs = cx.tcx.hir().attrs(hir_id);
 +                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
 +                    return;
 +                }
 +            },
 +            FnKind::Method(..) => (),
 +            FnKind::Closure => return,
 +        }
 +
 +        // Exclude non-inherent impls
 +        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +            if matches!(
 +                item.kind,
 +                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
 +            ) {
 +                return;
 +            }
 +        }
 +
 +        // Allow `Borrow` or functions to be taken by value
 +        let allowed_traits = [
 +            need!(cx.tcx.lang_items().fn_trait()),
 +            need!(cx.tcx.lang_items().fn_once_trait()),
 +            need!(cx.tcx.lang_items().fn_mut_trait()),
 +            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
 +        ];
 +
 +        let sized_trait = need!(cx.tcx.lang_items().sized_trait());
 +
 +        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
 +
 +        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
 +            .filter(|p| !p.is_global())
 +            .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)
++                    },
 +                    _ => None,
 +                }
 +            })
 +            .collect::<Vec<_>>();
 +
 +        // Collect moved variables and spans which will need dereferencings from the
 +        // function body.
 +        let MovedVariablesCtxt {
 +            moved_vars,
 +            spans_need_deref,
 +            ..
 +        } = {
 +            let mut ctx = MovedVariablesCtxt::default();
 +            let infcx = cx.tcx.infer_ctxt().build();
 +            euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
 +            ctx
 +        };
 +
 +        let fn_sig = cx.tcx.fn_sig(fn_def_id);
 +        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
 +
 +        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
 +            // All spans generated from a proc-macro invocation are the same...
 +            if span == input.span {
 +                return;
 +            }
 +
 +            // Ignore `self`s.
 +            if idx == 0 {
 +                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
 +                    if ident.name == kw::SelfLower {
 +                        continue;
 +                    }
 +                }
 +            }
 +
 +            //
 +            // * Exclude a type that is specifically bounded by `Borrow`.
 +            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
 +            //   `serde::Serialize`)
 +            let (implements_borrow_trait, all_borrowable_trait) = {
 +                let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();
 +
 +                (
 +                    preds.iter().any(|t| cx.tcx.is_diagnostic_item(sym::Borrow, t.def_id())),
 +                    !preds.is_empty() && {
 +                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_erased, ty);
 +                        preds.iter().all(|t| {
 +                            let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
 +                            implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
 +                        })
 +                    },
 +                )
 +            };
 +
 +            if_chain! {
 +                if !is_self(arg);
 +                if !ty.is_mutable_ptr();
 +                if !is_copy(cx, ty);
 +                if ty.is_sized(cx.tcx, cx.param_env);
 +                if !allowed_traits.iter().any(|&t| implements_trait_with_env(cx.tcx, cx.param_env, ty, t, [None]));
 +                if !implements_borrow_trait;
 +                if !all_borrowable_trait;
 +
 +                if let PatKind::Binding(BindingAnnotation(_, Mutability::Not), canonical_id, ..) = arg.pat.kind;
 +                if !moved_vars.contains(&canonical_id);
 +                then {
 +                    // Dereference suggestion
 +                    let sugg = |diag: &mut Diagnostic| {
 +                        if let ty::Adt(def, ..) = ty.kind() {
 +                            if let Some(span) = cx.tcx.hir().span_if_local(def.did()) {
 +                                if can_type_implement_copy(
 +                                    cx.tcx,
 +                                    cx.param_env,
 +                                    ty,
 +                                    traits::ObligationCause::dummy_with_span(span),
 +                                ).is_ok() {
 +                                    diag.span_help(span, "consider marking this type as `Copy`");
 +                                }
 +                            }
 +                        }
 +
 +                        let deref_span = spans_need_deref.get(&canonical_id);
 +                        if_chain! {
 +                            if is_type_diagnostic_item(cx, ty, sym::Vec);
 +                            if let Some(clone_spans) =
 +                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
 +                            if let TyKind::Path(QPath::Resolved(_, path)) = input.kind;
 +                            if let Some(elem_ty) = path.segments.iter()
 +                                .find(|seg| seg.ident.name == sym::Vec)
 +                                .and_then(|ps| ps.args.as_ref())
 +                                .map(|params| params.args.iter().find_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                }).unwrap());
 +                            then {
 +                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
 +                                diag.span_suggestion(
 +                                    input.span,
 +                                    "consider changing the type to",
 +                                    slice_ty,
 +                                    Applicability::Unspecified,
 +                                );
 +
 +                                for (span, suggestion) in clone_spans {
 +                                    diag.span_suggestion(
 +                                        span,
 +                                        snippet_opt(cx, span)
 +                                            .map_or(
 +                                                "change the call to".into(),
 +                                                |x| Cow::from(format!("change `{x}` to")),
 +                                            )
 +                                            .as_ref(),
 +                                        suggestion,
 +                                        Applicability::Unspecified,
 +                                    );
 +                                }
 +
 +                                // cannot be destructured, no need for `*` suggestion
 +                                assert!(deref_span.is_none());
 +                                return;
 +                            }
 +                        }
 +
 +                        if is_type_lang_item(cx, ty, LangItem::String) {
 +                            if let Some(clone_spans) =
 +                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
 +                                diag.span_suggestion(
 +                                    input.span,
 +                                    "consider changing the type to",
 +                                    "&str",
 +                                    Applicability::Unspecified,
 +                                );
 +
 +                                for (span, suggestion) in clone_spans {
 +                                    diag.span_suggestion(
 +                                        span,
 +                                        snippet_opt(cx, span)
 +                                            .map_or(
 +                                                "change the call to".into(),
 +                                                |x| Cow::from(format!("change `{x}` to"))
 +                                            )
 +                                            .as_ref(),
 +                                        suggestion,
 +                                        Applicability::Unspecified,
 +                                    );
 +                                }
 +
 +                                assert!(deref_span.is_none());
 +                                return;
 +                            }
 +                        }
 +
 +                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
 +
 +                        // Suggests adding `*` to dereference the added reference.
 +                        if let Some(deref_span) = deref_span {
 +                            spans.extend(
 +                                deref_span
 +                                    .iter()
 +                                    .copied()
 +                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
 +                            );
 +                            spans.sort_by_key(|&(span, _)| span);
 +                        }
 +                        multispan_sugg(diag, "consider taking a reference instead", spans);
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_PASS_BY_VALUE,
 +                        input.span,
 +                        "this argument is passed by value, but not consumed in the function body",
 +                        sugg,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Functions marked with these attributes must have the exact signature.
 +fn requires_exact_signature(attrs: &[Attribute]) -> bool {
 +    attrs.iter().any(|attr| {
 +        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
 +            .iter()
 +            .any(|&allow| attr.has_name(allow))
 +    })
 +}
 +
 +#[derive(Default)]
 +struct MovedVariablesCtxt {
 +    moved_vars: HirIdSet,
 +    /// Spans which need to be prefixed with `*` for dereferencing the
 +    /// suggested additional reference.
 +    spans_need_deref: HirIdMap<FxHashSet<Span>>,
 +}
 +
 +impl MovedVariablesCtxt {
 +    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
 +        if let euv::PlaceBase::Local(vid) = cmt.place.base {
 +            self.moved_vars.insert(vid);
 +        }
 +    }
 +}
 +
 +impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
 +    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId) {
 +        self.move_common(cmt);
 +    }
 +
 +    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}
 +
 +    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
index 819646bb6780e98099d405f0a1bd8993de003393,0000000000000000000000000000000000000000..79c1ae4861e805d8edefa6f127ef200d1ea4a97d
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,281 @@@
- use rustc_lint::{LateContext, LateLintPass};
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 +use clippy_utils::is_lint_allowed;
 +use clippy_utils::peel_blocks;
 +use clippy_utils::source::snippet_opt;
 +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};
-         if !&reduced.iter().any(|e| e.span.from_expansion());
++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;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unlike dead code, these statements are actually
 +    /// executed. However, as they have no effect, all they do is make the code less
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 0;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NO_EFFECT,
 +    complexity,
 +    "statements with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for binding to underscore prefixed variable without side-effects.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unlike dead code, these bindings are actually
 +    /// executed. However, as they have no effect and shouldn't be used further on, all they
 +    /// do is make the code less readable.
 +    ///
 +    /// ### Known problems
 +    /// Further usage of this variable is not checked, which can lead to false positives if it is
 +    /// used later in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _i_serve_no_purpose = 1;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub NO_EFFECT_UNDERSCORE_BINDING,
 +    pedantic,
 +    "binding to `_` prefixed variable with no side-effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expression statements that can be reduced to a
 +    /// sub-expression.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions by themselves often have no side-effects.
 +    /// Having such expressions reduces readability.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// compute_array()[0];
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_OPERATION,
 +    complexity,
 +    "outer expressions with no effect"
 +}
 +
 +declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NoEffect {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if check_no_effect(cx, stmt) {
 +            return;
 +        }
 +        check_unnecessary_operation(cx, stmt);
 +    }
 +}
 +
 +fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
 +    if let StmtKind::Semi(expr) = stmt.kind {
 +        if has_no_effect(cx, expr) {
 +            span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
 +            return true;
 +        }
 +    } else if let StmtKind::Local(local) = stmt.kind {
 +        if_chain! {
 +            if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id);
 +            if let Some(init) = local.init;
 +            if local.els.is_none();
 +            if !local.pat.span.from_expansion();
 +            if has_no_effect(cx, init);
 +            if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
 +            if ident.name.to_ident_string().starts_with('_');
 +            then {
 +                span_lint_hir(
 +                    cx,
 +                    NO_EFFECT_UNDERSCORE_BINDING,
 +                    init.hir_id,
 +                    stmt.span,
 +                    "binding to `_` prefixed variable with no side-effect"
 +                );
 +                return true;
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if expr.span.from_expansion() {
 +        return false;
 +    }
 +    match peel_blocks(expr).kind {
 +        ExprKind::Lit(..) | ExprKind::Closure { .. } => true,
 +        ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
 +        ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
 +        ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => has_no_effect(cx, inner),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            !has_drop(cx, cx.typeck_results().expr_ty(expr))
 +                && fields.iter().all(|field| has_no_effect(cx, field.expr))
 +                && base.as_ref().map_or(true, |base| has_no_effect(cx, base))
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
 +                    // type-dependent function call like `impl FnOnce for X`
 +                    return false;
 +                }
 +                let def_matched = matches!(
 +                    cx.qpath_res(qpath, callee.hir_id),
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                );
 +                if def_matched || is_range_literal(expr) {
 +                    !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        },
 +        _ => false,
 +    }
 +}
 +
 +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 !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)) =
 +                    (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span))
 +                {
 +                    format!("assert!({}.len() > {});", &arr, &func)
 +                } else {
 +                    return;
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    UNNECESSARY_OPERATION,
 +                    expr.hir_id,
 +                    stmt.span,
 +                    "unnecessary operation",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "statement can be written as",
 +                            snippet,
 +                            Applicability::MaybeIncorrect,
 +                        );
 +                    },
 +                );
 +            } else {
 +                let mut snippet = String::new();
 +                for e in reduced {
 +                    if let Some(snip) = snippet_opt(cx, e.span) {
 +                        snippet.push_str(&snip);
 +                        snippet.push(';');
 +                    } else {
 +                        return;
 +                    }
 +                }
 +                span_lint_hir_and_then(
 +                    cx,
 +                    UNNECESSARY_OPERATION,
 +                    expr.hir_id,
 +                    stmt.span,
 +                    "unnecessary operation",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "statement can be reduced to",
 +                            snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
 +    if expr.span.from_expansion() {
 +        return None;
 +    }
 +    match expr.kind {
 +        ExprKind::Index(a, b) => Some(vec![a, b]),
 +        ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
 +            Some(vec![a, b])
 +        },
 +        ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
 +                None
 +            } else {
 +                Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
 +            }
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                if cx.typeck_results().type_dependent_def(expr.hir_id).is_some() {
 +                    // type-dependent function call like `impl FnOnce for X`
 +                    return None;
 +                }
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                match res {
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                        if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
 +                    {
 +                        Some(args.iter().collect())
 +                    },
 +                    _ => None,
 +                }
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            if block.stmts.is_empty() {
 +                block.expr.as_ref().and_then(|e| {
 +                    match block.rules {
 +                        BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
 +                        BlockCheckMode::DefaultBlock => Some(vec![&**e]),
 +                        // in case of compiler-inserted signaling blocks
 +                        BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
 +                    }
 +                })
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index 92920bbad6e0a9a394ce2797f9e1a0ef90b635e7,0000000000000000000000000000000000000000..e395ff54cb15a00f0693e6f8007f9d4ab1585aab
mode 100644,000000..100644
--- /dev/null
@@@ -1,722 -1,0 +1,723 @@@
-                             lt: lt.clone(),
 +//! Checks for usage of  `&Vec[_]` and `&String`.
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::expr_sig;
 +use clippy_utils::visitors::contains_unsafe_block;
 +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
 +use if_chain::if_chain;
 +use rustc_errors::{Applicability, MultiSpan};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::HirIdMap;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{
 +    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg,
 +    ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind,
 +    TyKind, Unsafety,
 +};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_infer::traits::{Obligation, ObligationCause};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::{self, Binder, Clause, ExistentialPredicate, List, PredicateKind, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +use rustc_trait_selection::infer::InferCtxtExt as _;
 +use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
 +use std::fmt;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for function arguments of type `&String`, `&Vec`,
 +    /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
 +    /// with the appropriate `.to_owned()`/`to_string()` calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Requiring the argument to be of the specific size
 +    /// makes the function less useful for no benefit; slices in the form of `&[T]`
 +    /// or `&str` usually suffice and can be obtained from other types, too.
 +    ///
 +    /// ### Known problems
 +    /// There may be `fn(&Vec)`-typed references pointing to your function.
 +    /// If you have them, you will get a compiler error after applying this lint's
 +    /// suggestions. You then have the choice to undo your changes or change the
 +    /// type of the reference.
 +    ///
 +    /// Note that if the function is part of your public interface, there may be
 +    /// other crates referencing it, of which you may not be aware. Carefully
 +    /// deprecate the function before applying the lint suggestions in this case.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn foo(&Vec<u32>) { .. }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// fn foo(&[u32]) { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PTR_ARG,
 +    style,
 +    "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for equality comparisons with `ptr::null`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easier and more readable to use the inherent
 +    /// `.is_null()`
 +    /// method instead
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::ptr;
 +    ///
 +    /// if x == ptr::null {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if x.is_null() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_NULL,
 +    style,
 +    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for functions that take immutable references and return
 +    /// mutable ones. This will not trigger if no unsafe code exists as there
 +    /// are multiple safe functions which will do this transformation
 +    ///
 +    /// To be on the conservative side, if there's at least one mutable
 +    /// reference with the output lifetime, this lint will not trigger.
 +    ///
 +    /// ### Why is this bad?
 +    /// Creating a mutable reference which can be repeatably derived from an
 +    /// immutable reference is unsound as it allows creating multiple live
 +    /// mutable references to the same object.
 +    ///
 +    /// This [error](https://github.com/rust-lang/rust/issues/39465) actually
 +    /// lead to an interim Rust release 1.15.1.
 +    ///
 +    /// ### Known problems
 +    /// This pattern is used by memory allocators to allow allocating multiple
 +    /// objects while returning mutable references to each one. So long as
 +    /// different mutable references are returned each time such a function may
 +    /// be safe.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn foo(&Foo) -> &mut Bar { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MUT_FROM_REF,
 +    correctness,
 +    "fns that create mutable refs from immutable ref args"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for invalid usages of `ptr::null`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This causes undefined behavior.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Undefined behavior
 +    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub INVALID_NULL_PTR_USAGE,
 +    correctness,
 +    "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
 +}
 +
 +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ptr {
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
 +            if matches!(trait_method, TraitFn::Provided(_)) {
 +                // Handled by check body.
 +                return;
 +            }
 +
 +            check_mut_from_ref(cx, sig, None);
 +            for arg in check_fn_args(
 +                cx,
 +                cx.tcx.fn_sig(item.owner_id).skip_binder().inputs(),
 +                sig.decl.inputs,
 +                &[],
 +            )
 +            .filter(|arg| arg.mutability() == Mutability::Not)
 +            {
 +                span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| {
 +                    diag.span_suggestion(
 +                        arg.span,
 +                        "change this to",
 +                        format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
 +                        Applicability::Unspecified,
 +                    );
 +                });
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        let mut parents = hir.parent_iter(body.value.hir_id);
 +        let (item_id, sig, is_trait_item) = match parents.next() {
 +            Some((_, Node::Item(i))) => {
 +                if let ItemKind::Fn(sig, ..) = &i.kind {
 +                    (i.owner_id, sig, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::ImplItem(i))) => {
 +                if !matches!(parents.next(),
 +                    Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
 +                ) {
 +                    return;
 +                }
 +                if let ImplItemKind::Fn(sig, _) = &i.kind {
 +                    (i.owner_id, sig, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::TraitItem(i))) => {
 +                if let TraitItemKind::Fn(sig, _) = &i.kind {
 +                    (i.owner_id, sig, true)
 +                } else {
 +                    return;
 +                }
 +            },
 +            _ => return,
 +        };
 +
 +        check_mut_from_ref(cx, sig, Some(body));
 +        let decl = sig.decl;
 +        let sig = cx.tcx.fn_sig(item_id).skip_binder();
 +        let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
 +            .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
 +            .collect();
 +        let results = check_ptr_arg_usage(cx, body, &lint_args);
 +
 +        for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
 +            span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, &args.build_msg(), |diag| {
 +                diag.multipart_suggestion(
 +                    "change this to",
 +                    iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
 +                        .chain(result.replacements.iter().map(|r| {
 +                            (
 +                                r.expr_span,
 +                                format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
 +                            )
 +                        }))
 +                        .collect(),
 +                    Applicability::Unspecified,
 +                );
 +            });
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref op, l, r) = expr.kind {
 +            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
 +                span_lint(
 +                    cx,
 +                    CMP_NULL,
 +                    expr.span,
 +                    "comparing with null is better expressed by the `.is_null()` method",
 +                );
 +            }
 +        } else {
 +            check_invalid_ptr_usage(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
 +    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
 +        (&paths::SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_COPY, &[0, 1]),
 +        (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_READ, &[0]),
 +        (&paths::PTR_READ_UNALIGNED, &[0]),
 +        (&paths::PTR_READ_VOLATILE, &[0]),
 +        (&paths::PTR_REPLACE, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_SWAP, &[0, 1]),
 +        (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_WRITE, &[0]),
 +        (&paths::PTR_WRITE_UNALIGNED, &[0]),
 +        (&paths::PTR_WRITE_VOLATILE, &[0]),
 +        (&paths::PTR_WRITE_BYTES, &[0]),
 +    ];
 +
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
 +        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
 +            .iter()
 +            .find(|&&(fn_path, _)| fn_path == fun_def_path);
 +        then {
 +            for &arg_idx in arg_indices {
 +                if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        INVALID_NULL_PTR_USAGE,
 +                        arg.span,
 +                        "pointer must be non-null",
 +                        "change this to",
 +                        "core::ptr::NonNull::dangling().as_ptr()".to_string(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +struct PtrArgResult {
 +    skip: bool,
 +    replacements: Vec<PtrArgReplacement>,
 +}
 +
 +struct PtrArgReplacement {
 +    expr_span: Span,
 +    self_span: Span,
 +    replacement: &'static str,
 +}
 +
 +struct PtrArg<'tcx> {
 +    idx: usize,
 +    emission_id: hir::HirId,
 +    span: Span,
 +    ty_did: DefId,
 +    ty_name: Symbol,
 +    method_renames: &'static [(&'static str, &'static str)],
 +    ref_prefix: RefPrefix,
 +    deref_ty: DerefTy<'tcx>,
 +}
 +impl PtrArg<'_> {
 +    fn build_msg(&self) -> String {
 +        format!(
 +            "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.ty_name,
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.deref_ty.argless_str(),
 +        )
 +    }
 +
 +    fn mutability(&self) -> Mutability {
 +        self.ref_prefix.mutability
 +    }
 +}
 +
 +struct RefPrefix {
 +    lt: Lifetime,
 +    mutability: Mutability,
 +}
 +impl fmt::Display for RefPrefix {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use fmt::Write;
 +        f.write_char('&')?;
 +        if !self.lt.is_anonymous() {
 +            self.lt.ident.fmt(f)?;
 +            f.write_char(' ')?;
 +        }
 +        f.write_str(self.mutability.prefix_str())
 +    }
 +}
 +
 +struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
 +impl fmt::Display for DerefTyDisplay<'_, '_> {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use std::fmt::Write;
 +        match self.1 {
 +            DerefTy::Str => f.write_str("str"),
 +            DerefTy::Path => f.write_str("Path"),
 +            DerefTy::Slice(hir_ty, ty) => {
 +                f.write_char('[')?;
 +                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
 +                    Some(s) => f.write_str(&s)?,
 +                    None => ty.fmt(f)?,
 +                }
 +                f.write_char(']')
 +            },
 +        }
 +    }
 +}
 +
 +enum DerefTy<'tcx> {
 +    Str,
 +    Path,
 +    Slice(Option<Span>, Ty<'tcx>),
 +}
 +impl<'tcx> DerefTy<'tcx> {
 +    fn ty(&self, cx: &LateContext<'tcx>) -> Ty<'tcx> {
 +        match *self {
 +            Self::Str => cx.tcx.types.str_,
 +            Self::Path => cx.tcx.mk_adt(
 +                cx.tcx.adt_def(cx.tcx.get_diagnostic_item(sym::Path).unwrap()),
 +                List::empty(),
 +            ),
 +            Self::Slice(_, ty) => cx.tcx.mk_slice(ty),
 +        }
 +    }
 +
 +    fn argless_str(&self) -> &'static str {
 +        match *self {
 +            Self::Str => "str",
 +            Self::Path => "Path",
 +            Self::Slice(..) => "[_]",
 +        }
 +    }
 +
 +    fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
 +        DerefTyDisplay(cx, self)
 +    }
 +}
 +
 +fn check_fn_args<'cx, 'tcx: 'cx>(
 +    cx: &'cx LateContext<'tcx>,
 +    tys: &'tcx [Ty<'tcx>],
 +    hir_tys: &'tcx [hir::Ty<'tcx>],
 +    params: &'tcx [Param<'tcx>],
 +) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
 +    tys.iter()
 +        .zip(hir_tys.iter())
 +        .enumerate()
 +        .filter_map(|(i, (ty, hir_ty))| {
 +            if_chain! {
 +                if let ty::Ref(_, ty, mutability) = *ty.kind();
 +                if let ty::Adt(adt, substs) = *ty.kind();
 +
 +                if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
 +                if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
 +
 +                // Check that the name as typed matches the actual name of the type.
 +                // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
 +                if let [.., name] = path.segments;
 +                if cx.tcx.item_name(adt.did()) == name.ident.name;
 +
 +                then {
 +                    let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
 +                    let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
 +                        Some(sym::Vec) => (
 +                            [("clone", ".to_owned()")].as_slice(),
 +                            DerefTy::Slice(
 +                                name.args
 +                                    .and_then(|args| args.args.first())
 +                                    .and_then(|arg| if let GenericArg::Type(ty) = arg {
 +                                        Some(ty.span)
 +                                    } else {
 +                                        None
 +                                    }),
 +                                substs.type_at(0),
 +                            ),
 +                        ),
 +                        _ if Some(adt.did()) == cx.tcx.lang_items().string() => (
 +                            [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
 +                            DerefTy::Str,
 +                        ),
 +                        Some(sym::PathBuf) => (
 +                            [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
 +                            DerefTy::Path,
 +                        ),
 +                        Some(sym::Cow) if mutability == Mutability::Not => {
 +                            let ty_name = name.args
 +                                .and_then(|args| {
 +                                    args.args.iter().find_map(|a| match a {
 +                                        GenericArg::Type(x) => Some(x),
 +                                        _ => None,
 +                                    })
 +                                })
 +                                .and_then(|arg| snippet_opt(cx, arg.span))
 +                                .unwrap_or_else(|| substs.type_at(1).to_string());
 +                            span_lint_hir_and_then(
 +                                cx,
 +                                PTR_ARG,
 +                                emission_id,
 +                                hir_ty.span,
 +                                "using a reference to `Cow` is not recommended",
 +                                |diag| {
 +                                    diag.span_suggestion(
 +                                        hir_ty.span,
 +                                        "change this to",
 +                                        format!("&{}{ty_name}", mutability.prefix_str()),
 +                                        Applicability::Unspecified,
 +                                    );
 +                                }
 +                            );
 +                            return None;
 +                        },
 +                        _ => return None,
 +                    };
 +                    return Some(PtrArg {
 +                        idx: i,
 +                        emission_id,
 +                        span: hir_ty.span,
 +                        ty_did: adt.did(),
 +                        ty_name: name.ident.name,
 +                        method_renames,
 +                        ref_prefix: RefPrefix {
-             cx.tcx.mk_predicate(Binder::dummy(
-                 PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))),
-             )),
++                            lt: *lt,
 +                            mutability,
 +                        },
 +                        deref_ty,
 +                    });
 +                }
 +            }
 +            None
 +        })
 +}
 +
 +fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Option<&'tcx Body<'_>>) {
 +    if let FnRetTy::Return(ty) = sig.decl.output
 +        && let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty)
 +    {
 +        let out_region = cx.tcx.named_region(out.hir_id);
 +        let args: Option<Vec<_>> = sig
 +            .decl
 +            .inputs
 +            .iter()
 +            .filter_map(get_rptr_lm)
 +            .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region)
 +            .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span))
 +            .collect();
 +        if let Some(args) = args
 +            && !args.is_empty()
 +            && body.map_or(true, |body| {
 +                sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, body.value)
 +            })
 +        {
 +            span_lint_and_then(
 +                cx,
 +                MUT_FROM_REF,
 +                ty.span,
 +                "mutable borrow from immutable input(s)",
 +                |diag| {
 +                    let ms = MultiSpan::from_spans(args);
 +                    diag.span_note(ms, "immutable borrow here");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        /// Map from a local id to which argument it came from (index into `Self::args` and
 +        /// `Self::results`)
 +        bindings: HirIdMap<usize>,
 +        /// The arguments being checked.
 +        args: &'cx [PtrArg<'tcx>],
 +        /// The results for each argument (len should match args.len)
 +        results: Vec<PtrArgResult>,
 +        /// The number of arguments which can't be linted. Used to return early.
 +        skip_count: usize,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.skip_count == self.args.len() {
 +                return;
 +            }
 +
 +            // Check if this is local we care about
 +            let Some(&args_idx) = path_to_local(e).and_then(|id| self.bindings.get(&id)) else {
 +                return walk_expr(self, e);
 +            };
 +            let args = &self.args[args_idx];
 +            let result = &mut self.results[args_idx];
 +
 +            // Helper function to handle early returns.
 +            let mut set_skip_flag = || {
 +                if !result.skip {
 +                    self.skip_count += 1;
 +                }
 +                result.skip = true;
 +            };
 +
 +            match get_expr_use_or_unification_node(self.cx.tcx, e) {
 +                Some((Node::Stmt(_), _)) => (),
 +                Some((Node::Local(l), _)) => {
 +                    // Only trace simple bindings. e.g `let x = y;`
 +                    if let PatKind::Binding(BindingAnnotation::NONE, id, _, None) = l.pat.kind {
 +                        self.bindings.insert(id, args_idx);
 +                    } else {
 +                        set_skip_flag();
 +                    }
 +                },
 +                Some((Node::Expr(e), child_id)) => match e.kind {
 +                    ExprKind::Call(f, expr_args) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
 +                            match *ty.skip_binder().peel_refs().kind() {
 +                                ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds),
 +                                ty::Param(_) => true,
 +                                ty::Adt(def, _) => def.did() == args.ty_did,
 +                                _ => false,
 +                            }
 +                        }) {
 +                            // Passed to a function taking the non-dereferenced type.
 +                            set_skip_flag();
 +                        }
 +                    },
 +                    ExprKind::MethodCall(name, self_arg, expr_args, _) => {
 +                        let i = std::iter::once(self_arg)
 +                            .chain(expr_args.iter())
 +                            .position(|arg| arg.hir_id == child_id)
 +                            .unwrap_or(0);
 +                        if i == 0 {
 +                            // Check if the method can be renamed.
 +                            let name = name.ident.as_str();
 +                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
 +                                result.replacements.push(PtrArgReplacement {
 +                                    expr_span: e.span,
 +                                    self_span: self_arg.span,
 +                                    replacement,
 +                                });
 +                                return;
 +                            }
 +                        }
 +
 +                        let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else {
 +                            set_skip_flag();
 +                            return;
 +                        };
 +
 +                        match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
 +                            ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => {
 +                                set_skip_flag();
 +                            },
 +                            ty::Param(_) => {
 +                                set_skip_flag();
 +                            },
 +                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
 +                            // `slice::len`
 +                            ty::Adt(def, _) if def.did() == args.ty_did => {
 +                                set_skip_flag();
 +                            },
 +                            _ => (),
 +                        }
 +                    },
 +                    // Indexing is fine for currently supported types.
 +                    ExprKind::Index(e, _) if e.hir_id == child_id => (),
 +                    _ => set_skip_flag(),
 +                },
 +                _ => set_skip_flag(),
 +            }
 +        }
 +    }
 +
 +    let mut skip_count = 0;
 +    let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
 +    let mut v = V {
 +        cx,
 +        bindings: args
 +            .iter()
 +            .enumerate()
 +            .filter_map(|(i, arg)| {
 +                let param = &body.params[arg.idx];
 +                match param.pat.kind {
 +                    PatKind::Binding(BindingAnnotation::NONE, id, _, None)
 +                        if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
 +                    {
 +                        Some((id, i))
 +                    },
 +                    _ => {
 +                        skip_count += 1;
 +                        results[i].skip = true;
 +                        None
 +                    },
 +                }
 +            })
 +            .collect(),
 +        args,
 +        results,
 +        skip_count,
 +    };
 +    v.visit_expr(body.value);
 +    v.results
 +}
 +
 +fn matches_preds<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    preds: &'tcx [ty::PolyExistentialPredicate<'tcx>],
 +) -> bool {
 +    let infcx = cx.tcx.infer_ctxt().build();
 +    preds.iter().all(|&p| match cx.tcx.erase_late_bound_regions(p) {
 +        ExistentialPredicate::Trait(p) => infcx
 +            .type_implements_trait(p.def_id, [ty.into()].into_iter().chain(p.substs.iter()), cx.param_env)
 +            .must_apply_modulo_regions(),
 +        ExistentialPredicate::Projection(p) => infcx.predicate_must_hold_modulo_regions(&Obligation::new(
 +            cx.tcx,
 +            ObligationCause::dummy(),
 +            cx.param_env,
++            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)
 +            .must_apply_modulo_regions(),
 +    })
 +}
 +
 +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
 +    if let TyKind::Rptr(lt, ref m) = ty.kind {
 +        Some((lt, m.mutbl, ty.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(pathexp, []) = expr.kind {
 +        path_def_id(cx, pathexp).map_or(false, |id| {
 +            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
 +        })
 +    } else {
 +        false
 +    }
 +}
index c6fbb5e805ab2812a6da2d7db80b5ca4ec7d834d,0000000000000000000000000000000000000000..0a1b9d173cf9409ee453de6f7085ac14b9f8ab89
mode 100644,000000..100644
--- /dev/null
@@@ -1,532 -1,0 +1,532 @@@
- use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
 +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 rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::{Span, Spanned};
 +use std::cmp::Ordering;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for exclusive ranges where 1 is added to the
 +    /// upper bound, e.g., `x..(y+1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an inclusive range
 +    /// like `x..=y`.
 +    ///
 +    /// ### Known problems
 +    /// Will add unnecessary pair of parentheses when the
 +    /// expression is not wrapped in a pair but starts with an opening parenthesis
 +    /// and ends with a closing one.
 +    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
 +    ///
 +    /// Also in many cases, inclusive ranges are still slower to run than
 +    /// exclusive ranges, because they essentially add an extra branch that
 +    /// LLVM may fail to hoist out of the loop.
 +    ///
 +    /// This will cause a warning that cannot be fixed if the consumer of the
 +    /// range only accepts a specific range type, instead of the generic
 +    /// `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..(y+1) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..=y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_PLUS_ONE,
 +    pedantic,
 +    "`x..(y+1)` reads better as `x..=y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for inclusive ranges where 1 is subtracted from
 +    /// the upper bound, e.g., `x..=(y-1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an exclusive range
 +    /// like `x..y`.
 +    ///
 +    /// ### Known problems
 +    /// This will cause a warning that cannot be fixed if
 +    /// the consumer of the range only accepts a specific range type, instead of
 +    /// the generic `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..=(y-1) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_MINUS_ONE,
 +    pedantic,
 +    "`x..=(y-1)` reads better as `x..y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for range expressions `x..y` where both `x` and `y`
 +    /// are constant and `x` is greater or equal to `y`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Empty ranges yield no values so iterating them is a no-op.
 +    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// fn main() {
 +    ///     (10..=0).for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[3..1];
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     (0..=10).rev().for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[1..3];
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub REVERSED_EMPTY_RANGES,
 +    correctness,
 +    "reversing the limits of range expressions, resulting in empty ranges"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions like `x >= 3 && x < 8` that could
 +    /// be more readably expressed as `(3..8).contains(x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `contains` expresses the intent better and has less
 +    /// failure modes (such as fencepost errors or using `||` instead of `&&`).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // given
 +    /// let x = 6;
 +    ///
 +    /// assert!(x >= 3 && x < 8);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    ///# let x = 6;
 +    /// assert!((3..8).contains(&x));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_RANGE_CONTAINS,
 +    style,
 +    "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 +}
 +
 +pub struct Ranges {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl Ranges {
 +    #[must_use]
-             if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Ranges => [
 +    RANGE_PLUS_ONE,
 +    RANGE_MINUS_ONE,
 +    REVERSED_EMPTY_RANGES,
 +    MANUAL_RANGE_CONTAINS,
 +]);
 +
 +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 self.msrv.meets(msrvs::RANGE_CONTAINS) {
 +                check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
 +            }
 +        }
 +
 +        check_exclusive_range_plus_one(cx, expr);
 +        check_inclusive_range_minus_one(cx, expr);
 +        check_reversed_empty_range(cx, expr);
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_possible_range_contains(
 +    cx: &LateContext<'_>,
 +    op: BinOpKind,
 +    left: &Expr<'_>,
 +    right: &Expr<'_>,
 +    expr: &Expr<'_>,
 +    span: Span,
 +) {
 +    if in_constant(cx, expr.hir_id) {
 +        return;
 +    }
 +
 +    let combine_and = match op {
 +        BinOpKind::And | BinOpKind::BitAnd => true,
 +        BinOpKind::Or | BinOpKind::BitOr => false,
 +        _ => return,
 +    };
 +    // value, name, order (higher/lower), inclusiveness
 +    if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) {
 +        // we only lint comparisons on the same name and with different
 +        // direction
 +        if l.id != r.id || l.ord == r.ord {
 +            return;
 +        }
 +        let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val);
 +        if combine_and && ord == Some(r.ord) {
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            // we only lint inclusive lower bounds
 +            if !l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("RangeInclusive", "..=")
 +            } else {
 +                ("Range", "..")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `{range_type}::contains` implementation"),
 +                "use",
 +                format!("({lo}{space}{range_op}{hi}).contains(&{name})"),
 +                applicability,
 +            );
 +        } else if !combine_and && ord == Some(l.ord) {
 +            // `!_.contains(_)`
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            if l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("Range", "..")
 +            } else {
 +                ("RangeInclusive", "..=")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `!{range_type}::contains` implementation"),
 +                "use",
 +                format!("!({lo}{space}{range_op}{hi}).contains(&{name})"),
 +                applicability,
 +            );
 +        }
 +    }
 +
 +    // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have
 +    // the same operator precedence
 +    if_chain! {
 +        if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind;
 +        if op == lhs_op.node;
 +        let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent());
 +        if let Some(snip) = &snippet_opt(cx, new_span);
 +        // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
 +        if snip.matches('(').count() == snip.matches(')').count();
 +        then {
 +            check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
 +        }
 +    }
 +}
 +
 +struct RangeBounds<'a> {
 +    val: Constant,
 +    expr: &'a Expr<'a>,
 +    id: HirId,
 +    name_span: Span,
 +    val_span: Span,
 +    ord: Ordering,
 +    inc: bool,
 +}
 +
 +// Takes a binary expression such as x <= 2 as input
 +// Breaks apart into various pieces, such as the value of the number,
 +// hir id of the variable, and direction/inclusiveness of the operator
 +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
 +    if let ExprKind::Binary(ref op, l, r) = ex.kind {
 +        let (inclusive, ordering) = match op.node {
 +            BinOpKind::Gt => (false, Ordering::Greater),
 +            BinOpKind::Ge => (true, Ordering::Greater),
 +            BinOpKind::Lt => (false, Ordering::Less),
 +            BinOpKind::Le => (true, Ordering::Less),
 +            _ => return None,
 +        };
 +        if let Some(id) = path_to_local(l) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: r,
 +                    id,
 +                    name_span: l.span,
 +                    val_span: r.span,
 +                    ord: ordering,
 +                    inc: inclusive,
 +                });
 +            }
 +        } else if let Some(id) = path_to_local(r) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: l,
 +                    id,
 +                    name_span: r.span,
 +                    val_span: l.span,
 +                    ord: ordering.reverse(),
 +                    inc: inclusive,
 +                });
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +// exclusive range plus one: `x..(y+1)`
 +fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if expr.span.can_be_used_for_suggestions();
 +        if let Some(higher::Range {
 +            start,
 +            end: Some(end),
 +            limits: RangeLimits::HalfOpen
 +        }) = higher::Range::hir(expr);
 +        if let Some(y) = y_plus_one(cx, end);
 +        then {
 +            let span = expr.span;
 +            span_lint_and_then(
 +                cx,
 +                RANGE_PLUS_ONE,
 +                span,
 +                "an inclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
 +                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("({start}..={end})"),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        } else {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("{start}..={end}"),
 +                                Applicability::MachineApplicable, // snippet
 +                            );
 +                        }
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +// inclusive range minus one: `x..=(y-1)`
 +fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if expr.span.can_be_used_for_suggestions();
 +        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
 +        if let Some(y) = y_minus_one(cx, end);
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                RANGE_MINUS_ONE,
 +                expr.span,
 +                "an exclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "use",
 +                        format!("{start}..{end}"),
 +                        Applicability::MachineApplicable, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        matches!(
 +            get_parent_expr(cx, expr),
 +            Some(Expr {
 +                kind: ExprKind::Index(..),
 +                ..
 +            })
 +        )
 +    }
 +
 +    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        let mut cur_expr = expr;
 +        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
 +            match higher::ForLoop::hir(parent_expr) {
 +                Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true,
 +                _ => cur_expr = parent_expr,
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
 +        match limits {
 +            RangeLimits::HalfOpen => ordering != Ordering::Less,
 +            RangeLimits::Closed => ordering == Ordering::Greater,
 +        }
 +    }
 +
 +    if_chain! {
 +        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
 +        let ty = cx.typeck_results().expr_ty(start);
 +        if let ty::Int(_) | ty::Uint(_) = ty.kind();
 +        if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
 +        if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
 +        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
 +        if is_empty_range(limits, ordering);
 +        then {
 +            if inside_indexing_expr(cx, expr) {
 +                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
 +                if ordering != Ordering::Equal {
 +                    span_lint(
 +                        cx,
 +                        REVERSED_EMPTY_RANGES,
 +                        expr.span,
 +                        "this range is reversed and using it to index a slice will panic at run-time",
 +                    );
 +                }
 +            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
 +            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
 +                span_lint_and_then(
 +                    cx,
 +                    REVERSED_EMPTY_RANGES,
 +                    expr.span,
 +                    "this range is empty so it will yield no values",
 +                    |diag| {
 +                        if ordering != Ordering::Equal {
 +                            let start_snippet = snippet(cx, start.span, "_");
 +                            let end_snippet = snippet(cx, end.span, "_");
 +                            let dots = match limits {
 +                                RangeLimits::HalfOpen => "..",
 +                                RangeLimits::Closed => "..="
 +                            };
 +
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using the following if you are attempting to iterate over this \
 +                                 range in reverse",
 +                                format!("({end_snippet}{dots}{start_snippet}).rev()"),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) => {
 +            if is_integer_const(cx, lhs, 1) {
 +                Some(rhs)
 +            } else if is_integer_const(cx, rhs, 1) {
 +                Some(lhs)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Sub, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
 +        _ => None,
 +    }
 +}
index 8e675d34a183698b970e86e0fca4f5dd27c1903d,0000000000000000000000000000000000000000..2a42e73488f1905c4b98cb45ea1fee522baaf567
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,166 @@@
-                                 let app = Applicability::MachineApplicable;
-                                 let mut hint = Sugg::ast(cx, body, "..");
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_ast::visit as ast_visit;
 +use rustc_ast::visit::Visitor as AstVisitor;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit as hir_visit;
 +use rustc_hir::intravisit::Visitor as HirVisitor;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects closures called in the same expression where they
 +    /// are defined.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is unnecessarily adding to the expression's
 +    /// complexity.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = (|| 42)();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = 42;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_CLOSURE_CALL,
 +    complexity,
 +    "throwaway closures called in the expression they are defined"
 +}
 +
 +declare_lint_pass!(RedundantClosureCall => [REDUNDANT_CLOSURE_CALL]);
 +
 +// Used to find `return` statements or equivalents e.g., `?`
 +struct ReturnVisitor {
 +    found_return: bool,
 +}
 +
 +impl ReturnVisitor {
 +    #[must_use]
 +    fn new() -> Self {
 +        Self { found_return: false }
 +    }
 +}
 +
 +impl<'ast> ast_visit::Visitor<'ast> for ReturnVisitor {
 +    fn visit_expr(&mut self, ex: &'ast ast::Expr) {
 +        if let ast::ExprKind::Ret(_) | ast::ExprKind::Try(_) = ex.kind {
 +            self.found_return = true;
 +        }
 +
 +        ast_visit::walk_expr(self, ex);
 +    }
 +}
 +
 +impl EarlyLintPass for RedundantClosureCall {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +        if_chain! {
 +            if let ast::ExprKind::Call(ref paren, _) = expr.kind;
 +            if let ast::ExprKind::Paren(ref closure) = paren.kind;
 +            if let ast::ExprKind::Closure(box ast::Closure { ref asyncness, ref fn_decl, ref body, .. }) = closure.kind;
 +            then {
 +                let mut visitor = ReturnVisitor::new();
 +                visitor.visit_expr(body);
 +                if !visitor.found_return {
 +                    span_lint_and_then(
 +                        cx,
 +                        REDUNDANT_CLOSURE_CALL,
 +                        expr.span,
 +                        "try not to call a closure in the expression where it is declared",
 +                        |diag| {
 +                            if fn_decl.inputs.is_empty() {
++                                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 }`
 +                                    if !matches!(body.kind, ast::ExprKind::Block(_, _)) {
 +                                        hint = hint.blockify();
 +                                    }
 +
 +                                    hint = hint.asyncify();
 +                                }
 +
 +                                diag.span_suggestion(expr.span, "try doing something like", hint.to_string(), app);
 +                            }
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        fn count_closure_usage<'tcx>(
 +            cx: &LateContext<'tcx>,
 +            block: &'tcx hir::Block<'_>,
 +            path: &'tcx hir::Path<'tcx>,
 +        ) -> usize {
 +            struct ClosureUsageCount<'a, 'tcx> {
 +                cx: &'a LateContext<'tcx>,
 +                path: &'tcx hir::Path<'tcx>,
 +                count: usize,
 +            }
 +            impl<'a, 'tcx> hir_visit::Visitor<'tcx> for ClosureUsageCount<'a, 'tcx> {
 +                type NestedFilter = nested_filter::OnlyBodies;
 +
 +                fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +                    if_chain! {
 +                        if let hir::ExprKind::Call(closure, _) = expr.kind;
 +                        if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind;
 +                        if self.path.segments[0].ident == path.segments[0].ident;
 +                        if self.path.res == path.res;
 +                        then {
 +                            self.count += 1;
 +                        }
 +                    }
 +                    hir_visit::walk_expr(self, expr);
 +                }
 +
 +                fn nested_visit_map(&mut self) -> Self::Map {
 +                    self.cx.tcx.hir()
 +                }
 +            }
 +            let mut closure_usage_count = ClosureUsageCount { cx, path, count: 0 };
 +            closure_usage_count.visit_block(block);
 +            closure_usage_count.count
 +        }
 +
 +        for w in block.stmts.windows(2) {
 +            if_chain! {
 +                if let hir::StmtKind::Local(local) = w[0].kind;
 +                if let Option::Some(t) = local.init;
 +                if let hir::ExprKind::Closure { .. } = t.kind;
 +                if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind;
 +                if let hir::StmtKind::Semi(second) = w[1].kind;
 +                if let hir::ExprKind::Assign(_, call, _) = second.kind;
 +                if let hir::ExprKind::Call(closure, _) = call.kind;
 +                if let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = closure.kind;
 +                if ident == path.segments[0].ident;
 +                if count_closure_usage(cx, block, path) == 1;
 +                then {
 +                    span_lint(
 +                        cx,
 +                        REDUNDANT_CLOSURE_CALL,
 +                        second.span,
 +                        "closure called just once immediately after it was declared",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 40b03068f6c772059167ff6814824d1d0846e33e,0000000000000000000000000000000000000000..61bff4a0e38d842194be07d7d8bd960436aed2fe
mode 100644,000000..100644
--- /dev/null
@@@ -1,86 -1,0 +1,85 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
- use rustc_semver::RustcVersion;
++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;
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for fields in struct literals where shorthands
 +    /// could be used.
 +    ///
 +    /// ### Why is this bad?
 +    /// If the field and variable names are the same,
 +    /// the field name is redundant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let bar: u8 = 123;
 +    ///
 +    /// struct Foo {
 +    ///     bar: u8,
 +    /// }
 +    ///
 +    /// let foo = Foo { bar: bar };
 +    /// ```
 +    /// the last line can be simplified to
 +    /// ```ignore
 +    /// let foo = Foo { bar };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_FIELD_NAMES,
 +    style,
 +    "checks for fields in struct literals where shorthands could be used"
 +}
 +
 +pub struct RedundantFieldNames {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl RedundantFieldNames {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);
 +
 +impl EarlyLintPass for RedundantFieldNames {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) {
 +            return;
 +        }
 +
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +        if let ExprKind::Struct(ref se) = expr.kind {
 +            for field in &se.fields {
 +                if field.is_shorthand {
 +                    continue;
 +                }
 +                if let ExprKind::Path(None, path) = &field.expr.kind {
 +                    if path.segments.len() == 1
 +                        && path.segments[0].ident == field.ident
 +                        && path.segments[0].args.is_none()
 +                    {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            REDUNDANT_FIELD_NAMES,
 +                            field.span,
 +                            "redundant field names in struct initialization",
 +                            "replace it with",
 +                            field.ident.to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    extract_msrv_attr!(EarlyContext);
 +}
index 60ba62c4a4332532f2b42d46732005c7b3a10174,0000000000000000000000000000000000000000..3aa2490bc44e018fa32371b1f94e0c69a46e4892
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,116 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::msrvs::{self, Msrv};
 +use clippy_utils::source::snippet;
- use rustc_semver::RustcVersion;
 +use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for constants and statics with an explicit `'static` lifetime.
 +    ///
 +    /// ### Why is this bad?
 +    /// Adding `'static` to every reference can create very
 +    /// complicated types.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
 +    /// &[...]
 +    /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
 +    /// &[...]
 +    /// ```
 +    /// This code can be rewritten as
 +    /// ```ignore
 +    ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
 +    ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub REDUNDANT_STATIC_LIFETIMES,
 +    style,
 +    "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
 +}
 +
 +pub struct RedundantStaticLifetimes {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl RedundantStaticLifetimes {
 +    #[must_use]
-         if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
 +
 +impl RedundantStaticLifetimes {
 +    // Recursively visit types
 +    fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
 +        match ty.kind {
 +            // Be careful of nested structures (arrays and tuples)
 +            TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
 +                Self::visit_type(ty, cx, reason);
 +            },
 +            TyKind::Tup(ref tup) => {
 +                for tup_ty in tup {
 +                    Self::visit_type(tup_ty, cx, reason);
 +                }
 +            },
 +            // This is what we are looking for !
 +            TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
 +                // Match the 'static lifetime
 +                if let Some(lifetime) = *optional_lifetime {
 +                    match borrow_type.ty.kind {
 +                        TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
 +                            if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
 +                                let snip = snippet(cx, borrow_type.ty.span, "<type>");
 +                                let sugg = format!("&{snip}");
 +                                span_lint_and_then(
 +                                    cx,
 +                                    REDUNDANT_STATIC_LIFETIMES,
 +                                    lifetime.ident.span,
 +                                    reason,
 +                                    |diag| {
 +                                        diag.span_suggestion(
 +                                            ty.span,
 +                                            "consider removing `'static`",
 +                                            sugg,
 +                                            Applicability::MachineApplicable, //snippet
 +                                        );
 +                                    },
 +                                );
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                }
 +                Self::visit_type(&borrow_type.ty, cx, reason);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl EarlyLintPass for RedundantStaticLifetimes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        if !self.msrv.meets(msrvs::STATIC_IN_CONST) {
 +            return;
 +        }
 +
 +        if !item.span.from_expansion() {
 +            if let ItemKind::Const(_, ref var_type, _) = item.kind {
 +                Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
 +                // Don't check associated consts because `'static` cannot be elided on those (issue
 +                // #2438)
 +            }
 +
 +            if let ItemKind::Static(ref var_type, _, _) = item.kind {
 +                Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
index 2b2a41d160117a5f781cf7d389df8ad550e353d4,0000000000000000000000000000000000000000..81143d7799ea8aa585f2e023b3cb9d3560dfd22f
mode 100644,000000..100644
--- /dev/null
@@@ -1,291 -1,0 +1,306 @@@
-                     emit_return_lint(
-                         cx,
-                         peeled_drop_expr.span,
-                         semi_spans,
-                         inner.as_ref().map(|i| i.span),
-                         replacement,
-                     );
 +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet_opt, snippet_with_context};
 +use clippy_utils::visitors::{for_each_expr, Descend};
 +use clippy_utils::{fn_def_id, path_to_local_id};
 +use core::ops::ControlFlow;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +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
 +    /// Checks for `let`-bindings, which are subsequently
 +    /// returned.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is just extraneous code. Remove it to make your code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo() -> String {
 +    ///     let x = String::new();
 +    ///     x
 +    /// }
 +    /// ```
 +    /// instead, use
 +    /// ```
 +    /// fn foo() -> String {
 +    ///     String::new()
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LET_AND_RETURN,
 +    style,
 +    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for return statements at the end of a block.
 +    ///
 +    /// ### Why is this bad?
 +    /// Removing the `return` and semicolon will make the code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    /// simplify to
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_RETURN,
 +    style,
 +    "using a return statement like `return expr;` where an expression would suffice"
 +}
 +
 +#[derive(PartialEq, Eq, Copy, Clone)]
 +enum RetReplacement {
 +    Empty,
 +    Block,
 +    Unit,
 +}
 +
 +impl RetReplacement {
 +    fn sugg_help(self) -> &'static str {
 +        match self {
 +            Self::Empty => "remove `return`",
 +            Self::Block => "replace `return` with an empty block",
 +            Self::Unit => "replace `return` with a unit value",
 +        }
 +    }
 +}
 +
 +impl ToString for RetReplacement {
 +    fn to_string(&self) -> String {
 +        match *self {
 +            Self::Empty => "",
 +            Self::Block => "{}",
 +            Self::Unit => "()",
 +        }
 +        .to_string()
 +    }
 +}
 +
 +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Return {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
 +        // we need both a let-binding stmt and an expr
 +        if_chain! {
 +            if let Some(retexpr) = block.expr;
 +            if let Some(stmt) = block.stmts.iter().last();
 +            if let StmtKind::Local(local) = &stmt.kind;
 +            if local.ty.is_none();
 +            if cx.tcx.hir().attrs(local.hir_id).is_empty();
 +            if let Some(initexpr) = &local.init;
 +            if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
 +            if path_to_local_id(retexpr, local_id);
 +            if !last_statement_borrows(cx, initexpr);
 +            if !in_external_macro(cx.sess(), initexpr.span);
 +            if !in_external_macro(cx.sess(), retexpr.span);
 +            if !local.span.from_expansion();
 +            then {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    LET_AND_RETURN,
 +                    retexpr.hir_id,
 +                    retexpr.span,
 +                    "returning the result of a `let` binding from a block",
 +                    |err| {
 +                        err.span_label(local.span, "unnecessary `let` binding");
 +
 +                        if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
 +                            if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
 +                                snippet.push_str(" as _");
 +                            }
 +                            err.multipart_suggestion(
 +                                "return the expression directly",
 +                                vec![
 +                                    (local.span, String::new()),
 +                                    (retexpr.span, snippet),
 +                                ],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        } else {
 +                            err.span_help(initexpr.span, "this expression can be directly returned");
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'tcx>,
 +        body: &'tcx Body<'tcx>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        match kind {
 +            FnKind::Closure => {
 +                // when returning without value in closure, replace this `return`
 +                // with an empty block to prevent invalid suggestion (see #6501)
 +                let replacement = if let ExprKind::Ret(None) = &body.value.kind {
 +                    RetReplacement::Block
 +                } else {
 +                    RetReplacement::Empty
 +                };
 +                check_final_expr(cx, body.value, vec![], replacement);
 +            },
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                check_block_return(cx, &body.value.kind, vec![]);
 +            },
 +        }
 +    }
 +}
 +
 +// if `expr` is a block, check if there are needless returns in it
 +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, semi_spans: Vec<Span>) {
 +    if let ExprKind::Block(block, _) = expr_kind {
 +        if let Some(block_expr) = block.expr {
 +            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty);
 +        } else if let Some(stmt) = block.stmts.iter().last() {
 +            match stmt.kind {
 +                StmtKind::Expr(expr) => {
 +                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty);
 +                },
 +                StmtKind::Semi(semi_expr) => {
 +                    let mut semi_spans_and_this_one = semi_spans;
 +                    // we only want the span containing the semicolon so we can remove it later. From `entry.rs:382`
 +                    if let Some(semicolon_span) = stmt.span.trim_start(semi_expr.span) {
 +                        semi_spans_and_this_one.push(semicolon_span);
 +                        check_final_expr(cx, semi_expr, semi_spans_and_this_one, RetReplacement::Empty);
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    }
 +}
 +
 +fn check_final_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
 +                            * needless return */
 +    replacement: RetReplacement,
 +) {
 +    let peeled_drop_expr = expr.peel_drop_temps();
 +    match &peeled_drop_expr.kind {
 +        // simple return is always "bad"
 +        ExprKind::Ret(ref inner) => {
 +            if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
 +                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
 +                if !borrows {
++                    // 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);
 +                }
 +            }
 +        },
 +        ExprKind::If(_, then, else_clause_opt) => {
 +            check_block_return(cx, &then.kind, semi_spans.clone());
 +            if let Some(else_clause) = else_clause_opt {
 +                check_block_return(cx, &else_clause.kind, semi_spans);
 +            }
 +        },
 +        // a match expr, check all arms
 +        // an if/if let expr, check both exprs
 +        // note, if without else is going to be a type checking error anyways
 +        // (except for unit type functions) so we don't match it
 +        ExprKind::Match(_, arms, MatchSource::Normal) => {
 +            for arm in arms.iter() {
 +                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit);
 +            }
 +        },
 +        // if it's a whole block, check it
 +        other_expr_kind => check_block_return(cx, other_expr_kind, semi_spans),
 +    }
 +}
 +
 +fn emit_return_lint(
 +    cx: &LateContext<'_>,
 +    ret_span: Span,
 +    semi_spans: Vec<Span>,
 +    inner_span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    if ret_span.from_expansion() {
 +        return;
 +    }
 +    let mut applicability = Applicability::MachineApplicable;
 +    let return_replacement = inner_span.map_or_else(
 +        || replacement.to_string(),
 +        |inner_span| {
 +            let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
 +            snippet.to_string()
 +        },
 +    );
 +    let sugg_help = if inner_span.is_some() {
 +        "remove `return`"
 +    } else {
 +        replacement.sugg_help()
 +    };
 +    span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
 +        diag.span_suggestion_hidden(ret_span, sugg_help, return_replacement, applicability);
 +        // for each parent statement, we need to remove the semicolon
 +        for semi_stmt_span in semi_spans {
 +            diag.tool_only_span_suggestion(semi_stmt_span, "remove this semicolon", "", applicability);
 +        }
 +    });
 +}
 +
 +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    for_each_expr(expr, |e| {
 +        if let Some(def_id) = fn_def_id(cx, e)
 +            && cx
 +                .tcx
 +                .fn_sig(def_id)
 +                .skip_binder()
 +                .output()
 +                .walk()
 +                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 +        {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(Descend::from(!expr.span.from_expansion()))
 +        }
 +    })
 +    .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
++}
index 424a6e9264e4b96680b6e4a09cfab81e2a36083f,0000000000000000000000000000000000000000..83e651aba8e89b15ed99db9dfcf768afece1431f
mode 100644,000000..100644
--- /dev/null
@@@ -1,485 -1,0 +1,485 @@@
- use rustc_semver::RustcVersion;
 +mod crosspointer_transmute;
 +mod transmute_float_to_int;
 +mod transmute_int_to_bool;
 +mod transmute_int_to_char;
 +mod transmute_int_to_float;
 +mod transmute_num_to_bytes;
 +mod transmute_ptr_to_ptr;
 +mod transmute_ptr_to_ref;
 +mod transmute_ref_to_ref;
 +mod transmute_undefined_repr;
 +mod transmutes_expressible_as_ptr_casts;
 +mod transmuting_null;
 +mod unsound_collection_transmute;
 +mod useless_transmute;
 +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};
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes that can't ever be correct on any
 +    /// architecture.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's basically guaranteed to be undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// When accessing C, users might want to store pointer
 +    /// sized objects in `extradata` arguments to save an allocation.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let ptr: *const T = core::intrinsics::transmute('x')
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_TRANSMUTE,
 +    correctness,
 +    "transmutes that are confusing at best, undefined behavior at worst and always useless"
 +}
 +
 +// FIXME: Move this to `complexity` again, after #5343 is fixed
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes to the original type of the object
 +    /// and transmutes that could be a cast.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. The code tricks people into thinking that
 +    /// something complex is going on.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_TRANSMUTE,
 +    complexity,
 +    "transmutes that have the same to and from types or could be a cast/coercion"
 +}
 +
 +// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///Checks for transmutes that could be a pointer cast.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. The code tricks people into thinking that
 +    /// something complex is going on.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// # let p: *const [i32] = &[];
 +    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let p: *const [i32] = &[];
 +    /// p as *const [u16];
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    complexity,
 +    "transmutes that could be a pointer cast"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between a type `T` and `*T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easy to mistakenly transmute between a type and a
 +    /// pointer to that type.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// core::intrinsics::transmute(t) // where the result type is the same as
 +    ///                                // `*t` or `&t`'s
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CROSSPOINTER_TRANSMUTE,
 +    complexity,
 +    "transmutes that have to or from types that are a pointer to the other"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a pointer to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// This can always be rewritten with `&` and `*`.
 +    ///
 +    /// ### Known problems
 +    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
 +    /// while dereferencing raw pointer is not stable yet.
 +    /// If you need to do this in those places,
 +    /// you would have to use `transmute` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// unsafe {
 +    ///     let _: &T = std::mem::transmute(p); // where p: *const T
 +    /// }
 +    ///
 +    /// // can be written:
 +    /// let _: &T = &*p;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_PTR_TO_REF,
 +    complexity,
 +    "transmutes from a pointer to a reference type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a `char`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not every integer is a Unicode scalar value.
 +    ///
 +    /// ### Known problems
 +    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
 +    /// as it needs to validate the input.
 +    /// If you are certain that the input is always a valid Unicode scalar value,
 +    /// use [`from_u32_unchecked`] which is as fast as `transmute`
 +    /// but has a semantically meaningful name.
 +    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
 +    ///
 +    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
 +    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1_u32;
 +    /// unsafe {
 +    ///     let _: char = std::mem::transmute(x); // where x: u32
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _ = std::char::from_u32(x).unwrap();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_CHAR,
 +    complexity,
 +    "transmutes from an integer to a `char`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a `&[u8]` to a `&str`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not every byte slice is a valid UTF-8 string.
 +    ///
 +    /// ### Known problems
 +    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
 +    /// as it needs to validate the input.
 +    /// If you are certain that the input is always a valid UTF-8,
 +    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
 +    /// but has a semantically meaningful name.
 +    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
 +    ///
 +    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
 +    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let b: &[u8] = &[1_u8, 2_u8];
 +    /// unsafe {
 +    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _ = std::str::from_utf8(b).unwrap();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_BYTES_TO_STR,
 +    complexity,
 +    "transmutes from a `&[u8]` to a `&str`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a `bool`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This might result in an invalid in-memory representation of a `bool`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1_u8;
 +    /// unsafe {
 +    ///     let _: bool = std::mem::transmute(x); // where x: u8
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: bool = x != 0;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_BOOL,
 +    complexity,
 +    "transmutes from an integer to a `bool`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a float.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
 +    /// and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: f32 = f32::from_bits(1_u32);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_FLOAT,
 +    complexity,
 +    "transmutes from an integer to a float"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a float to an integer.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
 +    /// and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let _: u32 = std::mem::transmute(1f32);
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: u32 = 1f32.to_bits();
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub TRANSMUTE_FLOAT_TO_INT,
 +    complexity,
 +    "transmutes from a float to an integer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a number to an array of `u8`
 +    ///
 +    /// ### Why this is bad?
 +    /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
 +    /// is intuitive and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let x: [u8; 8] = std::mem::transmute(1i64);
 +    /// }
 +    ///
 +    /// // should be
 +    /// let x: [u8; 8] = 0i64.to_ne_bytes();
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TRANSMUTE_NUM_TO_BYTES,
 +    complexity,
 +    "transmutes from a number to an array of `u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a pointer to a pointer, or
 +    /// from a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous, and these can instead be
 +    /// written as casts.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr = &1u32 as *const u32;
 +    /// unsafe {
 +    ///     // pointer-to-pointer transmute
 +    ///     let _: *const f32 = std::mem::transmute(ptr);
 +    ///     // ref-ref transmute
 +    ///     let _: &f32 = std::mem::transmute(&1u32);
 +    /// }
 +    /// // These can be respectively written:
 +    /// let _ = ptr as *const f32;
 +    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_PTR_TO_PTR,
 +    pedantic,
 +    "transmutes from a pointer to a pointer / a reference to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between collections whose
 +    /// types have different ABI, size or alignment.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// Currently, we cannot know whether a type is a
 +    /// collection, so we just lint the ones that come with `std`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // different size, therefore likely out-of-bounds memory access
 +    /// // You absolutely do not want this in your code!
 +    /// unsafe {
 +    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
 +    /// };
 +    /// ```
 +    ///
 +    /// You must always iterate, map and collect the values:
 +    ///
 +    /// ```rust
 +    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub UNSOUND_COLLECTION_TRANSMUTE,
 +    correctness,
 +    "transmute between collections of layout-incompatible types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between types which do not have a representation defined relative to
 +    /// each other.
 +    ///
 +    /// ### Why is this bad?
 +    /// The results of such a transmute are not defined.
 +    ///
 +    /// ### Known problems
 +    /// This lint has had multiple problems in the past and was moved to `nursery`. See issue
 +    /// [#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo<T>(u32, T);
 +    /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[repr(C)]
 +    /// struct Foo<T>(u32, T);
 +    /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub TRANSMUTE_UNDEFINED_REPR,
 +    nursery,
 +    "transmute to or from a type with an undefined representation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmute calls which would receive a null pointer.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmuting a null pointer is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// Not all cases can be detected at the moment of this writing.
 +    /// For example, variables which hold a null pointer and are then fed to a `transmute`
 +    /// call, aren't detectable yet.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
 +    /// ```
 +    #[clippy::version = "1.35.0"]
 +    pub TRANSMUTING_NULL,
 +    correctness,
 +    "transmutes from a null pointer to a reference, which is undefined behavior"
 +}
 +
 +pub struct Transmute {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +impl_lint_pass!(Transmute => [
 +    CROSSPOINTER_TRANSMUTE,
 +    TRANSMUTE_PTR_TO_REF,
 +    TRANSMUTE_PTR_TO_PTR,
 +    USELESS_TRANSMUTE,
 +    WRONG_TRANSMUTE,
 +    TRANSMUTE_INT_TO_CHAR,
 +    TRANSMUTE_BYTES_TO_STR,
 +    TRANSMUTE_INT_TO_BOOL,
 +    TRANSMUTE_INT_TO_FLOAT,
 +    TRANSMUTE_FLOAT_TO_INT,
 +    TRANSMUTE_NUM_TO_BYTES,
 +    UNSOUND_COLLECTION_TRANSMUTE,
 +    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    TRANSMUTE_UNDEFINED_REPR,
 +    TRANSMUTING_NULL,
 +]);
 +impl Transmute {
 +    #[must_use]
-                     | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +impl<'tcx> LateLintPass<'tcx> for Transmute {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(path_expr, [arg]) = e.kind;
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
 +            if let Some(def_id) = path.res.opt_def_id();
 +            if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
 +            then {
 +                // Avoid suggesting non-const operations in const contexts:
 +                // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
 +                // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
 +                // - char conversions (https://github.com/rust-lang/rust/issues/89259)
 +                let const_context = in_constant(cx, e.hir_id);
 +
 +                let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
 +                // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
 +                let to_ty = cx.typeck_results().expr_ty(e);
 +
 +                // If useless_transmute is triggered, the other lints can be skipped.
 +                if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
 +                    return;
 +                }
 +
 +                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_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)
 +                    | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
 +                    | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | (
 +                        unsound_collection_transmute::check(cx, e, from_ty, to_ty)
 +                        || transmute_undefined_repr::check(cx, e, from_ty, to_ty)
 +                    );
 +
 +                if !linted {
 +                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 12d0b866e1c9bbcada978b4d065557fea93080d8,0000000000000000000000000000000000000000..3dde4eee67179fd9a394b10d5799b1d93ca04748
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,80 @@@
- use clippy_utils::{meets_msrv, msrvs, sugg};
 +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 rustc_semver::RustcVersion;
++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};
-     msrv: Option<RustcVersion>,
 +
 +/// Checks for `transmute_ptr_to_ref` lint.
 +/// Returns `true` if it's triggered, otherwise returns `false`.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +    arg: &'tcx Expr<'_>,
 +    path: &'tcx Path<'_>,
-                         if meets_msrv(msrv, msrvs::POINTER_CAST) {
++    msrv: &Msrv,
 +) -> bool {
 +    match (&from_ty.kind(), &to_ty.kind()) {
 +        (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
 +            span_lint_and_then(
 +                cx,
 +                TRANSMUTE_PTR_TO_REF,
 +                e.span,
 +                &format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"),
 +                |diag| {
 +                    let arg = sugg::Sugg::hir(cx, arg, "..");
 +                    let (deref, cast) = if *mutbl == Mutability::Mut {
 +                        ("&mut *", "*mut")
 +                    } else {
 +                        ("&*", "*const")
 +                    };
 +                    let mut app = Applicability::MachineApplicable;
 +
 +                    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()
 +                        } else {
 +                            sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string()
 +                        }
 +                    } else if from_ptr_ty.ty == *to_ref_ty {
 +                        if from_ptr_ty.has_erased_regions() {
++                            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}")))
 +                                    .to_string()
 +                            }
 +                        } else {
 +                            sugg::make_unop(deref, arg).to_string()
 +                        }
 +                    } else {
 +                        sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string()
 +                    };
 +
 +                    diag.span_suggestion(e.span, "try", sugg, app);
 +                },
 +            );
 +            true
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Gets the type `Bar` in `…::transmute<Foo, &Bar>`.
 +fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> {
 +    if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)?
 +        && let TyKind::Rptr(_, ty) = &ty.kind
 +    {
 +        Some(ty.ty)
 +    } else {
 +        None
 +    }
 +}
index e8f15a4447352a9cedd66892e34831cb089a800f,0000000000000000000000000000000000000000..2e1b6d8d4ea7f743a862d8088ec8554b28ba913d
mode 100644,000000..100644
--- /dev/null
@@@ -1,392 -1,0 +1,627 @@@
- declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
++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};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `unsafe` blocks and impls without a `// SAFETY: ` comment
 +    /// explaining why the unsafe operations performed inside
 +    /// the block are safe.
 +    ///
 +    /// Note the comment must appear on the line(s) preceding the unsafe block
 +    /// with nothing appearing in between. The following is ok:
 +    /// ```ignore
 +    /// foo(
 +    ///     // SAFETY:
 +    ///     // This is a valid safety comment
 +    ///     unsafe { *x }
 +    /// )
 +    /// ```
 +    /// But neither of these are:
 +    /// ```ignore
 +    /// // SAFETY:
 +    /// // This is not a valid safety comment
 +    /// foo(
 +    ///     /* SAFETY: Neither is this */ unsafe { *x },
 +    /// );
 +    /// ```
 +    ///
 +    /// ### Why is this bad?
 +    /// Undocumented unsafe blocks and impls can make it difficult to
 +    /// read and maintain code, as well as uncover unsoundness
 +    /// and bugs.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ptr::NonNull;
 +    /// let a = &mut 42;
 +    ///
 +    /// let ptr = unsafe { NonNull::new_unchecked(a) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::ptr::NonNull;
 +    /// let a = &mut 42;
 +    ///
 +    /// // SAFETY: references are guaranteed to be non-null.
 +    /// let ptr = unsafe { NonNull::new_unchecked(a) };
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub UNDOCUMENTED_UNSAFE_BLOCKS,
 +    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"
++}
 +
- impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
-     fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
++declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_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)
++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)
 +            && !is_unsafe_from_proc_macro(cx, block.span)
 +            && !block_has_safety_comment(cx, block.span)
 +            && !block_parents_have_safety_comment(cx, block.hir_id)
 +        {
 +            let source_map = cx.tcx.sess.source_map();
 +            let span = if source_map.is_multiline(block.span) {
 +                source_map.span_until_char(block.span, '\n')
 +            } else {
 +                block.span
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                UNDOCUMENTED_UNSAFE_BLOCKS,
 +                span,
 +                "unsafe block missing a safety comment",
 +                None,
 +                "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",
++            );
++        }
 +    }
 +
-             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",
-             );
++    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_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span)
++        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());
 +    file_pos
 +        .sf
 +        .src
 +        .as_deref()
 +        .and_then(|src| src.get(file_pos.pos.to_usize()..))
 +        .map_or(true, |src| !src.starts_with("unsafe"))
 +}
 +
 +// Checks if any parent {expression, statement, block, local, const, static}
 +// has a safety comment
 +fn block_parents_have_safety_comment(cx: &LateContext<'_>, id: hir::HirId) -> bool {
 +    if let Some(node) = get_parent_node(cx.tcx, id) {
 +        return match node {
 +            Node::Expr(expr) => !is_branchy(expr) && span_in_body_has_safety_comment(cx, expr.span),
 +            Node::Stmt(hir::Stmt {
 +                kind:
 +                    hir::StmtKind::Local(hir::Local { span, .. })
 +                    | hir::StmtKind::Expr(hir::Expr { span, .. })
 +                    | hir::StmtKind::Semi(hir::Expr { span, .. }),
 +                ..
 +            })
 +            | Node::Local(hir::Local { span, .. })
 +            | Node::Item(hir::Item {
 +                kind: hir::ItemKind::Const(..) | ItemKind::Static(..),
 +                span,
 +                ..
 +            }) => span_in_body_has_safety_comment(cx, *span),
 +            _ => false,
 +        };
 +    }
 +    false
 +}
 +
 +/// Checks if an expression is "branchy", e.g. loop, match/if/etc.
 +fn is_branchy(expr: &hir::Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        hir::ExprKind::If(..) | hir::ExprKind::Loop(..) | hir::ExprKind::Match(..)
 +    )
 +}
 +
 +/// Checks if the lines immediately preceding the block contain a safety comment.
 +fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 +    // This intentionally ignores text before the start of a function so something like:
 +    // ```
 +    //     // SAFETY: reason
 +    //     fn foo() { unsafe { .. } }
 +    // ```
 +    // won't work. This is to avoid dealing with where such a comment should be place relative to
 +    // attributes and doc comments.
 +
- fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
-     if span_from_macro_expansion_has_safety_comment(cx, item.span) {
-         return true;
++    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)]
-     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;
-                     }
-                 },
-                 _ => {
++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,
 +    }
 +
-                     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.
-             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(
++                    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;
++            },
++        };
 +
-                     )
-                 })
-             } else {
-                 // Problem getting source text. Pretend a comment was found.
-                 true
-             }
-         } else {
-             // No parent node. Pretend a comment was found.
-             true
++        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 {
-         false
++                    ) {
++                        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,
++                    }
++                }
++            });
 +        }
- fn comment_start_before_impl_in_mod(
 +    }
++    HasSafetyComment::Maybe
 +}
 +
-     imple: &hir::Item<'_>,
++fn comment_start_before_item_in_mod(
 +    cx: &LateContext<'_>,
 +    parent_mod: &hir::Mod<'_>,
 +    parent_mod_span: Span,
-         if *item_id == imple.item_id() {
++    item: &hir::Item<'_>,
 +) -> Option<BytePos> {
 +    parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
- fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
++        if *item_id == item.item_id() {
 +            if idx == 0 {
 +                // mod A { /* comment */ unsafe impl T {} ... }
 +                // ^------------------------------------------^ returns the start of this span
 +                // ^---------------------^ finally checks comments in this range
 +                if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) {
 +                    return Some(sp.lo());
 +                }
 +            } else {
 +                // some_item /* comment */ unsafe impl T {}
 +                // ^-------^ returns the end of this span
 +                //         ^---------------^ finally checks comments in this range
 +                let prev_item = cx.tcx.hir().item(parent_mod.item_ids[idx - 1]);
 +                if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) {
 +                    return Some(sp.hi());
 +                }
 +            }
 +        }
 +        None
 +    })
 +}
 +
-         false
++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() {
-                 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(),
-                 )
++        HasSafetyComment::Maybe
 +    } else {
 +        // From a macro expansion. Get the text from the start of the macro declaration to start of the
 +        // unsafe block.
 +        //     macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
 +        //     ^--------------------------------------------^
 +        if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
 +            && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
 +            && Lrc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
 +            && let Some(src) = unsafe_line.sf.src.as_deref()
 +        {
 +            unsafe_line.sf.lines(|lines| {
-             true
++                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.
-                 )
++            HasSafetyComment::Maybe
 +        }
 +    }
 +}
 +
 +fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
 +    let body = cx.enclosing_body?;
 +    let map = cx.tcx.hir();
 +    let mut span = map.body(body).value.span;
 +    for (_, node) in map.parent_iter(body.hir_id) {
 +        match node {
 +            Node::Expr(e) => span = e.span,
 +            Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
 +            _ => break,
 +        }
 +    }
 +    Some(span)
 +}
 +
 +fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 +    let source_map = cx.sess().source_map();
 +    let ctxt = span.ctxt();
 +    if ctxt == SyntaxContext::root()
 +        && let Some(search_span) = get_body_search_span(cx)
 +    {
 +        if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
 +            && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
 +            && let Ok(body_line) = source_map.lookup_line(body_span.lo())
 +            && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
 +            && let Some(src) = unsafe_line.sf.src.as_deref()
 +        {
 +            // Get the text from the start of function body to the unsafe block.
 +            //     fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
 +            //              ^-------------^
 +            unsafe_line.sf.lines(|lines| {
 +                body_line.line < unsafe_line.line && text_has_safety_comment(
 +                    src,
 +                    &lines[body_line.line + 1..=unsafe_line.line],
 +                    unsafe_line.sf.start_pos.to_usize(),
- fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
++                ).is_some()
 +            })
 +        } else {
 +            // Problem getting source text. Pretend a comment was found.
 +            true
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Checks if the given text has a safety comment for the immediately proceeding line.
-             src.get(start..end).map(|text| (start, text.trim_start()))
++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;
-         return false;
++            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 {
-         let mut line = line;
++        return None;
 +    };
 +    // Check for a sequence of line comments.
 +    if line.starts_with("//") {
-                 return true;
++        let (mut line, mut line_start) = (line, line_start);
 +        loop {
 +            if line.to_ascii_uppercase().contains("SAFETY:") {
-                 Some((_, x)) if x.starts_with("//") => line = x,
-                 _ => return false,
++                return Some(BytePos(
++                    u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(),
++                ));
 +            }
 +            match lines.next() {
-             let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
++                Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s),
++                _ => return None,
 +            }
 +        }
 +    }
 +    // No line comments; look for the start of a block comment.
 +    // This will only find them if they are at the start of a line.
 +    let (mut line_start, mut line) = (line_start, line);
 +    loop {
 +        if line.starts_with("/*") {
-             return src[..tokens.next().unwrap().len as usize]
++            let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset];
 +            let mut tokens = tokenize(src);
-                 && tokens.all(|t| t.kind == TokenKind::Whitespace);
++            return (src[..tokens.next().unwrap().len as usize]
 +                .to_ascii_uppercase()
 +                .contains("SAFETY:")
-             None => return false,
++                && 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 None,
 +        }
 +    }
 +}
index bb6fb38e9690e32a3e472c49f1462bc38fa28d1b,0000000000000000000000000000000000000000..7355260ae4af37bee18b3ea5ee126c3f2538ed21
mode 100644,000000..100644
--- /dev/null
@@@ -1,427 -1,0 +1,426 @@@
- use clippy_utils::{meets_msrv, msrvs, over};
 +#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 +
 +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 rustc_semver::RustcVersion;
++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};
- #[derive(Clone, Copy)]
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::DUMMY_SP;
 +
 +use std::cell::Cell;
 +use std::mem;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnested or-patterns, e.g., `Some(0) | Some(2)` and
 +    /// suggests replacing the pattern with a nested one, `Some(0 | 2)`.
 +    ///
 +    /// Another way to think of this is that it rewrites patterns in
 +    /// *disjunctive normal form (DNF)* into *conjunctive normal form (CNF)*.
 +    ///
 +    /// ### Why is this bad?
 +    /// In the example above, `Some` is repeated, which unnecessarily complicates the pattern.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     if let Some(0) | Some(2) = Some(0) {}
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     if let Some(0 | 2) = Some(0) {}
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub UNNESTED_OR_PATTERNS,
 +    pedantic,
 +    "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`"
 +}
 +
-     msrv: Option<RustcVersion>,
 +pub struct UnnestedOrPatterns {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +}
 +
 +impl UnnestedOrPatterns {
 +    #[must_use]
-         if meets_msrv(self.msrv, msrvs::OR_PATTERNS) {
++    pub fn new(msrv: Msrv) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +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);
 +            }
 +        }
 +    }
 +
 +    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 self.msrv.meets(msrvs::OR_PATTERNS) {
 +            lint_unnested_or_patterns(cx, &l.pat);
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +fn lint_unnested_or_patterns(cx: &EarlyContext<'_>, pat: &Pat) {
 +    if let Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_) = pat.kind {
 +        // This is a leaf pattern, so cloning is unprofitable.
 +        return;
 +    }
 +
 +    let mut pat = P(pat.clone());
 +
 +    // Nix all the paren patterns everywhere so that they aren't in our way.
 +    remove_all_parens(&mut pat);
 +
 +    // Transform all unnested or-patterns into nested ones, and if there were none, quit.
 +    if !unnest_or_patterns(&mut pat) {
 +        return;
 +    }
 +
 +    span_lint_and_then(cx, UNNESTED_OR_PATTERNS, pat.span, "unnested or-patterns", |db| {
 +        insert_necessary_parens(&mut pat);
 +        db.span_suggestion_verbose(
 +            pat.span,
 +            "nest the patterns",
 +            pprust::pat_to_string(&pat),
 +            Applicability::MachineApplicable,
 +        );
 +    });
 +}
 +
 +/// Remove all `(p)` patterns in `pat`.
 +fn remove_all_parens(pat: &mut P<Pat>) {
 +    struct Visitor;
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, pat: &mut P<Pat>) {
 +            noop_visit_pat(pat, self);
 +            let inner = match &mut pat.kind {
 +                Paren(i) => mem::replace(&mut i.kind, Wild),
 +                _ => return,
 +            };
 +            pat.kind = inner;
 +        }
 +    }
 +    Visitor.visit_pat(pat);
 +}
 +
 +/// Insert parens where necessary according to Rust's precedence rules for patterns.
 +fn insert_necessary_parens(pat: &mut P<Pat>) {
 +    struct Visitor;
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, pat: &mut P<Pat>) {
 +            use ast::BindingAnnotation;
 +            noop_visit_pat(pat, self);
 +            let target = match &mut pat.kind {
 +                // `i @ a | b`, `box a | b`, and `& mut? a | b`.
 +                Ident(.., Some(p)) | Box(p) | Ref(p, _) if matches!(&p.kind, Or(ps) if ps.len() > 1) => p,
 +                Ref(p, Mutability::Not) if matches!(p.kind, Ident(BindingAnnotation::MUT, ..)) => p, // `&(mut x)`
 +                _ => return,
 +            };
 +            target.kind = Paren(P(take_pat(target)));
 +        }
 +    }
 +    Visitor.visit_pat(pat);
 +}
 +
 +/// Unnest or-patterns `p0 | ... | p1` in the pattern `pat`.
 +/// For example, this would transform `Some(0) | FOO | Some(2)` into `Some(0 | 2) | FOO`.
 +fn unnest_or_patterns(pat: &mut P<Pat>) -> bool {
 +    struct Visitor {
 +        changed: bool,
 +    }
 +    impl MutVisitor for Visitor {
 +        fn visit_pat(&mut self, p: &mut P<Pat>) {
 +            // This is a bottom up transformation, so recurse first.
 +            noop_visit_pat(p, self);
 +
 +            // Don't have an or-pattern? Just quit early on.
 +            let Or(alternatives) = &mut p.kind else {
 +                return
 +            };
 +
 +            // Collapse or-patterns directly nested in or-patterns.
 +            let mut idx = 0;
 +            let mut this_level_changed = false;
 +            while idx < alternatives.len() {
 +                let inner = if let Or(ps) = &mut alternatives[idx].kind {
 +                    mem::take(ps)
 +                } else {
 +                    idx += 1;
 +                    continue;
 +                };
 +                this_level_changed = true;
 +                alternatives.splice(idx..=idx, inner);
 +            }
 +
 +            // Focus on `p_n` and then try to transform all `p_i` where `i > n`.
 +            let mut focus_idx = 0;
 +            while focus_idx < alternatives.len() {
 +                this_level_changed |= transform_with_focus_on_idx(alternatives, focus_idx);
 +                focus_idx += 1;
 +            }
 +            self.changed |= this_level_changed;
 +
 +            // Deal with `Some(Some(0)) | Some(Some(1))`.
 +            if this_level_changed {
 +                noop_visit_pat(p, self);
 +            }
 +        }
 +    }
 +
 +    let mut visitor = Visitor { changed: false };
 +    visitor.visit_pat(pat);
 +    visitor.changed
 +}
 +
 +/// Match `$scrutinee` against `$pat` and extract `$then` from it.
 +/// Panics if there is no match.
 +macro_rules! always_pat {
 +    ($scrutinee:expr, $pat:pat => $then:expr) => {
 +        match $scrutinee {
 +            $pat => $then,
 +            _ => unreachable!(),
 +        }
 +    };
 +}
 +
 +/// Focus on `focus_idx` in `alternatives`,
 +/// attempting to extend it with elements of the same constructor `C`
 +/// in `alternatives[focus_idx + 1..]`.
 +fn transform_with_focus_on_idx(alternatives: &mut Vec<P<Pat>>, focus_idx: usize) -> bool {
 +    // Extract the kind; we'll need to make some changes in it.
 +    let mut focus_kind = mem::replace(&mut alternatives[focus_idx].kind, PatKind::Wild);
 +    // We'll focus on `alternatives[focus_idx]`,
 +    // so we're draining from `alternatives[focus_idx + 1..]`.
 +    let start = focus_idx + 1;
 +
 +    // We're trying to find whatever kind (~"constructor") we found in `alternatives[start..]`.
 +    let changed = match &mut focus_kind {
 +        // These pattern forms are "leafs" and do not have sub-patterns.
 +        // Therefore they are not some form of constructor `C`,
 +        // with which a pattern `C(p_0)` may be formed,
 +        // which we would want to join with other `C(p_j)`s.
 +        Ident(.., None) | Lit(_) | Wild | Path(..) | Range(..) | Rest | MacCall(_)
 +        // Skip immutable refs, as grouping them saves few characters,
 +        // and almost always requires adding parens (increasing noisiness).
 +        // In the case of only two patterns, replacement adds net characters.
 +        | Ref(_, Mutability::Not)
 +        // Dealt with elsewhere.
 +        | Or(_) | Paren(_) => false,
 +        // Transform `box x | ... | box y` into `box (x | y)`.
 +        //
 +        // The cases below until `Slice(...)` deal with *singleton* products.
 +        // These patterns have the shape `C(p)`, and not e.g., `C(p0, ..., pn)`.
 +        Box(target) => extend_with_matching(
 +            target, start, alternatives,
 +            |k| matches!(k, Box(_)),
 +            |k| always_pat!(k, Box(p) => p),
 +        ),
 +        // Transform `&mut x | ... | &mut y` into `&mut (x | y)`.
 +        Ref(target, Mutability::Mut) => extend_with_matching(
 +            target, start, alternatives,
 +            |k| matches!(k, Ref(_, Mutability::Mut)),
 +            |k| always_pat!(k, Ref(p, _) => p),
 +        ),
 +        // Transform `b @ p0 | ... b @ p1` into `b @ (p0 | p1)`.
 +        Ident(b1, i1, Some(target)) => extend_with_matching(
 +            target, start, alternatives,
 +            // Binding names must match.
 +            |k| matches!(k, Ident(b2, i2, Some(_)) if b1 == b2 && eq_id(*i1, *i2)),
 +            |k| always_pat!(k, Ident(_, _, Some(p)) => p),
 +        ),
 +        // Transform `[pre, x, post] | ... | [pre, y, post]` into `[pre, x | y, post]`.
 +        Slice(ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(k, Slice(ps2) if eq_pre_post(ps1, ps2, idx)),
 +            |k| always_pat!(k, Slice(ps) => ps),
 +        ),
 +        // Transform `(pre, x, post) | ... | (pre, y, post)` into `(pre, x | y, post)`.
 +        Tuple(ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(k, Tuple(ps2) if eq_pre_post(ps1, ps2, idx)),
 +            |k| always_pat!(k, Tuple(ps) => ps),
 +        ),
 +        // Transform `S(pre, x, post) | ... | S(pre, y, post)` into `S(pre, x | y, post)`.
 +        TupleStruct(qself1, path1, ps1) => extend_with_matching_product(
 +            ps1, start, alternatives,
 +            |k, ps1, idx| matches!(
 +                k,
 +                TupleStruct(qself2, path2, ps2)
 +                    if eq_maybe_qself(qself1, qself2) && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
 +            ),
 +            |k| always_pat!(k, TupleStruct(_, _, ps) => ps),
 +        ),
 +        // Transform a record pattern `S { fp_0, ..., fp_n }`.
 +        Struct(qself1, path1, fps1, rest1) => extend_with_struct_pat(qself1, path1, fps1, *rest1, start, alternatives),
 +    };
 +
 +    alternatives[focus_idx].kind = focus_kind;
 +    changed
 +}
 +
 +/// Here we focusing on a record pattern `S { fp_0, ..., fp_n }`.
 +/// In particular, for a record pattern, the order in which the field patterns is irrelevant.
 +/// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
 +/// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
 +fn extend_with_struct_pat(
 +    qself1: &Option<P<ast::QSelf>>,
 +    path1: &ast::Path,
 +    fps1: &mut [ast::PatField],
 +    rest1: bool,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +) -> bool {
 +    (0..fps1.len()).any(|idx| {
 +        let pos_in_2 = Cell::new(None); // The element `k`.
 +        let tail_or = drain_matching(
 +            start,
 +            alternatives,
 +            |k| {
 +                matches!(k, Struct(qself2, path2, fps2, rest2)
 +                if rest1 == *rest2 // If one struct pattern has `..` so must the other.
 +                && eq_maybe_qself(qself1, qself2)
 +                && eq_path(path1, path2)
 +                && fps1.len() == fps2.len()
 +                && fps1.iter().enumerate().all(|(idx_1, fp1)| {
 +                    if idx_1 == idx {
 +                        // In the case of `k`, we merely require identical field names
 +                        // so that we will transform into `ident_k: p1_k | p2_k`.
 +                        let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
 +                        pos_in_2.set(pos);
 +                        pos.is_some()
 +                    } else {
 +                        fps2.iter().any(|fp2| eq_field_pat(fp1, fp2))
 +                    }
 +                }))
 +            },
 +            // Extract `p2_k`.
 +            |k| always_pat!(k, Struct(_, _, mut fps, _) => fps.swap_remove(pos_in_2.take().unwrap()).pat),
 +        );
 +        extend_with_tail_or(&mut fps1[idx].pat, tail_or)
 +    })
 +}
 +
 +/// Like `extend_with_matching` but for products with > 1 factor, e.g., `C(p_0, ..., p_n)`.
 +/// Here, the idea is that we fixate on some `p_k` in `C`,
 +/// allowing it to vary between two `targets` and `ps2` (returned by `extract`),
 +/// while also requiring `ps1[..n] ~ ps2[..n]` (pre) and `ps1[n + 1..] ~ ps2[n + 1..]` (post),
 +/// where `~` denotes semantic equality.
 +fn extend_with_matching_product(
 +    targets: &mut [P<Pat>],
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind, &[P<Pat>], usize) -> bool,
 +    extract: impl Fn(PatKind) -> Vec<P<Pat>>,
 +) -> bool {
 +    (0..targets.len()).any(|idx| {
 +        let tail_or = drain_matching(
 +            start,
 +            alternatives,
 +            |k| predicate(k, targets, idx),
 +            |k| extract(k).swap_remove(idx),
 +        );
 +        extend_with_tail_or(&mut targets[idx], tail_or)
 +    })
 +}
 +
 +/// Extract the pattern from the given one and replace it with `Wild`.
 +/// This is meant for temporarily swapping out the pattern for manipulation.
 +fn take_pat(from: &mut Pat) -> Pat {
 +    let dummy = Pat {
 +        id: DUMMY_NODE_ID,
 +        kind: Wild,
 +        span: DUMMY_SP,
 +        tokens: None,
 +    };
 +    mem::replace(from, dummy)
 +}
 +
 +/// Extend `target` as an or-pattern with the alternatives
 +/// in `tail_or` if there are any and return if there were.
 +fn extend_with_tail_or(target: &mut Pat, tail_or: Vec<P<Pat>>) -> bool {
 +    fn extend(target: &mut Pat, mut tail_or: Vec<P<Pat>>) {
 +        match target {
 +            // On an existing or-pattern in the target, append to it.
 +            Pat { kind: Or(ps), .. } => ps.append(&mut tail_or),
 +            // Otherwise convert the target to an or-pattern.
 +            target => {
 +                let mut init_or = vec![P(take_pat(target))];
 +                init_or.append(&mut tail_or);
 +                target.kind = Or(init_or);
 +            },
 +        }
 +    }
 +
 +    let changed = !tail_or.is_empty();
 +    if changed {
 +        // Extend the target.
 +        extend(target, tail_or);
 +    }
 +    changed
 +}
 +
 +// Extract all inner patterns in `alternatives` matching our `predicate`.
 +// Only elements beginning with `start` are considered for extraction.
 +fn drain_matching(
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind) -> bool,
 +    extract: impl Fn(PatKind) -> P<Pat>,
 +) -> Vec<P<Pat>> {
 +    let mut tail_or = vec![];
 +    let mut idx = 0;
 +    for pat in alternatives.drain_filter(|p| {
 +        // Check if we should extract, but only if `idx >= start`.
 +        idx += 1;
 +        idx > start && predicate(&p.kind)
 +    }) {
 +        tail_or.push(extract(pat.into_inner().kind));
 +    }
 +    tail_or
 +}
 +
 +fn extend_with_matching(
 +    target: &mut Pat,
 +    start: usize,
 +    alternatives: &mut Vec<P<Pat>>,
 +    predicate: impl Fn(&PatKind) -> bool,
 +    extract: impl Fn(PatKind) -> P<Pat>,
 +) -> bool {
 +    extend_with_tail_or(target, drain_matching(start, alternatives, predicate, extract))
 +}
 +
 +/// Are the patterns in `ps1` and `ps2` equal save for `ps1[idx]` compared to `ps2[idx]`?
 +fn eq_pre_post(ps1: &[P<Pat>], ps2: &[P<Pat>], idx: usize) -> bool {
 +    ps1.len() == ps2.len()
 +        && ps1[idx].is_rest() == ps2[idx].is_rest() // Avoid `[x, ..] | [x, 0]` => `[x, .. | 0]`.
 +        && over(&ps1[..idx], &ps2[..idx], |l, r| eq_pat(l, r))
 +        && over(&ps1[idx + 1..], &ps2[idx + 1..], |l, r| eq_pat(l, r))
 +}
index aac6719a8dc0feef10b0ea7c4f1578f8732195ca,0000000000000000000000000000000000000000..097568cd1f70037921ee4f5c6b2bc601a8aacee5
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,62 @@@
- fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
 +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};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Detects cases where a whole-number literal float is being rounded, using
 +    /// the `floor`, `ceil`, or `round` methods.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// This is unnecessary and confusing to the reader. Doing this is probably a mistake.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1f32.ceil();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = 1f32;
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub UNUSED_ROUNDING,
 +    nursery,
 +    "Uselessly rounding a whole number floating-point literal"
 +}
 +declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
 +
-         && 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
-             }
++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
-         if let Some((method_name, float)) = is_useless_rounding(expr) {
++        && 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
 +        }
 +}
 +
 +impl EarlyLintPass for UnusedRounding {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if let Some((method_name, float)) = is_useless_rounding(cx, expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                UNUSED_ROUNDING,
 +                expr.span,
 +                &format!("used the `{method_name}` method with a whole number float"),
 +                &format!("remove the `{method_name}` method call"),
 +                float,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
index e2860db71a5a772a945f11bced95c435051bf8d7,0000000000000000000000000000000000000000..4c755d812a0e0fe93c319fc6fa534be220d71022
mode 100644,000000..100644
--- /dev/null
@@@ -1,314 -1,0 +1,314 @@@
- use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
 +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 rustc_semver::RustcVersion;
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    self as hir,
 +    def::{CtorOf, DefKind, Res},
 +    def_id::LocalDefId,
 +    intravisit::{walk_inf, walk_ty, Visitor},
 +    Expr, ExprKind, FnRetTy, FnSig, GenericArg, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath,
 +    TyKind,
 +};
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::{LateContext, LateLintPass};
-     msrv: Option<RustcVersion>,
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary repetition of structure name when a
 +    /// replacement with `Self` is applicable.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unnecessary repetition. Mixed use of `Self` and struct
 +    /// name
 +    /// feels inconsistent.
 +    ///
 +    /// ### Known problems
 +    /// - Unaddressed false negative in fn bodies of trait implementations
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo;
 +    /// impl Foo {
 +    ///     fn new() -> Foo {
 +    ///         Foo {}
 +    ///     }
 +    /// }
 +    /// ```
 +    /// could be
 +    /// ```rust
 +    /// struct Foo;
 +    /// impl Foo {
 +    ///     fn new() -> Self {
 +    ///         Self {}
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USE_SELF,
 +    nursery,
 +    "unnecessary structure name repetition whereas `Self` is applicable"
 +}
 +
 +#[derive(Default)]
 +pub struct UseSelf {
-     pub fn new(msrv: Option<RustcVersion>) -> Self {
++    msrv: Msrv,
 +    stack: Vec<StackItem>,
 +}
 +
 +impl UseSelf {
 +    #[must_use]
-             if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS);
++    pub fn new(msrv: Msrv) -> Self {
 +        Self {
 +            msrv,
 +            ..Self::default()
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +enum StackItem {
 +    Check {
 +        impl_id: LocalDefId,
 +        in_body: u32,
 +        types_to_skip: FxHashSet<HirId>,
 +    },
 +    NoCheck,
 +}
 +
 +impl_lint_pass!(UseSelf => [USE_SELF]);
 +
 +const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
 +
 +impl<'tcx> LateLintPass<'tcx> for UseSelf {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
 +        if matches!(item.kind, ItemKind::OpaqueTy(_)) {
 +            // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
 +            return;
 +        }
 +        // We push the self types of `impl`s on a stack here. Only the top type on the stack is
 +        // relevant for linting, since this is the self type of the `impl` we're currently in. To
 +        // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
 +        // we're in an `impl` or nested item, that we don't want to lint
 +        let stack_item = if_chain! {
 +            if let ItemKind::Impl(Impl { self_ty, .. }) = item.kind;
 +            if let TyKind::Path(QPath::Resolved(_, item_path)) = self_ty.kind;
 +            let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args;
 +            if parameters.as_ref().map_or(true, |params| {
 +                !params.parenthesized && !params.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)))
 +            });
 +            if !item.span.from_expansion();
 +            if !is_from_proc_macro(cx, item); // expensive, should be last check
 +            then {
 +                StackItem::Check {
 +                    impl_id: item.owner_id.def_id,
 +                    in_body: 0,
 +                    types_to_skip: std::iter::once(self_ty.hir_id).collect(),
 +                }
 +            } else {
 +                StackItem::NoCheck
 +            }
 +        };
 +        self.stack.push(stack_item);
 +    }
 +
 +    fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
 +        if !matches!(item.kind, ItemKind::OpaqueTy(_)) {
 +            self.stack.pop();
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
 +        // We want to skip types in trait `impl`s that aren't declared as `Self` in the trait
 +        // declaration. The collection of those types is all this method implementation does.
 +        if_chain! {
 +            if let ImplItemKind::Fn(FnSig { decl, .. }, ..) = impl_item.kind;
 +            if let Some(&mut StackItem::Check {
 +                impl_id,
 +                ref mut types_to_skip,
 +                ..
 +            }) = self.stack.last_mut();
 +            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(impl_id);
 +            then {
 +                // `self_ty` is the semantic self type of `impl <trait> for <type>`. This cannot be
 +                // `Self`.
 +                let self_ty = impl_trait_ref.self_ty();
 +
 +                // `trait_method_sig` is the signature of the function, how it is declared in the
 +                // trait, not in the impl of the trait.
 +                let trait_method = cx
 +                    .tcx
 +                    .associated_item(impl_item.owner_id)
 +                    .trait_item_def_id
 +                    .expect("impl method matches a trait method");
 +                let trait_method_sig = cx.tcx.fn_sig(trait_method);
 +                let trait_method_sig = cx.tcx.erase_late_bound_regions(trait_method_sig);
 +
 +                // `impl_inputs_outputs` is an iterator over the types (`hir::Ty`) declared in the
 +                // implementation of the trait.
 +                let output_hir_ty = if let FnRetTy::Return(ty) = &decl.output {
 +                    Some(&**ty)
 +                } else {
 +                    None
 +                };
 +                let impl_inputs_outputs = decl.inputs.iter().chain(output_hir_ty);
 +
 +                // `impl_hir_ty` (of type `hir::Ty`) represents the type written in the signature.
 +                //
 +                // `trait_sem_ty` (of type `ty::Ty`) is the semantic type for the signature in the
 +                // trait declaration. This is used to check if `Self` was used in the trait
 +                // declaration.
 +                //
 +                // If `any`where in the `trait_sem_ty` the `self_ty` was used verbatim (as opposed
 +                // to `Self`), we want to skip linting that type and all subtypes of it. This
 +                // avoids suggestions to e.g. replace `Vec<u8>` with `Vec<Self>`, in an `impl Trait
 +                // for u8`, when the trait always uses `Vec<u8>`.
 +                //
 +                // See also https://github.com/rust-lang/rust-clippy/issues/2894.
 +                for (impl_hir_ty, trait_sem_ty) in impl_inputs_outputs.zip(trait_method_sig.inputs_and_output) {
 +                    if trait_sem_ty.walk().any(|inner| inner == self_ty.into()) {
 +                        let mut visitor = SkipTyCollector::default();
 +                        visitor.visit_ty(impl_hir_ty);
 +                        types_to_skip.extend(visitor.types_to_skip);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
 +        // `hir_ty_to_ty` cannot be called in `Body`s or it will panic (sometimes). But in bodies
 +        // we can use `cx.typeck_results.node_type(..)` to get the `ty::Ty` from a `hir::Ty`.
 +        // However the `node_type()` method can *only* be called in bodies.
 +        if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
 +            *in_body = in_body.saturating_add(1);
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, _: &LateContext<'_>, _: &hir::Body<'_>) {
 +        if let Some(&mut StackItem::Check { ref mut in_body, .. }) = self.stack.last_mut() {
 +            *in_body = in_body.saturating_sub(1);
 +        }
 +    }
 +
 +    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,
 +                ref types_to_skip,
 +            }) = self.stack.last();
 +            if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
 +            if !matches!(
 +                path.res,
 +                Res::SelfTyParam { .. }
 +                | Res::SelfTyAlias { .. }
 +                | Res::Def(DefKind::TyParam, _)
 +            );
 +            if !types_to_skip.contains(&hir_ty.hir_id);
 +            let ty = if in_body > 0 {
 +                cx.typeck_results().node_type(hir_ty.hir_id)
 +            } else {
 +                hir_ty_to_ty(cx.tcx, hir_ty)
 +            };
 +            if same_type_and_consts(ty, cx.tcx.type_of(impl_id));
 +            then {
 +                span_lint(cx, hir_ty.span);
 +            }
 +        }
 +    }
 +
 +    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; }
 +        }
 +        match expr.kind {
 +            ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path),
 +            ExprKind::Call(fun, _) => {
 +                if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
 +                    check_path(cx, path);
 +                }
 +            },
 +            ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path),
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
 +        if_chain! {
 +            if !pat.span.from_expansion();
++            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))
 +                 | PatKind::TupleStruct(QPath::Resolved(_, path), _, _)
 +                 | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind;
 +            if cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id);
 +            then {
 +                check_path(cx, path);
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[derive(Default)]
 +struct SkipTyCollector {
 +    types_to_skip: Vec<HirId>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for SkipTyCollector {
 +    fn visit_infer(&mut self, inf: &hir::InferArg) {
 +        self.types_to_skip.push(inf.hir_id);
 +
 +        walk_inf(self, inf);
 +    }
 +    fn visit_ty(&mut self, hir_ty: &hir::Ty<'_>) {
 +        self.types_to_skip.push(hir_ty.hir_id);
 +
 +        walk_ty(self, hir_ty);
 +    }
 +}
 +
 +fn span_lint(cx: &LateContext<'_>, span: Span) {
 +    span_lint_and_sugg(
 +        cx,
 +        USE_SELF,
 +        span,
 +        "unnecessary structure name repetition",
 +        "use the applicable keyword",
 +        "Self".to_owned(),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn check_path(cx: &LateContext<'_>, path: &Path<'_>) {
 +    match path.res {
 +        Res::Def(DefKind::Ctor(CtorOf::Variant, _) | DefKind::Variant, ..) => {
 +            lint_path_to_variant(cx, path);
 +        },
 +        Res::Def(DefKind::Ctor(CtorOf::Struct, _) | DefKind::Struct, ..) => span_lint(cx, path.span),
 +        _ => (),
 +    }
 +}
 +
 +fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
 +    if let [.., self_seg, _variant] = path.segments {
 +        let span = path
 +            .span
 +            .with_hi(self_seg.args().span_ext().unwrap_or(self_seg.ident.span).hi());
 +        span_lint(cx, span);
 +    }
 +}
index b37d4239477eaa1c2ed866cd3112173f71f6f2b7,0000000000000000000000000000000000000000..b6dc8cd7ab1197e338e653a2fa3e1d5f20442877
mode 100644,000000..100644
--- /dev/null
@@@ -1,577 -1,0 +1,581 @@@
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::str::FromStr;
 +use std::{cmp, env, fmt, fs, io, iter};
 +
 +#[rustfmt::skip]
 +const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
 +    "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +    "DirectX",
 +    "ECMAScript",
 +    "GPLv2", "GPLv3",
 +    "GitHub", "GitLab",
 +    "IPv4", "IPv6",
 +    "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +    "NaN", "NaNs",
 +    "OAuth", "GraphQL",
 +    "OCaml",
 +    "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +    "WebGL",
 +    "TensorFlow",
 +    "TrueType",
 +    "iOS", "macOS", "FreeBSD",
 +    "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +    "MinGW",
 +    "CamelCase",
 +];
 +const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedPath {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +impl DisallowedPath {
 +    pub fn path(&self) -> &str {
 +        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
 +
 +        path
 +    }
 +
 +    pub fn reason(&self) -> Option<String> {
 +        match self {
 +            Self::WithReason {
 +                reason: Some(reason), ..
 +            } => Some(format!("{reason} (from clippy.toml)")),
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<Box<dyn Error>>,
 +    pub warnings: Vec<Box<dyn Error>>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error + 'static) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![Box::new(error)],
 +            warnings: vec![],
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct ConfError(String);
 +
 +impl fmt::Display for ConfError {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        <String as fmt::Display>::fmt(&self.0, f)
 +    }
 +}
 +
 +impl Error for ConfError {}
 +
 +fn conf_error(s: impl Into<String>) -> Box<dyn Error> {
 +    Box::new(ConfError(s.into()))
 +}
 +
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal, $new_conf:ident)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $($(#[doc = $doc])+ pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                let mut warnings = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(conf_error(e.to_string())),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))),
 +                                    None => {
 +                                        $name = Some(value);
 +                                        // $new_conf is the same as one of the defined `$name`s, so
 +                                        // this variable is defined in line 2 of this function.
 +                                        $(match $new_conf {
 +                                            Some(_) => errors.push(conf_error(concat!(
 +                                                "duplicate field `", stringify!($new_conf),
 +                                                "` (provided as `", stringify!($name), "`)"
 +                                            ))),
 +                                            None => $new_conf = $name.clone(),
 +                                        })?
 +                                    },
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors, warnings })
 +            }
 +        }
 +
 +        #[cfg(feature = "internal")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                concat!($($doc, '\n',)*),
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
 +define_Conf! {
 +    /// Lint: Arithmetic.
 +    ///
 +    /// Suppress checking of the passed type names.
 +    (arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
 +    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// DEPRECATED LINT: BLACKLISTED_NAME.
 +    ///
 +    /// Use the Disallowed Names lint instead
 +    #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
 +    (blacklisted_names: Vec<String> = Vec::new()),
 +    /// Lint: COGNITIVE_COMPLEXITY.
 +    ///
 +    /// The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
 +    ///
 +    /// Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
 +    (cyclomatic_complexity_threshold: u64 = 25),
 +    /// Lint: DISALLOWED_NAMES.
 +    ///
 +    /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
 +    /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
 +    /// default configuration of Clippy. By default any configuration will replace the default value.
 +    (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks. The value
 +    /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
 +    /// default configuration of Clippy. By default any configuraction will replace the default value. For example:
 +    /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 +    /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 +    ///
 +    /// Default list:
 +    (doc_valid_idents: Vec<String> = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS.
 +    ///
 +    /// The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY.
 +    ///
 +    /// The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES.
 +    ///
 +    /// The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC.
 +    ///
 +    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES.
 +    ///
 +    /// The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT.
 +    ///
 +    /// The maximum size of an enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK.
 +    ///
 +    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
 +    ///
 +    /// The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
 +    ///
 +    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE.
 +    ///
 +    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES.
 +    ///
 +    /// The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
 +    ///
 +    /// The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX.
 +    ///
 +    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS.
 +    ///
 +    /// The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS.
 +    ///
 +    /// Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_MACROS.
 +    ///
 +    /// The list of disallowed macros, written as fully qualified paths.
 +    (disallowed_macros: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
 +    /// Lint: DISALLOWED_METHODS.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPES.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
 +    (disallowed_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL.
 +    ///
 +    /// Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS.
 +    ///
 +    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: MANUAL_LET_ELSE.
 +    ///
 +    /// Whether the matches should be considered by the lint, and whether there should
 +    /// be filtering for common types.
 +    (matches_for_let_else: crate::manual_let_else::MatchLintBehaviour =
 +        crate::manual_let_else::MatchLintBehaviour::WellKnownTypes),
 +    /// Lint: _CARGO_COMMON_METADATA.
 +    ///
 +    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
 +    /// Lint: NONSTANDARD_MACRO_BRACES.
 +    ///
 +    /// Enforce the named macros always use the braces specified.
 +    ///
 +    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
 +    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
 +    /// `crate_name::macro_name` and one with just the macro name.
 +    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
 +    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
 +    ///
 +    /// The list of imports to always rename, a fully qualified path followed by the rename.
 +    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
 +    /// Lint: DISALLOWED_SCRIPT_IDENTS.
 +    ///
 +    /// The list of unicode scripts allowed to be used in the scope.
 +    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
 +    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
 +    ///
 +    /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
 +    (enable_raw_pointer_heuristic_for_send: bool = true),
 +    /// Lint: INDEX_REFUTABLE_SLICE.
 +    ///
 +    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
 +    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
 +    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
 +    (max_suggested_slice_pattern_length: u64 = 3),
 +    /// Lint: AWAIT_HOLDING_INVALID_TYPE
 +    (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedPath> = Vec::new()),
 +    /// Lint: LARGE_INCLUDE_FILE.
 +    ///
 +    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
 +    (max_include_file_size: u64 = 1_000_000),
 +    /// Lint: EXPECT_USED.
 +    ///
 +    /// Whether `expect` should be allowed within `#[cfg(test)]`
 +    (allow_expect_in_tests: bool = false),
 +    /// Lint: UNWRAP_USED.
 +    ///
 +    /// Whether `unwrap` should be allowed in test cfg
 +    (allow_unwrap_in_tests: bool = false),
 +    /// Lint: DBG_MACRO.
 +    ///
 +    /// Whether `dbg!` should be allowed in test functions
 +    (allow_dbg_in_tests: bool = false),
 +    /// Lint: PRINT_STDOUT, PRINT_STDERR.
 +    ///
 +    /// Whether print macros (ex. `println!`) should be allowed in test functions
 +    (allow_print_in_tests: bool = false),
 +    /// Lint: RESULT_LARGE_ERR.
 +    ///
 +    /// The maximum size of the `Err`-variant in a `Result` returned from a function
 +    (large_error_threshold: u64 = 128),
 +    /// Lint: MUTABLE_KEY.
 +    ///
 +    /// 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.
 +///
 +/// # Errors
 +///
 +/// Returns any unexpected filesystem error encountered when searching for the config file
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +
 +    let mut found_config: Option<PathBuf> = None;
 +
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => {
 +                        // warn if we happen to find two config files #8323
 +                        if let Some(ref found_config_) = found_config {
 +                            eprintln!(
 +                                "Using config file `{}`\nWarning: `{}` will be ignored.",
 +                                found_config_.display(),
 +                                config_file.display(),
 +                            );
 +                        } else {
 +                            found_config = Some(config_file);
 +                        }
 +                    },
 +                }
 +            }
 +        }
 +
 +        if found_config.is_some() {
 +            return Ok(found_config);
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    match toml::from_str::<TryConf>(&content) {
 +        Ok(mut conf) => {
 +            extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
 +            extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
 +
 +            conf
 +        },
 +        Err(e) => TryConf::from_error(e),
 +    }
 +}
 +
 +fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
 +    if vec.contains(&"..".to_string()) {
 +        vec.extend(default.iter().map(ToString::to_string));
 +    }
 +}
 +
 +const SEPARATOR_WIDTH: usize = 4;
 +
 +// Check whether the error is "unknown field" and, if so, list the available fields sorted and at
 +// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it.
 +pub fn format_error(error: Box<dyn Error>) -> String {
 +    let s = error.to_string();
 +
 +    if_chain! {
 +        if error.downcast::<toml::de::Error>().is_ok();
 +        if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s);
 +        then {
 +            use fmt::Write;
 +
 +            fields.sort_unstable();
 +
 +            let (rows, column_widths) = calculate_dimensions(&fields);
 +
 +            let mut msg = String::from(prefix);
 +            for row in 0..rows {
 +                writeln!(msg).unwrap();
 +                for (column, column_width) in column_widths.iter().copied().enumerate() {
 +                    let index = column * rows + row;
 +                    let field = fields.get(index).copied().unwrap_or_default();
 +                    write!(
 +                        msg,
 +                        "{:SEPARATOR_WIDTH$}{field:column_width$}",
 +                        " "
 +                    )
 +                    .unwrap();
 +                }
 +            }
 +            write!(msg, "\n{suffix}").unwrap();
 +            msg
 +        } else {
 +            s
 +        }
 +    }
 +}
 +
 +// `parse_unknown_field_message` will become unnecessary if
 +// https://github.com/alexcrichton/toml-rs/pull/364 is merged.
 +fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> {
 +    // An "unknown field" message has the following form:
 +    //   unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
 +    //                                           ^^      ^^^^                     ^^
 +    if_chain! {
 +        if s.starts_with("unknown field");
 +        let slices = s.split("`, `").collect::<Vec<_>>();
 +        let n = slices.len();
 +        if n >= 2;
 +        if let Some((prefix, first_field)) = slices[0].rsplit_once(" `");
 +        if let Some((last_field, suffix)) = slices[n - 1].split_once("` ");
 +        then {
 +            let fields = iter::once(first_field)
 +                .chain(slices[1..n - 1].iter().copied())
 +                .chain(iter::once(last_field))
 +                .collect::<Vec<_>>();
 +            Some((prefix, fields, suffix))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
 +    let columns = env::var("CLIPPY_TERMINAL_WIDTH")
 +        .ok()
 +        .and_then(|s| <usize as FromStr>::from_str(&s).ok())
 +        .map_or(1, |terminal_width| {
 +            let max_field_width = fields.iter().map(|field| field.len()).max().unwrap();
 +            cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width))
 +        });
 +
 +    let rows = (fields.len() + (columns - 1)) / columns;
 +
 +    let column_widths = (0..columns)
 +        .map(|column| {
 +            if column < columns - 1 {
 +                (0..rows)
 +                    .map(|row| {
 +                        let index = column * rows + row;
 +                        let field = fields.get(index).copied().unwrap_or_default();
 +                        field.len()
 +                    })
 +                    .max()
 +                    .unwrap()
 +            } else {
 +                // Avoid adding extra space to the last column.
 +                0
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    (rows, column_widths)
 +}
index 1e994e3f2b1713b1c2cef1502479177d8f7566e0,0000000000000000000000000000000000000000..9876a8a765ccbca52ed97abc0aa2d89769ad2bab
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,63 @@@
-                     .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::ty::{self, subst::GenericArgKind};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
 +    ///
 +    pub MISSING_MSRV_ATTR_IMPL,
 +    internal,
 +    "checking if all necessary steps were taken when adding a MSRV to a lint"
 +}
 +
 +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
 +
 +impl LateLintPass<'_> for MsrvAttrImpl {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if_chain! {
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: Some(lint_pass_trait_ref),
 +                self_ty,
 +                items,
 +                ..
 +            }) = &item.kind;
 +            if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
 +            let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
 +            if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
 +            let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
 +            if let ty::Adt(self_ty_def, _) = self_ty.kind();
 +            if self_ty_def.is_struct();
 +            if self_ty_def.all_fields().any(|f| {
 +                cx.tcx
 +                    .type_of(f.did)
 +                    .walk()
 +                    .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
++                    .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
 +            });
 +            if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
 +            then {
 +                let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
 +                let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
 +                let span = cx.sess().source_map().span_through_char(item.span, '{');
 +                span_lint_and_sugg(
 +                    cx,
 +                    MISSING_MSRV_ATTR_IMPL,
 +                    span,
 +                    &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
 +                    &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
 +                    format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index cd8575c90e86caaad333ab8f76ecce98d82b3ae8,0000000000000000000000000000000000000000..7987a233bdc184b960f9c53fbd4b9d2c0d0c7619
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,159 @@@
- pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
-     let mut unique_attr = None;
 +use rustc_ast::ast;
 +use rustc_ast::attr;
 +use rustc_errors::Applicability;
 +use rustc_session::Session;
 +use rustc_span::sym;
 +use std::str::FromStr;
 +
 +/// Deprecation status of attributes known by Clippy.
 +pub enum DeprecationStatus {
 +    /// Attribute is deprecated
 +    Deprecated,
 +    /// Attribute is deprecated and was replaced by the named attribute
 +    Replaced(&'static str),
 +    None,
 +}
 +
 +#[rustfmt::skip]
 +pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
 +    ("author",                DeprecationStatus::None),
 +    ("version",               DeprecationStatus::None),
 +    ("cognitive_complexity",  DeprecationStatus::None),
 +    ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
 +    ("dump",                  DeprecationStatus::None),
 +    ("msrv",                  DeprecationStatus::None),
 +    ("has_significant_drop",  DeprecationStatus::None),
 +];
 +
 +pub struct LimitStack {
 +    stack: Vec<u64>,
 +}
 +
 +impl Drop for LimitStack {
 +    fn drop(&mut self) {
 +        assert_eq!(self.stack.len(), 1);
 +    }
 +}
 +
 +impl LimitStack {
 +    #[must_use]
 +    pub fn new(limit: u64) -> Self {
 +        Self { stack: vec![limit] }
 +    }
 +    pub fn limit(&self) -> u64 {
 +        *self.stack.last().expect("there should always be a value in the stack")
 +    }
 +    pub fn push_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
 +        let stack = &mut self.stack;
 +        parse_attrs(sess, attrs, name, |val| stack.push(val));
 +    }
 +    pub fn pop_attrs(&mut self, sess: &Session, attrs: &[ast::Attribute], name: &'static str) {
 +        let stack = &mut self.stack;
 +        parse_attrs(sess, attrs, name, |val| assert_eq!(stack.pop(), Some(val)));
 +    }
 +}
 +
 +pub fn get_attr<'a>(
 +    sess: &'a Session,
 +    attrs: &'a [ast::Attribute],
 +    name: &'static str,
 +) -> impl Iterator<Item = &'a ast::Attribute> {
 +    attrs.iter().filter(move |attr| {
 +        let attr = if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            &normal.item
 +        } else {
 +            return false;
 +        };
 +        let attr_segments = &attr.path.segments;
 +        if attr_segments.len() == 2 && attr_segments[0].ident.name == sym::clippy {
 +            BUILTIN_ATTRIBUTES
 +                .iter()
 +                .find_map(|&(builtin_name, ref deprecation_status)| {
 +                    if attr_segments[1].ident.name.as_str() == builtin_name {
 +                        Some(deprecation_status)
 +                    } else {
 +                        None
 +                    }
 +                })
 +                .map_or_else(
 +                    || {
 +                        sess.span_err(attr_segments[1].ident.span, "usage of unknown attribute");
 +                        false
 +                    },
 +                    |deprecation_status| {
 +                        let mut diag =
 +                            sess.struct_span_err(attr_segments[1].ident.span, "usage of deprecated attribute");
 +                        match *deprecation_status {
 +                            DeprecationStatus::Deprecated => {
 +                                diag.emit();
 +                                false
 +                            },
 +                            DeprecationStatus::Replaced(new_name) => {
 +                                diag.span_suggestion(
 +                                    attr_segments[1].ident.span,
 +                                    "consider using",
 +                                    new_name,
 +                                    Applicability::MachineApplicable,
 +                                );
 +                                diag.emit();
 +                                false
 +                            },
 +                            DeprecationStatus::None => {
 +                                diag.cancel();
 +                                attr_segments[1].ident.name.as_str() == name
 +                            },
 +                        }
 +                    },
 +                )
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'static str, mut f: F) {
 +    for attr in get_attr(sess, attrs, name) {
 +        if let Some(ref value) = attr.value_str() {
 +            if let Ok(value) = FromStr::from_str(value.as_str()) {
 +                f(value);
 +            } else {
 +                sess.span_err(attr.span, "not a number");
 +            }
 +        } else {
 +            sess.span_err(attr.span, "bad clippy attribute");
 +        }
 +    }
 +}
 +
-         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"));
-             },
++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) {
++        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
 +}
 +
 +/// Return true if the attributes contain any of `proc_macro`,
 +/// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
 +pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
 +    attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
 +}
 +
 +/// Return true if the attributes contain `#[doc(hidden)]`
 +pub fn is_doc_hidden(attrs: &[ast::Attribute]) -> bool {
 +    attrs
 +        .iter()
 +        .filter(|attr| attr.has_name(sym::doc))
 +        .filter_map(ast::Attribute::meta_item_list)
 +        .any(|l| attr::list_contains_name(&l, sym::hidden))
 +}
index f74f7dadfa90809aedb880e349a98515a98748c9,0000000000000000000000000000000000000000..96711936968b5d4ddd1f3f7fb0f3c52d33a69e9f
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,250 @@@
-                     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)
-                         {
 +//! Utilities for evaluating whether eagerly evaluated expressions can be made lazy and vice versa.
 +//!
 +//! Things to consider:
 +//!  - has the expression side-effects?
 +//!  - is the expression computationally expensive?
 +//!
 +//! See lints:
 +//!  - unnecessary-lazy-evaluations
 +//!  - or-fun-call
 +//!  - option-if-let-else
 +
 +use crate::ty::{all_predicates_of, is_copy};
 +use crate::visitors::is_const_evaluatable;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, PredicateKind};
 +use rustc_span::{sym, Symbol};
 +use std::cmp;
 +use std::ops;
 +
 +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 +enum EagernessSuggestion {
 +    // The expression is cheap and should be evaluated eagerly
 +    Eager,
 +    // The expression may be cheap, so don't suggested lazy evaluation; or the expression may not be safe to switch to
 +    // eager evaluation.
 +    NoChange,
 +    // The expression is likely expensive and should be evaluated lazily.
 +    Lazy,
 +    // The expression cannot be placed into a closure.
 +    ForceNoChange,
 +}
 +impl ops::BitOr for EagernessSuggestion {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self {
 +        cmp::max(self, rhs)
 +    }
 +}
 +impl ops::BitOrAssign for EagernessSuggestion {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Determine the eagerness of the given function call.
 +fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
 +    use EagernessSuggestion::{Eager, Lazy, NoChange};
 +    let name = name.as_str();
 +
 +    let ty = match cx.tcx.impl_of_method(fn_id) {
 +        Some(id) => cx.tcx.type_of(id),
 +        None => return Lazy,
 +    };
 +
 +    if (name.starts_with("as_") || name == "len" || name == "is_empty") && have_one_arg {
 +        if matches!(
 +            cx.tcx.crate_name(fn_id.krate),
 +            sym::std | sym::core | sym::alloc | sym::proc_macro
 +        ) {
 +            Eager
 +        } else {
 +            NoChange
 +        }
 +    } else if let ty::Adt(def, subs) = ty.kind() {
 +        // Types where the only fields are generic types (or references to) with no trait bounds other
 +        // than marker traits.
 +        // Due to the limited operations on these types functions should be fairly cheap.
 +        if def
 +            .variants()
 +            .iter()
 +            .flat_map(|v| v.fields.iter())
 +            .any(|x| matches!(cx.tcx.type_of(x.did).peel_refs().kind(), ty::Param(_)))
 +            && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
 +                PredicateKind::Clause(ty::Clause::Trait(pred)) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
 +                _ => true,
 +            })
 +            && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
 +        {
 +            // Limit the function to either `(self) -> bool` or `(&self) -> bool`
 +            match &**cx.tcx.fn_sig(fn_id).skip_binder().inputs_and_output {
 +                [arg, res] if !arg.is_mutable_ptr() && arg.peel_refs() == ty && res.is_bool() => NoChange,
 +                _ => Lazy,
 +            }
 +        } else {
 +            Lazy
 +        }
 +    } else {
 +        Lazy
 +    }
 +}
 +
++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> {
 +        cx: &'cx LateContext<'tcx>,
 +        eagerness: EagernessSuggestion,
 +    }
 +
 +    impl<'cx, 'tcx> Visitor<'tcx> for V<'cx, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            use EagernessSuggestion::{ForceNoChange, Lazy, NoChange};
 +            if self.eagerness == ForceNoChange {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::Call(
 +                    &Expr {
 +                        kind: ExprKind::Path(ref path),
 +                        hir_id,
 +                        ..
 +                    },
 +                    args,
 +                ) => match self.cx.qpath_res(path, hir_id) {
-                 | ExprKind::Path(_)
++                    res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
++                        if res_has_significant_drop(res, self.cx, e) {
 +                            self.eagerness = ForceNoChange;
 +                            return;
 +                        }
 +                    },
 +                    Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (),
 +                    // No need to walk the arguments here, `is_const_evaluatable` already did
 +                    Res::Def(..) if is_const_evaluatable(self.cx, e) => {
 +                        self.eagerness |= NoChange;
 +                        return;
 +                    },
 +                    Res::Def(_, id) => match path {
 +                        QPath::Resolved(_, p) => {
 +                            self.eagerness |=
 +                                fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty());
 +                        },
 +                        QPath::TypeRelative(_, name) => {
 +                            self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty());
 +                        },
 +                        QPath::LangItem(..) => self.eagerness = Lazy,
 +                    },
 +                    _ => self.eagerness = Lazy,
 +                },
 +                // No need to walk the arguments here, `is_const_evaluatable` already did
 +                ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => {
 +                    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
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true));
 +                },
 +                ExprKind::Index(_, e) => {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(e);
 +                    if is_copy(self.cx, ty) && !ty.is_ref() {
 +                        self.eagerness |= NoChange;
 +                    } else {
 +                        self.eagerness = Lazy;
 +                    }
 +                },
 +
 +                // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
 +                ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
 +                ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
 +
 +                ExprKind::Unary(_, e)
 +                    if matches!(
 +                        self.cx.typeck_results().expr_ty(e).kind(),
 +                        ty::Bool | ty::Int(_) | ty::Uint(_),
 +                    ) => {},
 +                ExprKind::Binary(_, lhs, rhs)
 +                    if self.cx.typeck_results().expr_ty(lhs).is_primitive()
 +                        && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {},
 +
 +                // Can't be moved into a closure
 +                ExprKind::Break(..)
 +                | ExprKind::Continue(_)
 +                | ExprKind::Ret(_)
 +                | ExprKind::InlineAsm(_)
 +                | ExprKind::Yield(..)
 +                | ExprKind::Err => {
 +                    self.eagerness = ForceNoChange;
 +                    return;
 +                },
 +
 +                // Memory allocation, custom operator, loop, or call to an unknown function
 +                ExprKind::Box(_)
 +                | ExprKind::Unary(..)
 +                | ExprKind::Binary(..)
 +                | ExprKind::Loop(..)
 +                | ExprKind::Call(..) => self.eagerness = Lazy,
 +
 +                ExprKind::ConstBlock(_)
 +                | ExprKind::Array(_)
 +                | ExprKind::Tup(_)
 +                | ExprKind::Lit(_)
 +                | ExprKind::Cast(..)
 +                | ExprKind::Type(..)
 +                | ExprKind::DropTemps(_)
 +                | ExprKind::Let(..)
 +                | ExprKind::If(..)
 +                | ExprKind::Match(..)
 +                | ExprKind::Closure { .. }
 +                | ExprKind::Field(..)
 +                | ExprKind::AddrOf(..)
 +                | ExprKind::Struct(..)
 +                | ExprKind::Repeat(..)
 +                | ExprKind::Block(Block { stmts: [], .. }, _) => (),
 +
 +                // Assignment might be to a local defined earlier, so don't eagerly evaluate.
 +                // Blocks with multiple statements might be expensive, so don't eagerly evaluate.
 +                // TODO: Actually check if either of these are true here.
 +                ExprKind::Assign(..) | ExprKind::AssignOp(..) | ExprKind::Block(..) => self.eagerness |= NoChange,
 +            }
 +            walk_expr(self, e);
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        eagerness: EagernessSuggestion::Eager,
 +    };
 +    v.visit_expr(e);
 +    v.eagerness
 +}
 +
 +/// Whether the given expression should be changed to evaluate eagerly
 +pub fn switch_to_eager_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    expr_eagerness(cx, expr) == EagernessSuggestion::Eager
 +}
 +
 +/// Whether the given expression should be changed to evaluate lazily
 +pub fn switch_to_lazy_eval<'tcx>(cx: &'_ LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    expr_eagerness(cx, expr) == EagernessSuggestion::Lazy
 +}
index 9e2682925a22116c71acdb6847f1572207f84d74,0000000000000000000000000000000000000000..90192f46cbfa09f416e88c94060d3962f5c9cfef
mode 100644,000000..100644
--- /dev/null
@@@ -1,2515 -1,0 +1,2494 @@@
- use rustc_semver::RustcVersion;
- use rustc_session::Session;
 +#![feature(array_chunks)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_chains)]
 +#![feature(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_typeck;
 +extern crate rustc_index;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_mir_dataflow;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +
 +#[macro_use]
 +pub mod sym_helper;
 +
 +pub mod ast_utils;
 +pub mod attrs;
 +mod check_proc_macro;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod macros;
 +pub mod mir;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +pub mod str_utils;
 +pub mod sugg;
 +pub mod ty;
 +pub mod usage;
 +pub mod visitors;
 +
 +pub use self::attrs::*;
 +pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 +pub use self::hir_utils::{
 +    both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
 +};
 +
 +use core::ops::ControlFlow;
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +use std::sync::OnceLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_ast::Attribute;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 +use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination,
 +    Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
 +    MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
 +    TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::place::PlaceBase;
 +use rustc_middle::ty as rustc_ty;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 +use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
 +    ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
 +    PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
 +};
 +use rustc_middle::ty::{
 +    layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
 +};
 +use rustc_middle::ty::{FloatTy, IntTy, UintTy};
- 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))
- }
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::source_map::SourceMap;
 +use rustc_span::sym;
 +use rustc_span::symbol::{kw, Ident, Symbol};
 +use rustc_span::Span;
 +use rustc_target::abi::Integer;
 +
 +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;
 +
-             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");
-                     }
-                 },
-                 _ => (),
-             }
 +#[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);
++            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);
 +        }
 +    };
 +}
 +
 +/// If the given expression is a local binding, find the initializer expression.
 +/// If that initializer expression is another local binding, find its initializer again.
 +/// This process repeats as long as possible (but usually no more than once). Initializer
 +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 +/// instead.
 +///
 +/// Examples:
 +/// ```
 +/// let abc = 1;
 +/// //        ^ output
 +/// let def = abc;
 +/// dbg!(def);
 +/// //   ^^^ input
 +///
 +/// // or...
 +/// let abc = 1;
 +/// let def = abc + 2;
 +/// //        ^^^^^^^ output
 +/// dbg!(def);
 +/// //   ^^^ input
 +/// ```
 +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
 +    while let Some(init) = path_to_local(expr)
 +        .and_then(|id| find_binding_init(cx, id))
 +        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
 +    {
 +        expr = init;
 +    }
 +    expr
 +}
 +
 +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
 +/// By only considering immutable bindings, we guarantee that the returned expression represents the
 +/// value of the binding wherever it is referenced.
 +///
 +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
 +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
 +/// canonical binding `HirId`.
 +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    let hir = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Pat(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::NONE, ..));
 +        let parent = hir.get_parent_node(hir_id);
 +        if let Some(Node::Local(local)) = hir.find(parent);
 +        then {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns `true` if the given `NodeId` is inside a constant context
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// if in_constant(cx, expr.hir_id) {
 +///     // Do something
 +/// }
 +/// ```
 +pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 +    let parent_id = cx.tcx.hir().get_parent_item(id).def_id;
 +    match cx.tcx.hir().get_by_def_id(parent_id) {
 +        Node::Item(&Item {
 +            kind: ItemKind::Const(..) | ItemKind::Static(..),
 +            ..
 +        })
 +        | Node::TraitItem(&TraitItem {
 +            kind: TraitItemKind::Const(..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Const(..),
 +            ..
 +        })
 +        | Node::AnonConst(_) => true,
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(ref sig, ..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(ref sig, _),
 +            ..
 +        }) => sig.header.constness == Constness::Const,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if a `Res` refers to a constructor of a `LangItem`
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
 +    if let Res::Def(DefKind::Ctor(..), id) = res
 +        && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
 +        && let Some(id) = cx.tcx.opt_parent(id)
 +    {
 +        id == lang_id
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_res_diagnostic_ctor(cx: &LateContext<'_>, res: Res, diag_item: Symbol) -> bool {
 +    if let Res::Def(DefKind::Ctor(..), id) = res
 +        && let Some(id) = cx.tcx.opt_parent(id)
 +    {
 +        cx.tcx.is_diagnostic_item(diag_item, id)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Checks if a `QPath` resolves to a constructor of a diagnostic item.
 +pub fn is_diagnostic_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, diagnostic_item: Symbol) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            return cx.tcx.is_diagnostic_item(diagnostic_item, cx.tcx.parent(ctor_id));
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if the `DefId` matches the given diagnostic item or it's constructor.
 +pub fn is_diagnostic_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: Symbol) -> bool {
 +    let did = match cx.tcx.def_kind(did) {
 +        DefKind::Ctor(..) => cx.tcx.parent(did),
 +        // Constructors for types in external crates seem to have `DefKind::Variant`
 +        DefKind::Variant => match cx.tcx.opt_parent(did) {
 +            Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
 +            _ => did,
 +        },
 +        _ => did,
 +    };
 +
 +    cx.tcx.is_diagnostic_item(item, did)
 +}
 +
 +/// Checks if the `DefId` matches the given `LangItem` or it's constructor.
 +pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) -> bool {
 +    let did = match cx.tcx.def_kind(did) {
 +        DefKind::Ctor(..) => cx.tcx.parent(did),
 +        // Constructors for types in external crates seem to have `DefKind::Variant`
 +        DefKind::Variant => match cx.tcx.opt_parent(did) {
 +            Some(did) if matches!(cx.tcx.def_kind(did), DefKind::Variant) => did,
 +            _ => did,
 +        },
 +        _ => did,
 +    };
 +
 +    cx.tcx.lang_items().get(item) == Some(did)
 +}
 +
 +pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: None,
 +                ..
 +            },
 +            _
 +        ) | ExprKind::Tup([])
 +    )
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild(pat: &Pat<'_>) -> bool {
 +    matches!(pat.kind, PatKind::Wild)
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +/// This is a deprecated function, consider using [`is_trait_method`].
 +pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
 +    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +    let trt_id = cx.tcx.trait_of_item(def_id);
 +    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 +}
 +
 +/// Checks if a method is defined in an impl of a diagnostic item
 +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +            return cx.tcx.is_diagnostic_item(diag_item, adt.did());
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if a method is in a diagnostic item trait
 +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
 +        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
 +    }
 +    false
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    cx.typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 +}
 +
 +/// Checks if the given expression is a path referring an item on the trait
 +/// that is marked with the given diagnostic item.
 +///
 +/// For checking method call expressions instead of path expressions, use
 +/// [`is_trait_method`].
 +///
 +/// For example, this can be used to find if an expression like `u64::default`
 +/// refers to an item of the trait `Default`, which is associated with the
 +/// `diag_item` of `sym::Default`.
 +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    if let hir::ExprKind::Path(ref qpath) = expr.kind {
 +        cx.qpath_res(qpath, expr.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    last_path_segment(qpath)
 +        .args
 +        .map_or(&[][..], |a| a.args)
 +        .iter()
 +        .filter_map(|a| match a {
 +            hir::GenericArg::Type(ty) => Some(*ty),
 +            _ => None,
 +        })
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +///
 +/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
 +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
 +    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 +/// it matches the given lang item.
 +pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
 +    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 +/// it matches the given diagnostic item.
 +pub fn is_path_diagnostic_item<'tcx>(
 +    cx: &LateContext<'_>,
 +    maybe_path: &impl MaybePath<'tcx>,
 +    diag_item: Symbol,
 +) -> bool {
 +    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `Path` against a slice of segment string literals.
 +///
 +/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 +/// `rustc_hir::Path`.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// if match_path(&trait_ref.path, &paths::HASH) {
 +///     // This is the `std::hash::Hash` trait.
 +/// }
 +///
 +/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
 +///     // This is a `rustc_middle::lint::Lint`.
 +/// }
 +/// ```
 +pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
 +    path.segments
 +        .iter()
 +        .rev()
 +        .zip(segments.iter().rev())
 +        .all(|(a, b)| a.ident.name.as_str() == *b)
 +}
 +
 +/// If the expression is a path to a local, returns the canonical `HirId` of the local.
 +pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
 +    if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
 +        if let Res::Local(id) = path.res {
 +            return Some(id);
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns true if the expression is a path to a local with the specified `HirId`.
 +/// Use this function to see if an expression matches a function argument or a match binding.
 +pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
 +    path_to_local(expr) == Some(id)
 +}
 +
 +pub trait MaybePath<'hir> {
 +    fn hir_id(&self) -> HirId;
 +    fn qpath_opt(&self) -> Option<&QPath<'hir>>;
 +}
 +
 +macro_rules! maybe_path {
 +    ($ty:ident, $kind:ident) => {
 +        impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn qpath_opt(&self) -> Option<&QPath<'hir>> {
 +                match &self.kind {
 +                    hir::$kind::Path(qpath) => Some(qpath),
 +                    _ => None,
 +                }
 +            }
 +        }
 +    };
 +}
 +maybe_path!(Expr, ExprKind);
 +maybe_path!(Pat, PatKind);
 +maybe_path!(Ty, TyKind);
 +
 +/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
 +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
 +    match maybe_path.qpath_opt() {
 +        None => Res::Err,
 +        Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
 +    }
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
 +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
 +    path_res(cx, maybe_path).opt_def_id()
 +}
 +
 +fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
 +    let ty = match name {
 +        "bool" => BoolSimplifiedType,
 +        "char" => CharSimplifiedType,
 +        "str" => StrSimplifiedType,
 +        "array" => ArraySimplifiedType,
 +        "slice" => SliceSimplifiedType,
 +        // FIXME: rustdoc documents these two using just `pointer`.
 +        //
 +        // Maybe this is something we should do here too.
 +        "const_ptr" => PtrSimplifiedType(Mutability::Not),
 +        "mut_ptr" => PtrSimplifiedType(Mutability::Mut),
 +        "isize" => IntSimplifiedType(IntTy::Isize),
 +        "i8" => IntSimplifiedType(IntTy::I8),
 +        "i16" => IntSimplifiedType(IntTy::I16),
 +        "i32" => IntSimplifiedType(IntTy::I32),
 +        "i64" => IntSimplifiedType(IntTy::I64),
 +        "i128" => IntSimplifiedType(IntTy::I128),
 +        "usize" => UintSimplifiedType(UintTy::Usize),
 +        "u8" => UintSimplifiedType(UintTy::U8),
 +        "u16" => UintSimplifiedType(UintTy::U16),
 +        "u32" => UintSimplifiedType(UintTy::U32),
 +        "u64" => UintSimplifiedType(UintTy::U64),
 +        "u128" => UintSimplifiedType(UintTy::U128),
 +        "f32" => FloatSimplifiedType(FloatTy::F32),
 +        "f64" => FloatSimplifiedType(FloatTy::F64),
 +        _ => return [].iter().copied(),
 +    };
 +
 +    tcx.incoherent_impls(ty).iter().copied()
 +}
 +
 +fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
 +    match tcx.def_kind(def_id) {
 +        DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
 +            .module_children(def_id)
 +            .iter()
 +            .filter(|item| item.ident.name == name)
 +            .map(|child| child.res.expect_non_local())
 +            .collect(),
 +        DefKind::Impl => tcx
 +            .associated_item_def_ids(def_id)
 +            .iter()
 +            .copied()
 +            .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
 +            .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
 +            .collect(),
 +        _ => Vec::new(),
 +    }
 +}
 +
 +fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
 +    let hir = tcx.hir();
 +
 +    let root_mod;
 +    let item_kind = match hir.find_by_def_id(local_id) {
 +        Some(Node::Crate(r#mod)) => {
 +            root_mod = ItemKind::Mod(r#mod);
 +            &root_mod
 +        },
 +        Some(Node::Item(item)) => &item.kind,
 +        _ => return Vec::new(),
 +    };
 +
 +    let res = |ident: Ident, owner_id: OwnerId| {
 +        if ident.name == name {
 +            let def_id = owner_id.to_def_id();
 +            Some(Res::Def(tcx.def_kind(def_id), def_id))
 +        } else {
 +            None
 +        }
 +    };
 +
 +    match item_kind {
 +        ItemKind::Mod(r#mod) => r#mod
 +            .item_ids
 +            .iter()
 +            .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
 +            .collect(),
 +        ItemKind::Impl(r#impl) => r#impl
 +            .items
 +            .iter()
 +            .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
 +            .collect(),
 +        ItemKind::Trait(.., trait_item_refs) => trait_item_refs
 +            .iter()
 +            .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
 +            .collect(),
 +        _ => Vec::new(),
 +    }
 +}
 +
 +fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
 +    if let Some(local_id) = def_id.as_local() {
 +        local_item_children_by_name(tcx, local_id, name)
 +    } else {
 +        non_local_item_children_by_name(tcx, def_id, name)
 +    }
 +}
 +
 +/// Resolves a def path like `std::vec::Vec`.
 +///
 +/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
 +/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
 +///
 +/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
 +/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
 +///
 +/// This function is expensive and should be used sparingly.
 +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
 +    fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
 +        tcx.crates(())
 +            .iter()
 +            .copied()
 +            .filter(move |&num| tcx.crate_name(num) == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
 +    let tcx = cx.tcx;
 +
 +    let (base, mut path) = match *path {
 +        [primitive] => {
 +            return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
 +        },
 +        [base, ref path @ ..] => (base, path),
 +        _ => return Vec::new(),
 +    };
 +
 +    let base_sym = Symbol::intern(base);
 +
 +    let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
 +        Some(LOCAL_CRATE.as_def_id())
 +    } else {
 +        None
 +    };
 +
 +    let starts = find_primitive_impls(tcx, base)
 +        .chain(find_crates(tcx, base_sym))
 +        .chain(local_crate)
 +        .map(|id| Res::Def(tcx.def_kind(id), id));
 +
 +    let mut resolutions: Vec<Res> = starts.collect();
 +
 +    while let [segment, rest @ ..] = path {
 +        path = rest;
 +        let segment = Symbol::intern(segment);
 +
 +        resolutions = resolutions
 +            .into_iter()
 +            .filter_map(|res| res.opt_def_id())
 +            .flat_map(|def_id| {
 +                // When the current def_id is e.g. `struct S`, check the impl items in
 +                // `impl S { ... }`
 +                let inherent_impl_children = tcx
 +                    .inherent_impls(def_id)
 +                    .iter()
 +                    .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
 +
 +                let direct_children = item_children_by_name(tcx, def_id, segment);
 +
 +                inherent_impl_children.chain(direct_children)
 +            })
 +            .collect();
 +    }
 +
 +    resolutions
 +}
 +
 +/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
 +pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
 +    def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id())
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +///
 +/// This function is expensive and should be used sparingly.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    def_path_res(cx, path).into_iter().find_map(|res| match res {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != hir::CRATE_OWNER_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl.def_id);
 +        if let hir::ItemKind::Impl(impl_) = &item.kind;
 +        then {
 +            return impl_.of_trait.as_ref();
 +        }
 +    }
 +    None
 +}
 +
 +/// This method will return tuple of projection stack and root of the expression,
 +/// used in `can_mut_borrow_both`.
 +///
 +/// For example, if `e` represents the `v[0].a.b[x]`
 +/// this method will return a tuple, composed of a `Vec`
 +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
 +/// and an `Expr` for root of them, `v`
 +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
 +    let mut result = vec![];
 +    let root = loop {
 +        match e.kind {
 +            ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
 +                result.push(e);
 +                e = ep;
 +            },
 +            _ => break e,
 +        };
 +    };
 +    result.reverse();
 +    (result, root)
 +}
 +
 +/// Gets the mutability of the custom deref adjustment, if any.
 +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
 +    cx.typeck_results()
 +        .expr_adjustments(e)
 +        .iter()
 +        .find_map(|a| match a.kind {
 +            Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
 +            Adjust::Deref(None) => None,
 +            _ => Some(None),
 +        })
 +        .and_then(|x| x)
 +}
 +
 +/// Checks if two expressions can be mutably borrowed simultaneously
 +/// and they aren't dependent on borrowing same thing twice
 +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
 +    let (s1, r1) = projection_stack(e1);
 +    let (s2, r2) = projection_stack(e2);
 +    if !eq_expr_value(cx, r1, r2) {
 +        return true;
 +    }
 +    if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
 +        return false;
 +    }
 +
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
 +            return false;
 +        }
 +
 +        match (&x1.kind, &x2.kind) {
 +            (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
 +                if i1 != i2 {
 +                    return true;
 +                }
 +            },
 +            (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
 +                if !eq_expr_value(cx, i1, i2) {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
 +/// constructor from the std library
 +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
 +    let std_types_symbols = &[
 +        sym::Vec,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::HashMap,
 +        sym::BTreeMap,
 +        sym::HashSet,
 +        sym::BTreeSet,
 +        sym::BinaryHeap,
 +    ];
 +
 +    if let QPath::TypeRelative(_, method) = path {
 +        if method.ident.name == sym::new {
 +            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +                    return std_types_symbols.iter().any(|&symbol| {
 +                        cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
 +                    });
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Return true if the expr is equal to `Default::default` when evaluated.
 +pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        if is_diag_trait_item(cx, repl_def_id, sym::Default)
 +            || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 +        then { true } else { false }
 +    }
 +}
 +
 +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
 +/// It doesn't cover all cases, for example indirect function calls (some of std
 +/// functions are supported) but it is the best we have.
 +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    match &e.kind {
 +        ExprKind::Lit(lit) => match lit.node {
 +            LitKind::Bool(false) | LitKind::Int(0, _) => true,
 +            LitKind::Str(s, _) => s.is_empty(),
 +            _ => false,
 +        },
 +        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
 +        ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
 +            if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
 +            if let LitKind::Int(v, _) = const_lit.node;
 +            if v <= 32 && is_default_equivalent(cx, x);
 +            then {
 +                true
 +            }
 +            else {
 +                false
 +            }
 +        },
 +        ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
 +        ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
 +        ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
 +        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
 +        _ => false,
 +    }
 +}
 +
 +fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &Expr<'_>) -> bool {
 +    if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = from_func.kind &&
 +        seg.ident.name == sym::from
 +    {
 +        match arg.kind {
 +            ExprKind::Lit(hir::Lit {
 +                node: LitKind::Str(ref sym, _),
 +                ..
 +            }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
 +            ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
 +            ExprKind::Repeat(_, ArrayLen::Body(len)) => {
 +                if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
 +                    let LitKind::Int(v, _) = const_lit.node
 +                {
 +                        return v == 0 && is_path_diagnostic_item(cx, ty, sym::Vec);
 +                }
 +            }
 +            _ => (),
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if the top level expression can be moved into a closure as is.
 +/// Currently checks for:
 +/// * Break/Continue outside the given loop HIR ids.
 +/// * Yield/Return statements.
 +/// * Inline assembly.
 +/// * Usages of a field of a local where the type of the local can be partially moved.
 +///
 +/// For example, given the following function:
 +///
 +/// ```
 +/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
 +///     for item in iter {
 +///         let s = item.1;
 +///         if item.0 > 10 {
 +///             continue;
 +///         } else {
 +///             s.clear();
 +///         }
 +///     }
 +/// }
 +/// ```
 +///
 +/// When called on the expression `item.0` this will return false unless the local `item` is in the
 +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
 +/// isn't always safe to move into a closure when only a single field is needed.
 +///
 +/// When called on the `continue` expression this will return false unless the outer loop expression
 +/// is in the `loop_ids` set.
 +///
 +/// Note that this check is not recursive, so passing the `if` expression will always return true
 +/// even though sub-expressions might return false.
 +pub fn can_move_expr_to_closure_no_visit<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    loop_ids: &[HirId],
 +    ignore_locals: &HirIdSet,
 +) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
 +    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
 +        let mut capture = CaptureKind::Ref(Mutability::Not);
 +        pat.each_binding_or_first(&mut |_, id, span, _| match cx
 +            .typeck_results()
 +            .extract_binding_mode(cx.sess(), id, span)
 +            .unwrap()
 +        {
 +            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
 +                capture = CaptureKind::Value;
 +            },
 +            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
 +                capture = CaptureKind::Ref(Mutability::Mut);
 +            },
 +            _ => (),
 +        });
 +        capture
 +    }
 +
 +    debug_assert!(matches!(
 +        e.kind,
 +        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
 +    ));
 +
 +    let mut child_id = e.hir_id;
 +    let mut capture = CaptureKind::Value;
 +    let mut capture_expr_ty = e;
 +
 +    for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
 +        if let [
 +            Adjustment {
 +                kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
 +                target,
 +            },
 +            ref adjust @ ..,
 +        ] = *cx
 +            .typeck_results()
 +            .adjustments()
 +            .get(child_id)
 +            .map_or(&[][..], |x| &**x)
 +        {
 +            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
 +                *adjust.last().map_or(target, |a| a.target).kind()
 +            {
 +                return CaptureKind::Ref(mutability);
 +            }
 +        }
 +
 +        match parent {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
 +                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
 +                ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
 +                    return CaptureKind::Ref(Mutability::Mut);
 +                },
 +                ExprKind::Field(..) => {
 +                    if capture == CaptureKind::Value {
 +                        capture_expr_ty = e;
 +                    }
 +                },
 +                ExprKind::Let(let_expr) => {
 +                    let mutability = match pat_capture_kind(cx, let_expr.pat) {
 +                        CaptureKind::Value => Mutability::Not,
 +                        CaptureKind::Ref(m) => m,
 +                    };
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                ExprKind::Match(_, arms, _) => {
 +                    let mut mutability = Mutability::Not;
 +                    for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
 +                        match capture {
 +                            CaptureKind::Value => break,
 +                            CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
 +                            CaptureKind::Ref(Mutability::Not) => (),
 +                        }
 +                    }
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                _ => break,
 +            },
 +            Node::Local(l) => match pat_capture_kind(cx, l.pat) {
 +                CaptureKind::Value => break,
 +                capture @ CaptureKind::Ref(_) => return capture,
 +            },
 +            _ => break,
 +        }
 +
 +        child_id = parent_id;
 +    }
 +
 +    if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
 +        // Copy types are never automatically captured by value.
 +        CaptureKind::Ref(Mutability::Not)
 +    } else {
 +        capture
 +    }
 +}
 +
 +/// Checks if the expression can be moved into a closure as is. This will return a list of captures
 +/// if so, otherwise, `None`.
 +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        // Stack of potential break targets contained in the expression.
 +        loops: Vec<HirId>,
 +        /// Local variables created in the expression. These don't need to be captured.
 +        locals: HirIdSet,
 +        /// Whether this expression can be turned into a closure.
 +        allow_closure: bool,
 +        /// Locals which need to be captured, and whether they need to be by value, reference, or
 +        /// mutable reference.
 +        captures: HirIdMap<CaptureKind>,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.allow_closure {
 +                return;
 +            }
 +
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
 +                    if !self.locals.contains(&l) {
 +                        let cap = capture_local_usage(self.cx, e);
 +                        self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
 +                    }
 +                },
 +                ExprKind::Closure { .. } => {
 +                    let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
 +                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
 +                        let local_id = match capture.place.base {
 +                            PlaceBase::Local(id) => id,
 +                            PlaceBase::Upvar(var) => var.var_path.hir_id,
 +                            _ => continue,
 +                        };
 +                        if !self.locals.contains(&local_id) {
 +                            let capture = match capture.info.capture_kind {
 +                                UpvarCapture::ByValue => CaptureKind::Value,
 +                                UpvarCapture::ByRef(kind) => match kind {
 +                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
 +                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
 +                                        CaptureKind::Ref(Mutability::Mut)
 +                                    },
 +                                },
 +                            };
 +                            self.captures
 +                                .entry(local_id)
 +                                .and_modify(|e| *e |= capture)
 +                                .or_insert(capture);
 +                        }
 +                    }
 +                },
 +                ExprKind::Loop(b, ..) => {
 +                    self.loops.push(e.hir_id);
 +                    self.visit_block(b);
 +                    self.loops.pop();
 +                },
 +                _ => {
 +                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
 +                    walk_expr(self, e);
 +                },
 +            }
 +        }
 +
 +        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +            p.each_binding_or_first(&mut |_, id, _, _| {
 +                self.locals.insert(id);
 +            });
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +        captures: HirIdMap::default(),
 +    };
 +    v.visit_expr(expr);
 +    v.allow_closure.then_some(v.captures)
 +}
 +
 +/// Arguments of a method: the receiver and all the additional arguments.
 +pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
 +
 +/// Returns the method names and argument list of nested method call expressions that make up
 +/// `expr`. method/span lists are sorted with the most recent call first.
 +pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
 +    let mut method_names = Vec::with_capacity(max_depth);
 +    let mut arg_lists = Vec::with_capacity(max_depth);
 +    let mut spans = Vec::with_capacity(max_depth);
 +
 +    let mut current = expr;
 +    for _ in 0..max_depth {
 +        if let ExprKind::MethodCall(path, receiver, args, _) = &current.kind {
 +            if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push((*receiver, &**args));
 +            spans.push(path.ident.span);
 +            current = receiver;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    (method_names, arg_lists, spans)
 +}
 +
 +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
 +///
 +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
 +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
 +/// containing the `Expr`s for
 +/// `.bar()` and `.baz()`
 +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<(&'a Expr<'a>, &'a [Expr<'a>])>> {
 +    let mut current = expr;
 +    let mut matched = Vec::with_capacity(methods.len());
 +    for method_name in methods.iter().rev() {
 +        // method chains are stored last -> first
 +        if let ExprKind::MethodCall(path, receiver, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if receiver.span.from_expansion() || args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push((receiver, args)); // build up `matched` backwards
 +                current = receiver; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id).def_id;
 +    match cx.tcx.hir().find_by_def_id(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
 +pub struct ContainsName {
 +    pub name: Symbol,
 +    pub result: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ContainsName {
 +    fn visit_name(&mut self, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +}
 +
 +/// Checks if an `Expr` contains a certain name.
 +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 +    let mut cn = ContainsName { name, result: false };
 +    cn.visit_expr(expr);
 +    cn.result
 +}
 +
 +/// Returns `true` if `expr` contains a return expression
 +pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
 +    for_each_expr(expr, |e| {
 +        if matches!(e.kind, hir::ExprKind::Ret(..)) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_some()
 +}
 +
 +/// Gets the parent node, if any.
 +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 +    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 +}
 +
 +/// Gets the parent expression, if any –- this is useful to constrain a lint.
 +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    get_parent_expr_for_hir(cx, e.hir_id)
 +}
 +
 +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
 +/// constraint lints
 +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
 +    match get_parent_node(cx.tcx, hir_id) {
 +        Some(Node::Expr(parent)) => Some(parent),
 +        _ => None,
 +    }
 +}
 +
 +pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
 +    let map = &cx.tcx.hir();
 +    let enclosing_node = map
 +        .get_enclosing_scope(hir_id)
 +        .and_then(|enclosing_id| map.find(enclosing_id));
 +    enclosing_node.and_then(|node| match node {
 +        Node::Block(block) => Some(block),
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(_, _, eid),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(_, eid),
 +            ..
 +        }) => match cx.tcx.hir().body(eid).value.kind {
 +            ExprKind::Block(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the loop or closure enclosing the given expression, if any.
 +pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'_>,
 +) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::Closure { .. } => {
 +                    if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
 +                        && subs.as_closure().kind() == ClosureKind::FnOnce
 +                    {
 +                        continue;
 +                    }
 +                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
 +                        let Node::Expr(e) = node else {
 +                            return None;
 +                        };
 +                        match e.kind {
 +                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
 +                            ExprKind::Call(f, args) => {
 +                                let i = args.iter().position(|arg| arg.hir_id == id)?;
 +                                let sig = expr_sig(cx, f)?;
 +                                let predicates = sig
 +                                    .predicates_id()
 +                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
 +                                    .caller_bounds();
 +                                sig.input(i).and_then(|ty| {
 +                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
 +                                })
 +                            },
 +                            ExprKind::MethodCall(_, receiver, args, _) => {
 +                                let i = std::iter::once(receiver)
 +                                    .chain(args.iter())
 +                                    .position(|arg| arg.hir_id == id)?;
 +                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
 +                                let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
 +                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
 +                            },
 +                            _ => None,
 +                        }
 +                    })
 +                    .is_some();
 +                    if !is_once {
 +                        return Some(e);
 +                    }
 +                },
 +                ExprKind::Loop(..) => return Some(e),
 +                _ => (),
 +            },
 +            Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    match tcx.hir().parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// and no statements. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{{ x }}`          -> `x`
 +///  * `{ x; }`           -> `{ x; }`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{ x; }`           -> `x`
 +///  * `{{ x; }}`         -> `x`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        }
 +        | Block {
 +            stmts:
 +                [
 +                    Stmt {
 +                        kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
 +                        ..
 +                    },
 +                ],
 +            expr: None,
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let mut iter = tcx.hir().parent_iter(expr.hir_id);
 +    match iter.next() {
 +        Some((
 +            _,
 +            Node::Expr(Expr {
 +                kind: ExprKind::If(_, _, Some(else_expr)),
 +                ..
 +            }),
 +        )) => else_expr.hir_id == expr.hir_id,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks whether the given expression is a constant integer of the given value.
 +/// unlike `is_integer_literal`, this version does const folding
 +pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
 +    if is_integer_literal(e, value) {
 +        return true;
 +    }
 +    let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
 +    if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
 +        return value == v;
 +    }
 +    false
 +}
 +
 +/// Checks whether the given expression is a constant literal of the given value.
 +pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
 +    // FIXME: use constant folding
 +    if let ExprKind::Lit(ref spanned) = expr.kind {
 +        if let LitKind::Int(v, _) = spanned.node {
 +            return v == value;
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if the given `Expr` has been coerced before.
 +///
 +/// Examples of coercions can be found in the Nomicon at
 +/// <https://doc.rust-lang.org/nomicon/coercions.html>.
 +///
 +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_hir_analysis::check::coercion` for
 +/// more information on adjustments and coercions.
 +pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    cx.typeck_results().adjustments().get(e.hir_id).is_some()
 +}
 +
 +/// Returns the pre-expansion span if this comes from an expansion of the
 +/// macro `name`.
 +/// See also [`is_direct_expn_of`].
 +#[must_use]
 +pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
 +    loop {
 +        if span.from_expansion() {
 +            let data = span.ctxt().outer_expn_data();
 +            let new_span = data.call_site;
 +
 +            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +                if mac_name.as_str() == name {
 +                    return Some(new_span);
 +                }
 +            }
 +
 +            span = new_span;
 +        } else {
 +            return None;
 +        }
 +    }
 +}
 +
 +/// Returns the pre-expansion span if the span directly comes from an expansion
 +/// of the macro `name`.
 +/// The difference with [`is_expn_of`] is that in
 +/// ```rust
 +/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
 +/// # macro_rules! bar { ($e:expr) => { $e } }
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
 +/// from `bar!` by `is_direct_expn_of`.
 +#[must_use]
 +pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
 +    if span.from_expansion() {
 +        let data = span.ctxt().outer_expn_data();
 +        let new_span = data.call_site;
 +
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Convenience function to get the nth argument type of a function.
 +pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
 +    cx.tcx.erase_late_bound_regions(arg)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
 +        i.into_iter().any(|pat| is_refutable(cx, pat))
 +    }
 +
 +    match pat.kind {
 +        PatKind::Wild => false,
 +        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
 +        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats)
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
 +        PatKind::Slice(head, middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
 +                _ => {
 +                    // unreachable!()
 +                    true
 +                },
 +            }
 +        },
 +    }
 +}
 +
 +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
 +/// the function once on the given pattern.
 +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
 +    if let PatKind::Or(pats) = pat.kind {
 +        pats.iter().for_each(f);
 +    } else {
 +        f(pat);
 +    }
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
 +        if let Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } = path.res {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
 +    (0..decl.inputs.len()).map(move |i| &body.params[i])
 +}
 +
 +/// Checks if a given expression is a match expression expanded from the `?`
 +/// operator or the `try` macro.
 +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if_chain! {
 +            if let PatKind::TupleStruct(ref path, pat, ddpos) = arm.pat.kind;
 +            if ddpos.as_opt_usize().is_none();
 +            if is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultOk);
 +            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
 +            if path_to_local_id(arm.body, hir_id);
 +            then {
 +                return true;
 +            }
 +        }
 +        false
 +    }
 +
 +    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
 +            is_res_lang_ctor(cx, cx.qpath_res(path, arm.pat.hir_id), ResultErr)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    if let ExprKind::Match(_, arms, ref source) = expr.kind {
 +        // desugared from a `?` operator
 +        if *source == MatchSource::TryDesugar {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context. This is useful for
 +/// skipping long running code when it's unnecessary
 +///
 +/// This function should check the lint level for the same node, that the lint will
 +/// be emitted at. If the information is buffered to be emitted at a later point, please
 +/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
 +/// expectations at the checked nodes will be fulfilled.
 +pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[expect(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[expect(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
 +    attrs.iter().any(|attr| attr.has_name(symbol))
 +}
 +
 +pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
 +}
 +
 +pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if has_attr(map.attrs(enclosing_node), symbol) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.get_parent_item(enclosing_node).into();
 +    }
 +
 +    false
 +}
 +
 +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    any_parent_has_attr(tcx, node, sym::automatically_derived)
 +}
 +
 +/// Matches a function call with the given path and returns the arguments.
 +///
 +/// Usage:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
 +/// This function is deprecated. Use [`match_function_call_with_def_id`].
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args);
 +        }
 +    };
 +    None
 +}
 +
 +pub fn match_function_call_with_def_id<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    fun_def_id: DefId,
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if cx.qpath_res(qpath, fun.hir_id).opt_def_id() == Some(fun_def_id);
 +        then {
 +            return Some(args);
 +        }
 +    };
 +    None
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 +}
 +
 +/// Checks if the given `DefId` matches the `libc` item.
 +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
 +    let path = cx.get_def_path(did);
 +    // libc is meant to be used as a flat list of names, but they're all actually defined in different
 +    // modules based on the target platform. Ignore everything but crate name and the item name.
 +    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
 +}
 +
 +/// Returns the list of condition expressions and the list of blocks in a
 +/// sequence of `if/else`.
 +/// E.g., this returns `([a, b], [c, d, e])` for the expression
 +/// `if a { c } else if b { d } else { e }`.
 +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
 +    let mut conds = Vec::new();
 +    let mut blocks: Vec<&Block<'_>> = Vec::new();
 +
 +    while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
 +        conds.push(cond);
 +        if let ExprKind::Block(block, _) = then.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(else_expr) = r#else {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    match kind {
 +        FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
 +        FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
 +        FnKind::Closure => false,
 +    }
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[
 +            Expr {
 +                kind: ExprKind::Closure(&Closure { body, .. }),
 +                ..
 +            },
 +        ],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
 +}
 +
 +/// Checks if an expression represents the identity function
 +/// Only examines closures and `std::convert::identity`
 +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
 +    /// * `|x| x`
 +    /// * `|x| return x`
 +    /// * `|x| { return x }`
 +    /// * `|x| { return x; }`
 +    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 +        let id = if_chain! {
 +            if let [param] = func.params;
 +            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
 +            then {
 +                id
 +            } else {
 +                return false;
 +            }
 +        };
 +
 +        let mut expr = func.value;
 +        loop {
 +            match expr.kind {
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
 +                | ExprKind::Ret(Some(e)) => expr = e,
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
 +                    if_chain! {
 +                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
 +                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
 +                        then {
 +                            expr = ret_val;
 +                        } else {
 +                            return false;
 +                        }
 +                    }
 +                },
 +                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
 +            }
 +        }
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
 +        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +/// Returns both the node and the `HirId` of the closest child node.
 +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
 +    let mut child_id = expr.hir_id;
 +    let mut iter = tcx.hir().parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some((Node::Expr(expr), child_id)),
 +            },
 +            Some((_, node)) => break Some((node, child_id)),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some((
 +            Node::Stmt(Stmt {
 +                kind: StmtKind::Expr(_)
 +                    | StmtKind::Semi(_)
 +                    | StmtKind::Local(Local {
 +                        pat: Pat {
 +                            kind: PatKind::Wild,
 +                            ..
 +                        },
 +                        ..
 +                    }),
 +                ..
 +            }),
 +            _
 +        ))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 +}
 +
 +pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
 +    if !is_no_std_crate(cx) {
 +        Some("std")
 +    } else if !is_no_core_crate(cx) {
 +        Some("core")
 +    } else {
 +        None
 +    }
 +}
 +
 +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            normal.item.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            normal.item.path == sym::no_core
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust
 +/// # struct S;
 +/// # trait Trait { fn f(); }
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
 +    traits::impossible_predicates(
 +        cx.tcx,
 +        traits::elaborate_predicates(cx.tcx, predicates)
 +            .map(|o| o.predicate)
 +            .collect::<Vec<_>>(),
 +    )
 +}
 +
 +/// Returns the `DefId` of the callee if the given expression is a function or method call.
 +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
 +    match &expr.kind {
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(qpath),
 +                hir_id: path_hir_id,
 +                ..
 +            },
 +            ..,
 +        ) => {
 +            // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
 +            // deref to fn pointers, dyn Fn, impl Fn - #8850
 +            if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
 +                cx.typeck_results().qpath_res(qpath, *path_hir_id)
 +            {
 +                Some(id)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns `Option<String>` where String is a textual representation of the type encapsulated in
 +/// the slice iff the given expression is a slice of primitives (as defined in the
 +/// `is_recursively_primitive_type` function) and `None` otherwise.
 +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
 +    let expr_kind = expr_type.kind();
 +    let is_primitive = match expr_kind {
 +        rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
 +        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
 +            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
 +                is_recursively_primitive_type(*element_type)
 +            } else {
 +                unreachable!()
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_primitive {
 +        // if we have wrappers like Array, Slice or Tuple, print these
 +        // and get the type enclosed in the slice ref
 +        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
 +            rustc_ty::Slice(..) => return Some("slice".into()),
 +            rustc_ty::Array(..) => return Some("array".into()),
 +            rustc_ty::Tuple(..) => return Some("tuple".into()),
 +            _ => {
 +                // is_recursively_primitive_type() should have taken care
 +                // of the rest and we can rely on the type that is found
 +                let refs_peeled = expr_type.peel_refs();
 +                return Some(refs_peeled.walk().last().unwrap().to_string());
 +            },
 +        }
 +    }
 +    None
 +}
 +
 +/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
 +/// `hash` must be comformed with `eq`
 +pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
 +where
 +    Hash: Fn(&T) -> u64,
 +    Eq: Fn(&T, &T) -> bool,
 +{
 +    match exprs {
 +        [a, b] if eq(a, b) => return vec![(a, b)],
 +        _ if exprs.len() <= 2 => return vec![],
 +        _ => {},
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: UnhashMap<u64, Vec<&_>> =
 +        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
 +
 +    for expr in exprs {
 +        match map.entry(hash(expr)) {
 +            Entry::Occupied(mut o) => {
 +                for o in o.get() {
 +                    if eq(o, expr) {
 +                        match_expr_list.push((o, expr));
 +                    }
 +                }
 +                o.get_mut().push(expr);
 +            },
 +            Entry::Vacant(v) => {
 +                v.insert(vec![expr]);
 +            },
 +        }
 +    }
 +
 +    match_expr_list
 +}
 +
 +/// Peels off all references on the pattern. Returns the underlying pattern and the number of
 +/// references removed.
 +pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
 +        if let PatKind::Ref(pat, _) = pat.kind {
 +            peel(pat, count + 1)
 +        } else {
 +            (pat, count)
 +        }
 +    }
 +    peel(pat, 0)
 +}
 +
 +/// Peels of expressions while the given closure returns `Some`.
 +pub fn peel_hir_expr_while<'tcx>(
 +    mut expr: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
 +) -> &'tcx Expr<'tcx> {
 +    while let Some(e) = f(expr) {
 +        expr = e;
 +    }
 +    expr
 +}
 +
 +/// Peels off up to the given number of references on the expression. Returns the underlying
 +/// expression and the number of references removed.
 +pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
 +    let mut remaining = count;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
 +            remaining -= 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count - remaining)
 +}
 +
 +/// Peels off all references on the expression. Returns the underlying expression and the number of
 +/// references removed.
 +pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
 +    let mut count = 0;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
 +            count += 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count)
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
 +    let mut count = 0;
 +    loop {
 +        match &ty.kind {
 +            TyKind::Rptr(_, ref_ty) => {
 +                ty = ref_ty.ty;
 +                count += 1;
 +            },
 +            _ => break (ty, count),
 +        }
 +    }
 +}
 +
 +/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 +/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 +pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    loop {
 +        match expr.kind {
 +            ExprKind::AddrOf(_, _, e) => expr = e,
 +            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        if let Res::Def(_, def_id) = path.res {
 +            return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
 +        }
 +    }
 +    false
 +}
 +
 +static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
 +
 +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
 +    let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
 +    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
 +    let value = map.entry(module);
 +    match value {
 +        Entry::Occupied(entry) => f(entry.get()),
 +        Entry::Vacant(entry) => {
 +            let mut names = Vec::new();
 +            for id in tcx.hir().module_items(module) {
 +                if matches!(tcx.def_kind(id.owner_id), DefKind::Const)
 +                    && let item = tcx.hir().item(id)
 +                    && let ItemKind::Const(ty, _body) = item.kind {
 +                    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +                        // We could also check for the type name `test::TestDescAndFn`
 +                        if let Res::Def(DefKind::Struct, _) = path.res {
 +                            let has_test_marker = tcx
 +                                .hir()
 +                                .attrs(item.hir_id())
 +                                .iter()
 +                                .any(|a| a.has_name(sym::rustc_test_marker));
 +                            if has_test_marker {
 +                                names.push(item.ident.name);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            names.sort_unstable();
 +            f(entry.insert(names))
 +        },
 +    }
 +}
 +
 +/// Checks if the function containing the given `HirId` is a `#[test]` function
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    with_test_item_names(tcx, tcx.parent_module(id), |names| {
 +        tcx.hir()
 +            .parent_iter(id)
 +            // Since you can nest functions we need to collect all until we leave
 +            // function scope
 +            .any(|(_id, node)| {
 +                if let Node::Item(item) = node {
 +                    if let ItemKind::Fn(_, _, _) = item.kind {
 +                        // Note that we have sorted the item names in the visitor,
 +                        // so the binary_search gets the same as `contains`, but faster.
 +                        return names.binary_search(&item.ident.name).is_ok();
 +                    }
 +                }
 +                false
 +            })
 +    })
 +}
 +
 +/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
 +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    fn is_cfg_test(attr: &Attribute) -> bool {
 +        if attr.has_name(sym::cfg)
 +            && let Some(items) = attr.meta_item_list()
 +            && let [item] = &*items
 +            && item.has_name(sym::test)
 +        {
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +    tcx.hir()
 +        .parent_iter(id)
 +        .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
 +        .any(is_cfg_test)
 +}
 +
 +/// Checks whether item either has `test` attribute applied, or
 +/// is a module with `test` in its name.
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
 +    is_in_test_function(tcx, item.hir_id())
 +        || matches!(item.kind, ItemKind::Mod(..))
 +            && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
 +}
 +
 +/// Walks the HIR tree from the given expression, up to the node where the value produced by the
 +/// expression is consumed. Calls the function for every node encountered this way until it returns
 +/// `Some`.
 +///
 +/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
 +/// produced by the expression is consumed.
 +pub fn walk_to_expr_usage<'tcx, T>(
 +    cx: &LateContext<'tcx>,
 +    e: &Expr<'tcx>,
 +    mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
 +) -> Option<T> {
 +    let map = cx.tcx.hir();
 +    let mut iter = map.parent_iter(e.hir_id);
 +    let mut child_id = e.hir_id;
 +
 +    while let Some((parent_id, parent)) = iter.next() {
 +        if let Some(x) = f(parent, child_id) {
 +            return Some(x);
 +        }
 +        let parent = match parent {
 +            Node::Expr(e) => e,
 +            Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            Node::Arm(a) if a.body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            _ => return None,
 +        };
 +        match parent.kind {
 +            ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
 +            ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
 +                child_id = id;
 +                iter = map.parent_iter(id);
 +            },
 +            ExprKind::Block(..) => child_id = parent_id,
 +            _ => return None,
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a given span has any comment token
 +/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
 +pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
 +    let Ok(snippet) = sm.span_to_snippet(span) else { return false };
 +    return tokenize(&snippet).any(|token| {
 +        matches!(
 +            token.kind,
 +            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
 +        )
 +    });
 +}
 +
 +/// Return all the comments a given span contains
 +/// Comments are returned wrapped with their relevant delimiters
 +pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String {
 +    let snippet = sm.span_to_snippet(span).unwrap_or_default();
 +    let mut comments_buf: Vec<String> = Vec::new();
 +    let mut index: usize = 0;
 +
 +    for token in tokenize(&snippet) {
 +        let token_range = index..(index + token.len as usize);
 +        index += token.len as usize;
 +        match token.kind {
 +            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => {
 +                if let Some(comment) = snippet.get(token_range) {
 +                    comments_buf.push(comment.to_string());
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    comments_buf.join("\n")
 +}
 +
 +macro_rules! op_utils {
 +    ($($name:ident $assign:ident)*) => {
 +        /// Binary operation traits like `LangItem::Add`
 +        pub static BINOP_TRAITS: &[LangItem] = &[$(LangItem::$name,)*];
 +
 +        /// Operator-Assign traits like `LangItem::AddAssign`
 +        pub static OP_ASSIGN_TRAITS: &[LangItem] = &[$(LangItem::$assign,)*];
 +
 +        /// Converts `BinOpKind::Add` to `(LangItem::Add, LangItem::AddAssign)`, for example
 +        pub fn binop_traits(kind: hir::BinOpKind) -> Option<(LangItem, LangItem)> {
 +            match kind {
 +                $(hir::BinOpKind::$name => Some((LangItem::$name, LangItem::$assign)),)*
 +                _ => None,
 +            }
 +        }
 +    };
 +}
 +
 +op_utils! {
 +    Add    AddAssign
 +    Sub    SubAssign
 +    Mul    MulAssign
 +    Div    DivAssign
 +    Rem    RemAssign
 +    BitXor BitXorAssign
 +    BitAnd BitAndAssign
 +    BitOr  BitOrAssign
 +    Shl    ShlAssign
 +    Shr    ShrAssign
 +}
index 79b19e6fb3eb051eff7a865ac44ea944e4d9cd88,0000000000000000000000000000000000000000..12a512f78a699eb30c92ac178b17663db424d090
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,143 @@@
++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 {
 +        $($name:ident),* $(,)?
 +    })*) => {
 +        $($(
 +        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
 +        )*)*
 +    };
 +}
 +
 +// names may refer to stabilized feature flags or library items
 +msrv_aliases! {
 +    1,65,0 { LET_ELSE }
 +    1,62,0 { BOOL_THEN_SOME }
 +    1,58,0 { FORMAT_ARGS_CAPTURE }
 +    1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
 +    1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
 +    1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
 +    1,50,0 { BOOL_THEN, CLAMP }
 +    1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
 +    1,46,0 { CONST_IF_MATCH }
 +    1,45,0 { STR_STRIP_PREFIX }
 +    1,43,0 { LOG2_10, LOG10_2 }
 +    1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
 +    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
 +    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
 +    1,38,0 { POINTER_CAST, REM_EUCLID }
 +    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
 +    1,36,0 { ITERATOR_COPIED }
 +    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
 +    1,34,0 { TRY_FROM }
 +    1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
 +    1,28,0 { FROM_BOOL }
 +    1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
 +    1,24,0 { IS_ASCII_DIGIT }
 +    1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
 +    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
 +    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();
++        }
++    }
++}
index 6c09c146082ab41e673ea06b142afb334fea833c,0000000000000000000000000000000000000000..6417f0f3c71348bb6761918cb08c7c373e7b9bc6
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,159 @@@
- pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 +//! This module contains paths to types and functions Clippy needs to know
 +//! about.
 +//!
 +//! Whenever possible, please consider diagnostic items over hardcoded paths.
 +//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 +
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
 +    ["rustc_lint_defs", "Applicability", "Unspecified"],
 +    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
 +    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
 +    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
 +];
 +#[cfg(feature = "internal")]
 +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 +pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
 +pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
 +pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
 +pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
 +pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
 +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
 +pub const EXIT: [&str; 3] = ["std", "process", "exit"];
 +pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
 +pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
 +#[cfg(feature = "internal")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal")]
 +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 +pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
 +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +#[cfg(feature = "internal")]
 +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"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
 +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
 +pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
 +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"];
- #[cfg(feature = "internal")]
- pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 +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"];
 +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
 +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
 +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
 +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
 +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
 +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
 +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
 +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
 +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
 +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 +pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 +pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
 +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 +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"];
 +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"];
 +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
 +pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
 +pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
 +pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
 +pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 +pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
 +pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
 +pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
 +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
 +pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
 +pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
 +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 +pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal")]
 +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 +pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
 +pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
 +pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];
index b8c2dd5ab9ea16ee31ac7111304444bd16484774,0000000000000000000000000000000000000000..480e8e55cf39cc8c66b0edf209a7f609c0071ccb
mode 100644,000000..100644
--- /dev/null
@@@ -1,398 -1,0 +1,398 @@@
- pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
 +// This code used to be a part of `rustc` but moved to Clippy as a result of
 +// https://github.com/rust-lang/rust/issues/76618. Because of that, it contains unused code and some
 +// 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::{
 +    Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
 +    Terminator, TerminatorKind,
 +};
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
 +use rustc_semver::RustcVersion;
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +use std::borrow::Cow;
 +
 +type McfResult = Result<(), (Span, Cow<'static, str>)>;
 +
-                 ty::PredicateKind::Clause(ty::Clause::RegionOutlives(_))
-                 | ty::PredicateKind::Clause(ty::Clause::TypeOutlives(_))
++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::Projection(_))
++                ty::PredicateKind::Clause(
++                    ty::Clause::RegionOutlives(_)
++                    | ty::Clause::TypeOutlives(_)
++                    | ty::Clause::Projection(_)
++                    | ty::Clause::Trait(..),
++                )
 +                | ty::PredicateKind::WellFormed(_)
-                 | ty::PredicateKind::Clause(ty::Clause::Trait(..))
 +                | ty::PredicateKind::ConstEvaluatable(..)
 +                | ty::PredicateKind::ConstEquate(..)
-     msrv: Option<RustcVersion>,
 +                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
 +                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::Ambiguous => panic!("ambiguous predicate on function: {predicate:#?}"),
 +            }
 +        }
 +        match predicates.parent {
 +            Some(parent) => current = parent,
 +            None => break,
 +        }
 +    }
 +
 +    for local in &body.local_decls {
 +        check_ty(tcx, local.ty, local.source_info.span)?;
 +    }
 +    // impl trait is gone in MIR, so check the return type manually
 +    check_ty(
 +        tcx,
 +        tcx.fn_sig(def_id).output().skip_binder(),
 +        body.local_decls.iter().next().unwrap().source_info.span,
 +    )?;
 +
 +    for bb in body.basic_blocks.iter() {
 +        check_terminator(tcx, body, bb.terminator(), msrv)?;
 +        for stmt in &bb.statements {
 +            check_statement(tcx, body, def_id, stmt)?;
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
 +    for arg in ty.walk() {
 +        let ty = match arg.unpack() {
 +            GenericArgKind::Type(ty) => ty,
 +
 +            // No constraints on lifetimes or constants, except potentially
 +            // constants' types, but `walk` will get to them as well.
 +            GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue,
 +        };
 +
 +        match ty.kind() {
 +            ty::Ref(_, _, hir::Mutability::Mut) => {
 +                return Err((span, "mutable references in const fn are unstable".into()));
 +            },
 +            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
 +            ty::FnPtr(..) => {
 +                return Err((span, "function pointers in const fn are unstable".into()));
 +            },
 +            ty::Dynamic(preds, _, _) => {
 +                for pred in preds.iter() {
 +                    match pred.skip_binder() {
 +                        ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
 +                            return Err((
 +                                span,
 +                                "trait bounds other than `Sized` \
 +                                 on const fn parameters are unstable"
 +                                    .into(),
 +                            ));
 +                        },
 +                        ty::ExistentialPredicate::Trait(trait_ref) => {
 +                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
 +                                return Err((
 +                                    span,
 +                                    "trait bounds other than `Sized` \
 +                                     on const fn parameters are unstable"
 +                                        .into(),
 +                                ));
 +                            }
 +                        },
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    Ok(())
 +}
 +
 +fn check_rvalue<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    def_id: DefId,
 +    rvalue: &Rvalue<'tcx>,
 +    span: Span,
 +) -> McfResult {
 +    match rvalue {
 +        Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
 +        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
 +            check_place(tcx, *place, span, body)
 +        },
 +        Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body),
 +        Rvalue::Repeat(operand, _)
 +        | Rvalue::Use(operand)
 +        | Rvalue::Cast(
 +            CastKind::PointerFromExposedAddress
 +            | CastKind::IntToInt
 +            | CastKind::FloatToInt
 +            | CastKind::IntToFloat
 +            | CastKind::FloatToFloat
 +            | CastKind::FnPtrToPtr
 +            | CastKind::PtrToPtr
 +            | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
 +            operand,
 +            _,
 +        ) => check_operand(tcx, operand, span, body),
 +        Rvalue::Cast(
 +            CastKind::Pointer(
 +                PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
 +            ),
 +            _,
 +            _,
 +        ) => Err((span, "function pointer casts are not allowed in const fn".into())),
 +        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
 +            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
 +                deref_ty.ty
 +            } else {
 +                // We cannot allow this for now.
 +                return Err((span, "unsizing casts are only allowed for references right now".into()));
 +            };
 +            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
 +            if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
 +                check_operand(tcx, op, span, body)?;
 +                // Casting/coercing things to slices is fine.
 +                Ok(())
 +            } else {
 +                // We just can't allow trait objects until we have figured out trait method calls.
 +                Err((span, "unsizing casts are not allowed in const fn".into()))
 +            }
 +        },
 +        Rvalue::Cast(CastKind::PointerExposeAddress, _, _) => {
 +            Err((span, "casting pointers to ints is unstable in const fn".into()))
 +        },
 +        Rvalue::Cast(CastKind::DynStar, _, _) => {
 +            // FIXME(dyn-star)
 +            unimplemented!()
 +        },
 +        // binops are fine on integers
 +        Rvalue::BinaryOp(_, box (lhs, rhs)) | Rvalue::CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            check_operand(tcx, lhs, span, body)?;
 +            check_operand(tcx, rhs, span, body)?;
 +            let ty = lhs.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() || ty.is_char() {
 +                Ok(())
 +            } else {
 +                Err((
 +                    span,
 +                    "only int, `bool` and `char` operations are stable in const fn".into(),
 +                ))
 +            }
 +        },
 +        Rvalue::NullaryOp(NullOp::SizeOf | NullOp::AlignOf, _) | Rvalue::ShallowInitBox(_, _) => Ok(()),
 +        Rvalue::UnaryOp(_, operand) => {
 +            let ty = operand.ty(body, tcx);
 +            if ty.is_integral() || ty.is_bool() {
 +                check_operand(tcx, operand, span, body)
 +            } else {
 +                Err((span, "only int and `bool` operations are stable in const fn".into()))
 +            }
 +        },
 +        Rvalue::Aggregate(_, operands) => {
 +            for operand in operands {
 +                check_operand(tcx, operand, span, body)?;
 +            }
 +            Ok(())
 +        },
 +    }
 +}
 +
 +fn check_statement<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    def_id: DefId,
 +    statement: &Statement<'tcx>,
 +) -> McfResult {
 +    let span = statement.source_info.span;
 +    match &statement.kind {
 +        StatementKind::Assign(box (place, rval)) => {
 +            check_place(tcx, *place, span, body)?;
 +            check_rvalue(tcx, body, def_id, rval, span)
 +        },
 +
 +        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
 +        // just an assignment
 +        StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
 +            check_place(tcx, **place, span, body)
 +        },
 +
 +        StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body),
 +
 +        StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
 +            rustc_middle::mir::CopyNonOverlapping { dst, src, count },
 +        )) => {
 +            check_operand(tcx, dst, span, body)?;
 +            check_operand(tcx, src, span, body)?;
 +            check_operand(tcx, count, span, body)
 +        },
 +        // These are all NOPs
 +        StatementKind::StorageLive(_)
 +        | StatementKind::StorageDead(_)
 +        | StatementKind::Retag { .. }
 +        | StatementKind::AscribeUserType(..)
 +        | StatementKind::Coverage(..)
 +        | StatementKind::Nop => Ok(()),
 +    }
 +}
 +
 +fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    match operand {
 +        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
 +        Operand::Constant(c) => match c.check_static_ptr(tcx) {
 +            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
 +            None => Ok(()),
 +        },
 +    }
 +}
 +
 +fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
 +    let mut cursor = place.projection.as_ref();
 +    while let [ref proj_base @ .., elem] = *cursor {
 +        cursor = proj_base;
 +        match elem {
 +            ProjectionElem::Field(..) => {
 +                let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
 +                if let Some(def) = base_ty.ty_adt_def() {
 +                    // No union field accesses in `const fn`
 +                    if def.is_union() {
 +                        return Err((span, "accessing union fields is unstable".into()));
 +                    }
 +                }
 +            },
 +            ProjectionElem::ConstantIndex { .. }
 +            | ProjectionElem::OpaqueCast(..)
 +            | ProjectionElem::Downcast(..)
 +            | ProjectionElem::Subslice { .. }
 +            | ProjectionElem::Deref
 +            | ProjectionElem::Index(_) => {},
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +fn check_terminator<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &Body<'tcx>,
 +    terminator: &Terminator<'tcx>,
- fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
++    msrv: &Msrv,
 +) -> McfResult {
 +    let span = terminator.source_info.span;
 +    match &terminator.kind {
 +        TerminatorKind::FalseEdge { .. }
 +        | TerminatorKind::FalseUnwind { .. }
 +        | TerminatorKind::Goto { .. }
 +        | TerminatorKind::Return
 +        | TerminatorKind::Resume
 +        | TerminatorKind::Unreachable => Ok(()),
 +
 +        TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
 +        TerminatorKind::DropAndReplace { place, value, .. } => {
 +            check_place(tcx, *place, span, body)?;
 +            check_operand(tcx, value, span, body)
 +        },
 +
 +        TerminatorKind::SwitchInt {
 +            discr,
 +            switch_ty: _,
 +            targets: _,
 +        } => check_operand(tcx, discr, span, body),
 +
 +        TerminatorKind::Abort => Err((span, "abort is not stable in const fn".into())),
 +        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
 +            Err((span, "const fn generators are unstable".into()))
 +        },
 +
 +        TerminatorKind::Call {
 +            func,
 +            args,
 +            from_hir_call: _,
 +            destination: _,
 +            target: _,
 +            cleanup: _,
 +            fn_span: _,
 +        } => {
 +            let fn_ty = func.ty(body, tcx);
 +            if let ty::FnDef(fn_def_id, _) = *fn_ty.kind() {
 +                if !is_const_fn(tcx, fn_def_id, msrv) {
 +                    return Err((
 +                        span,
 +                        format!(
 +                            "can only call other `const fn` within a `const fn`, \
 +                             but `{func:?}` is not stable as `const fn`",
 +                        )
 +                        .into(),
 +                    ));
 +                }
 +
 +                // HACK: This is to "unstabilize" the `transmute` intrinsic
 +                // within const fns. `transmute` is allowed in all other const contexts.
 +                // This won't really scale to more intrinsics or functions. Let's allow const
 +                // transmutes in const fn before we add more hacks to this.
 +                if tcx.is_intrinsic(fn_def_id) && tcx.item_name(fn_def_id) == sym::transmute {
 +                    return Err((
 +                        span,
 +                        "can only call `transmute` from const items, not `const fn`".into(),
 +                    ));
 +                }
 +
 +                check_operand(tcx, func, span, body)?;
 +
 +                for arg in args {
 +                    check_operand(tcx, arg, span, body)?;
 +                }
 +                Ok(())
 +            } else {
 +                Err((span, "can only call other const fns within const fn".into()))
 +            }
 +        },
 +
 +        TerminatorKind::Assert {
 +            cond,
 +            expected: _,
 +            msg: _,
 +            target: _,
 +            cleanup: _,
 +        } => check_operand(tcx, cond, span, body),
 +
 +        TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
 +    }
 +}
 +
-                 crate::meets_msrv(
-                     msrv,
-                     RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
-                         panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
-                     }),
-                 )
++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 {
 +                // Checking MSRV is manually necessary because `rustc` has no such concept. This entire
 +                // function could be removed if `rustc` provided a MSRV-aware version of `is_const_fn`.
 +                // as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
 +
 +                // HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
 +                // doesn't accept                  the `-dev` version number so we have to strip it
 +                // off.
 +                let short_version = since
 +                    .as_str()
 +                    .split('-')
 +                    .next()
 +                    .expect("rustc_attr::StabilityLevel::Stable::since` is empty");
 +
 +                let since = rustc_span::Symbol::intern(short_version);
 +
-                 msrv.is_none()
++                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.current().is_none()
 +            }
 +        })
 +}
index eacfa91ba556d41d454f2b3a63c5a325ae4311cd,0000000000000000000000000000000000000000..cd5dcfdaca34b64245632331bd7707c36205adcd
mode 100644,000000..100644
--- /dev/null
@@@ -1,516 -1,0 +1,540 @@@
-     snippet_opt(cx, span).map_or_else(
 +//! Utils for extracting, inspecting or transforming source code
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +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};
 +use std::borrow::Cow;
 +
 +/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
 +/// Also takes an `Option<String>` which can be put inside the braces.
 +pub fn expr_block<'a, T: LintContext>(
 +    cx: &T,
 +    expr: &Expr<'_>,
 +    option: Option<String>,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let code = snippet_block(cx, expr.span, default, indent_relative_to);
 +    let string = option.unwrap_or_default();
 +    if expr.span.from_expansion() {
 +        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
 +    } else if let ExprKind::Block(_, _) = expr.kind {
 +        Cow::Owned(format!("{code}{string}"))
 +    } else if string.is_empty() {
 +        Cow::Owned(format!("{{ {code} }}"))
 +    } else {
 +        Cow::Owned(format!("{{\n{code};\n{string}\n}}"))
 +    }
 +}
 +
 +/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
 +/// line.
 +///
 +/// ```rust,ignore
 +///     let x = ();
 +/// //          ^^
 +/// // will be converted to
 +///     let x = ();
 +/// //  ^^^^^^^^^^
 +/// ```
 +pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
 +}
 +
 +fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
 +    let line_span = line_span(cx, span);
 +    snippet_opt(cx, line_span).and_then(|snip| {
 +        snip.find(|c: char| !c.is_whitespace())
 +            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
 +    })
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust
 +///        let x = ();
 +/// //             ^^
 +/// // will be converted to
 +///        let x = ();
 +/// // ^^^^^^^^^^^^^^
 +/// ```
 +fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    let span = original_sp(span, DUMMY_SP);
 +    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
 +    let line_no = source_map_and_line.line;
 +    let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
 +    span.with_lo(line_start)
 +}
 +
 +/// Returns the indentation of the line of a span
 +///
 +/// ```rust,ignore
 +/// let x = ();
 +/// //      ^^ -- will return 0
 +///     let x = ();
 +/// //          ^^ -- will return 4
 +/// ```
 +pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
 +    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 +}
 +
 +/// Gets a snippet of the indentation of the line of a span
 +pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
 +        let len = s.len() - s.trim_start().len();
 +        s.truncate(len);
 +        s
 +    })
 +}
 +
 +// If the snippet is empty, it's an attribute that was inserted during macro
 +// expansion and we want to ignore those, because they could come from external
 +// sources that the user has no control over.
 +// For some reason these attributes don't have any expansion info on them, so
 +// we have to check it this way until there is a better way.
 +pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
 +    if let Some(snippet) = snippet_opt(cx, span) {
 +        if snippet.is_empty() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +/// Returns the position just before rarrow
 +///
 +/// ```rust,ignore
 +/// fn into(self) -> () {}
 +///              ^
 +/// // in case of unformatted code
 +/// fn into2(self)-> () {}
 +///               ^
 +/// fn into3(self)   -> () {}
 +///               ^
 +/// ```
 +pub fn position_before_rarrow(s: &str) -> Option<usize> {
 +    s.rfind("->").map(|rpos| {
 +        let mut rpos = rpos;
 +        let chars: Vec<char> = s.chars().collect();
 +        while rpos > 1 {
 +            if let Some(c) = chars.get(rpos - 1) {
 +                if c.is_whitespace() {
 +                    rpos -= 1;
 +                    continue;
 +                }
 +            }
 +            break;
 +        }
 +        rpos
 +    })
 +}
 +
 +/// Reindent a multiline string with possibility of ignoring the first line.
 +#[expect(clippy::needless_pass_by_value)]
 +pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
 +    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
 +    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
 +    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
 +}
 +
 +fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
 +    let x = s
 +        .lines()
 +        .skip(usize::from(ignore_first))
 +        .filter_map(|l| {
 +            if l.is_empty() {
 +                None
 +            } else {
 +                // ignore empty lines
 +                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
 +            }
 +        })
 +        .min()
 +        .unwrap_or(0);
 +    let indent = indent.unwrap_or(0);
 +    s.lines()
 +        .enumerate()
 +        .map(|(i, l)| {
 +            if (ignore_first && i == 0) || l.is_empty() {
 +                l.to_owned()
 +            } else if x > indent {
 +                l.split_at(x - indent).1.to_owned()
 +            } else {
 +                " ".repeat(indent - x) + l
 +            }
 +        })
 +        .collect::<Vec<String>>()
 +        .join("\n")
 +}
 +
 +/// Converts a span to a code snippet if available, otherwise returns the default.
 +///
 +/// This is useful if you want to provide suggestions for your lint or more generally, if you want
 +/// to convert a given `Span` to a `str`. To create suggestions consider using
 +/// [`snippet_with_applicability`] to ensure that the applicability stays correct.
 +///
 +/// # Example
 +/// ```rust,ignore
 +/// // Given two spans one for `value` and one for the `init` expression.
 +/// let value = Vec::new();
 +/// //  ^^^^^   ^^^^^^^^^^
 +/// //  span1   span2
 +///
 +/// // The snipped call would return the corresponding code snippet
 +/// snippet(cx, span1, "..") // -> "value"
 +/// snippet(cx, span2, "..") // -> "Vec::new()"
 +/// ```
 +pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
 +}
 +
 +/// Same as [`snippet`], but it adapts the applicability level by following rules:
 +///
 +/// - Applicability level `Unspecified` will never be changed.
 +/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 +/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
 +/// `HasPlaceholders`
 +pub fn snippet_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    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;
 +    }
- pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-     cx.sess().source_map().span_to_snippet(span).ok()
++    snippet_opt_sess(sess, span).map_or_else(
 +        || {
 +            if *applicability == Applicability::MachineApplicable {
 +                *applicability = Applicability::HasPlaceholders;
 +            }
 +            Cow::Borrowed(default)
 +        },
 +        From::from,
 +    )
 +}
 +
 +/// Same as `snippet`, but should only be used when it's clear that the input span is
 +/// not a macro argument.
 +pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
 +    snippet(cx, span.source_callsite(), default)
 +}
 +
 +/// Converts a span to a code snippet. Returns `None` if not available.
- pub fn snippet_block_with_applicability<'a, T: LintContext>(
-     cx: &T,
++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.
 +///
 +/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
 +/// things which need to be printed as such.
 +///
 +/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
 +/// resulting snippet of the given span.
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", None)
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///     y;
 +/// }
 +/// ```
 +///
 +/// ```rust,ignore
 +/// snippet_block(cx, block.span, "..", Some(if_expr.span))
 +/// // where, `block` is the block of the if expr
 +///     if x {
 +///         y;
 +///     }
 +/// // will return the snippet
 +/// {
 +///         y;
 +///     } // aligned with `if`
 +/// ```
 +/// Note that the first line of the snippet always has 0 indentation.
 +pub fn snippet_block<'a, T: LintContext>(
 +    cx: &T,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +) -> Cow<'a, str> {
 +    let snip = snippet(cx, span, default);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_block`, but adapts the applicability level by the rules of
 +/// `snippet_with_applicability`.
-     cx: &LateContext<'_>,
++pub fn snippet_block_with_applicability<'a>(
++    cx: &impl LintContext,
 +    span: Span,
 +    default: &'a str,
 +    indent_relative_to: Option<Span>,
 +    applicability: &mut Applicability,
 +) -> Cow<'a, str> {
 +    let snip = snippet_with_applicability(cx, span, default, applicability);
 +    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
 +    reindent_multiline(snip, true, indent)
 +}
 +
 +/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
 +/// will result in the macro call, rather then the expansion, if the span is from a child context.
 +/// If the span is not from a child context, it will be used directly instead.
 +///
 +/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
 +/// would result in `box []`. If given the context of the address of expression, this function will
 +/// correctly get a snippet of `vec![]`.
 +///
 +/// This will also return whether or not the snippet is a macro call.
 +pub fn snippet_with_context<'a>(
-         snippet_with_applicability(cx, span, default, applicability),
++    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,
 +    applicability: &mut Applicability,
 +) -> (Cow<'a, str>, bool) {
 +    let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else(
 +        || {
 +            // The span is from a macro argument, and the outer context is the macro using the argument
 +            if *applicability != Applicability::Unspecified {
 +                *applicability = Applicability::MaybeIncorrect;
 +            }
 +            // TODO: get the argument span.
 +            (span, false)
 +        },
 +        |outer_span| (outer_span, span.ctxt() != outer),
 +    );
 +
 +    (
++        snippet_with_applicability_sess(sess, span, default, applicability),
 +        is_macro_call,
 +    )
 +}
 +
 +/// Walks the span up to the target context, thereby returning the macro call site if the span is
 +/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
 +/// case of the span being in a macro expansion, but the target context is from expanding a macro
 +/// argument.
 +///
 +/// Given the following
 +///
 +/// ```rust,ignore
 +/// macro_rules! m { ($e:expr) => { f($e) }; }
 +/// g(m!(0))
 +/// ```
 +///
 +/// If called with a span of the call to `f` and a context of the call to `g` this will return a
 +/// span containing `m!(0)`. However, if called with a span of the literal `0` this will give a span
 +/// containing `0` as the context is the same as the outer context.
 +///
 +/// This will traverse through multiple macro calls. Given the following:
 +///
 +/// ```rust,ignore
 +/// macro_rules! m { ($e:expr) => { n!($e, 0) }; }
 +/// macro_rules! n { ($e:expr, $f:expr) => { f($e, $f) }; }
 +/// g(m!(0))
 +/// ```
 +///
 +/// If called with a span of the call to `f` and a context of the call to `g` this will return a
 +/// span containing `m!(0)`.
 +pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
 +    let outer_span = hygiene::walk_chain(span, outer);
 +    (outer_span.ctxt() == outer).then_some(outer_span)
 +}
 +
 +/// Removes block comments from the given `Vec` of lines.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// without_block_comments(vec!["/*", "foo", "*/"]);
 +/// // => vec![]
 +///
 +/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
 +/// // => vec!["bar"]
 +/// ```
 +pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
 +    let mut without = vec![];
 +
 +    let mut nest_level = 0;
 +
 +    for line in lines {
 +        if line.contains("/*") {
 +            nest_level += 1;
 +            continue;
 +        } else if line.contains("*/") {
 +            nest_level -= 1;
 +            continue;
 +        }
 +
 +        if nest_level == 0 {
 +            without.push(line);
 +        }
 +    }
 +
 +    without
 +}
 +
 +/// Trims the whitespace from the start and the end of the span.
 +pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
 +    let data = span.data();
 +    let sf: &_ = &sm.lookup_source_file(data.lo);
 +    let Some(src) = sf.src.as_deref() else {
 +        return span;
 +    };
 +    let Some(snip) = &src.get((data.lo - sf.start_pos).to_usize()..(data.hi - sf.start_pos).to_usize()) else {
 +        return span;
 +    };
 +    let trim_start = snip.len() - snip.trim_start().len();
 +    let trim_end = snip.len() - snip.trim_end().len();
 +    SpanData {
 +        lo: data.lo + BytePos::from_usize(trim_start),
 +        hi: data.hi - BytePos::from_usize(trim_end),
 +        ctxt: data.ctxt,
 +        parent: data.parent,
 +    }
 +    .span()
 +}
 +
 +/// Expand a span to include a preceding comma
 +/// ```rust,ignore
 +/// writeln!(o, "")   ->   writeln!(o, "")
 +///             ^^                   ^^^^
 +/// ```
 +pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
 +    let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true);
 +    extended.with_lo(extended.lo() - BytePos(1))
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::{reindent_multiline, without_block_comments};
 +
 +    #[test]
 +    fn test_reindent_multiline_single_line() {
 +        assert_eq!("", reindent_multiline("".into(), false, None));
 +        assert_eq!("...", reindent_multiline("...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
 +        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_block() {
 +        assert_eq!("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +        } else {
 +            z
 +        }".into(), false, None));
 +        assert_eq!("\
 +    if x {
 +    \ty
 +    } else {
 +    \tz
 +    }", reindent_multiline("    if x {
 +        \ty
 +        } else {
 +        \tz
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_empty_line() {
 +        assert_eq!("\
 +    if x {
 +        y
 +
 +    } else {
 +        z
 +    }", reindent_multiline("    if x {
 +            y
 +
 +        } else {
 +            z
 +        }".into(), false, None));
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_reindent_multiline_lines_deeper() {
 +        assert_eq!("\
 +        if x {
 +            y
 +        } else {
 +            z
 +        }", reindent_multiline("\
 +    if x {
 +        y
 +    } else {
 +        z
 +    }".into(), true, Some(8)));
 +    }
 +
 +    #[test]
 +    fn test_without_block_comments_lines_without_block_comments() {
 +        let result = without_block_comments(vec!["/*", "", "*/"]);
 +        println!("result: {result:?}");
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
 +        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
 +
 +        let result = without_block_comments(vec!["/* rust", "", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* one-line comment */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
 +        assert!(result.is_empty());
 +
 +        let result = without_block_comments(vec!["foo", "bar", "baz"]);
 +        assert_eq!(result, vec!["foo", "bar", "baz"]);
 +    }
 +}
index 3cacdb493772138594a03114722fc2b10c9c74ff,0000000000000000000000000000000000000000..b66604f33db1799d49b8fd5fb9b3ccfaa40fa1f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,1110 -1,0 +1,1117 @@@
-     pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
 +//! Contains utility functions to generate suggestions.
 +#![deny(clippy::missing_docs_in_private_items)]
 +
 +use crate::source::{
 +    snippet, snippet_opt, snippet_with_applicability, snippet_with_context, snippet_with_macro_callsite,
 +};
 +use crate::ty::expr_sig;
 +use crate::{get_parent_expr_for_hir, higher};
 +use rustc_ast::util::parser::AssocOp;
 +use rustc_ast::{ast, token};
 +use rustc_ast_pretty::pprust::token_kind_to_string;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::{Closure, ExprKind, HirId, MutTy, TyKind};
 +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{EarlyContext, LateContext, LintContext};
 +use rustc_middle::hir::place::ProjectionKind;
 +use rustc_middle::mir::{FakeReadCause, Mutability};
 +use rustc_middle::ty;
 +use rustc_span::source_map::{BytePos, CharPos, Pos, Span, SyntaxContext};
 +use std::borrow::Cow;
 +use std::fmt::{Display, Write as _};
 +use std::ops::{Add, Neg, Not, Sub};
 +
 +/// A helper type to build suggestion correctly handling parentheses.
 +#[derive(Clone, Debug, PartialEq)]
 +pub enum Sugg<'a> {
 +    /// An expression that never needs parentheses such as `1337` or `[0; 42]`.
 +    NonParen(Cow<'a, str>),
 +    /// An expression that does not fit in other variants.
 +    MaybeParen(Cow<'a, str>),
 +    /// A binary operator expression, including `as`-casts and explicit type
 +    /// coercion.
 +    BinOp(AssocOp, Cow<'a, str>, Cow<'a, str>),
 +}
 +
 +/// Literal constant `0`, for convenience.
 +pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
 +/// Literal constant `1`, for convenience.
 +pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
 +/// a constant represents an empty string, for convenience.
 +pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));
 +
 +impl Display for Sugg<'_> {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        match *self {
 +            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) => s.fmt(f),
 +            Sugg::BinOp(op, ref lhs, ref rhs) => binop_to_string(op, lhs, rhs).fmt(f),
 +        }
 +    }
 +}
 +
 +#[expect(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
 +impl<'a> Sugg<'a> {
 +    /// Prepare a suggestion from an expression.
 +    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
 +        let get_snippet = |span| snippet(cx, span, "");
 +        snippet_opt(cx, expr.span).map(|_| Self::hir_from_snippet(expr, get_snippet))
 +    }
 +
 +    /// Convenience function around `hir_opt` for suggestions with a default
 +    /// text.
 +    pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
 +        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
 +    }
 +
 +    /// Same as `hir`, but it adapts the applicability level by following rules:
 +    ///
 +    /// - Applicability level `Unspecified` will never be changed.
 +    /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 +    /// - If the default value is used and the applicability level is `MachineApplicable`, change it
 +    ///   to
 +    /// `HasPlaceholders`
 +    pub fn hir_with_applicability(
 +        cx: &LateContext<'_>,
 +        expr: &hir::Expr<'_>,
 +        default: &'a str,
 +        applicability: &mut Applicability,
 +    ) -> Self {
 +        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
 +            *applicability = Applicability::MaybeIncorrect;
 +        }
 +        Self::hir_opt(cx, expr).unwrap_or_else(|| {
 +            if *applicability == Applicability::MachineApplicable {
 +                *applicability = Applicability::HasPlaceholders;
 +            }
 +            Sugg::NonParen(Cow::Borrowed(default))
 +        })
 +    }
 +
 +    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
 +    pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
 +        let get_snippet = |span| snippet_with_macro_callsite(cx, span, default);
 +        Self::hir_from_snippet(expr, get_snippet)
 +    }
 +
 +    /// Same as `hir`, but first walks the span up to the given context. This will result in the
 +    /// macro call, rather then the expansion, if the span is from a child context. If the span is
 +    /// not from a child context, it will be used directly instead.
 +    ///
 +    /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR
 +    /// node would result in `box []`. If given the context of the address of expression, this
 +    /// function will correctly get a snippet of `vec![]`.
 +    pub fn hir_with_context(
 +        cx: &LateContext<'_>,
 +        expr: &hir::Expr<'_>,
 +        ctxt: SyntaxContext,
 +        default: &'a str,
 +        applicability: &mut Applicability,
 +    ) -> Self {
 +        if expr.span.ctxt() == ctxt {
 +            Self::hir_from_snippet(expr, |span| snippet(cx, span, default))
 +        } else {
 +            let (snip, _) = snippet_with_context(cx, expr.span, ctxt, default, applicability);
 +            Sugg::NonParen(snip)
 +        }
 +    }
 +
 +    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
 +    /// function variants of `Sugg`, since these use different snippet functions.
 +    fn hir_from_snippet(expr: &hir::Expr<'_>, get_snippet: impl Fn(Span) -> Cow<'a, str>) -> Self {
 +        if let Some(range) = higher::Range::hir(expr) {
 +            let op = match range.limits {
 +                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
 +                ast::RangeLimits::Closed => AssocOp::DotDotEq,
 +            };
 +            let start = range.start.map_or("".into(), |expr| get_snippet(expr.span));
 +            let end = range.end.map_or("".into(), |expr| get_snippet(expr.span));
 +
 +            return Sugg::BinOp(op, start, end);
 +        }
 +
 +        match expr.kind {
 +            hir::ExprKind::AddrOf(..)
 +            | hir::ExprKind::Box(..)
 +            | hir::ExprKind::If(..)
 +            | hir::ExprKind::Let(..)
 +            | hir::ExprKind::Closure { .. }
 +            | hir::ExprKind::Unary(..)
 +            | hir::ExprKind::Match(..) => Sugg::MaybeParen(get_snippet(expr.span)),
 +            hir::ExprKind::Continue(..)
 +            | hir::ExprKind::Yield(..)
 +            | hir::ExprKind::Array(..)
 +            | hir::ExprKind::Block(..)
 +            | hir::ExprKind::Break(..)
 +            | hir::ExprKind::Call(..)
 +            | hir::ExprKind::Field(..)
 +            | hir::ExprKind::Index(..)
 +            | hir::ExprKind::InlineAsm(..)
 +            | hir::ExprKind::ConstBlock(..)
 +            | hir::ExprKind::Lit(..)
 +            | hir::ExprKind::Loop(..)
 +            | hir::ExprKind::MethodCall(..)
 +            | hir::ExprKind::Path(..)
 +            | hir::ExprKind::Repeat(..)
 +            | hir::ExprKind::Ret(..)
 +            | hir::ExprKind::Struct(..)
 +            | hir::ExprKind::Tup(..)
 +            | hir::ExprKind::Err => Sugg::NonParen(get_snippet(expr.span)),
 +            hir::ExprKind::DropTemps(inner) => Self::hir_from_snippet(inner, get_snippet),
 +            hir::ExprKind::Assign(lhs, rhs, _) => {
 +                Sugg::BinOp(AssocOp::Assign, get_snippet(lhs.span), get_snippet(rhs.span))
 +            },
 +            hir::ExprKind::AssignOp(op, lhs, rhs) => {
 +                Sugg::BinOp(hirbinop2assignop(op), get_snippet(lhs.span), get_snippet(rhs.span))
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node.into()),
 +                get_snippet(lhs.span),
 +                get_snippet(rhs.span),
 +            ),
 +            hir::ExprKind::Cast(lhs, ty) => Sugg::BinOp(AssocOp::As, get_snippet(lhs.span), get_snippet(ty.span)),
 +            hir::ExprKind::Type(lhs, ty) => Sugg::BinOp(AssocOp::Colon, get_snippet(lhs.span), get_snippet(ty.span)),
 +        }
 +    }
 +
 +    /// Prepare a suggestion from an expression.
-         let snippet_without_expansion = |cx, span: Span, default| {
-             if span.from_expansion() {
-                 snippet_with_macro_callsite(cx, span, default)
-             } else {
-                 snippet(cx, span, default)
-             }
-         };
++    pub fn ast(
++        cx: &EarlyContext<'_>,
++        expr: &ast::Expr,
++        default: &'a str,
++        ctxt: SyntaxContext,
++        app: &mut Applicability,
++    ) -> Self {
 +        use rustc_ast::ast::RangeLimits;
 +
-             | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.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::Err => Sugg::NonParen(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(..)
 +            | ast::ExprKind::Call(..)
 +            | ast::ExprKind::Continue(..)
 +            | ast::ExprKind::Yield(..)
 +            | ast::ExprKind::Field(..)
 +            | ast::ExprKind::ForLoop(..)
 +            | ast::ExprKind::Index(..)
 +            | ast::ExprKind::InlineAsm(..)
 +            | ast::ExprKind::ConstBlock(..)
 +            | ast::ExprKind::Lit(..)
 +            | ast::ExprKind::IncludedBytes(..)
 +            | ast::ExprKind::Loop(..)
 +            | ast::ExprKind::MacCall(..)
 +            | ast::ExprKind::MethodCall(..)
 +            | ast::ExprKind::Paren(..)
 +            | ast::ExprKind::Underscore
 +            | ast::ExprKind::Path(..)
 +            | ast::ExprKind::Repeat(..)
 +            | ast::ExprKind::Ret(..)
 +            | ast::ExprKind::Yeet(..)
 +            | ast::ExprKind::Struct(..)
 +            | ast::ExprKind::Try(..)
 +            | ast::ExprKind::TryBlock(..)
 +            | ast::ExprKind::Tup(..)
 +            | ast::ExprKind::Array(..)
 +            | ast::ExprKind::While(..)
 +            | ast::ExprKind::Await(..)
-                 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)),
++            | 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,
-                 snippet_without_expansion(cx, lhs.span, default),
-                 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, ty.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_with_context(cx, lhs.span, ctxt, default, app).0,
++                snippet_with_context(cx, ty.span, ctxt, default, app).0,
 +            ),
 +        }
 +    }
 +
 +    /// Convenience method to create the `<lhs> && <rhs>` suggestion.
 +    pub fn and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::And, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> & <rhs>` suggestion.
 +    pub fn bit_and(self, rhs: &Self) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::BitAnd, &self, rhs)
 +    }
 +
 +    /// Convenience method to create the `<lhs> as <rhs>` suggestion.
 +    pub fn as_ty<R: Display>(self, rhs: R) -> Sugg<'static> {
 +        make_assoc(AssocOp::As, &self, &Sugg::NonParen(rhs.to_string().into()))
 +    }
 +
 +    /// Convenience method to create the `&<expr>` suggestion.
 +    pub fn addr(self) -> Sugg<'static> {
 +        make_unop("&", self)
 +    }
 +
 +    /// Convenience method to create the `&mut <expr>` suggestion.
 +    pub fn mut_addr(self) -> Sugg<'static> {
 +        make_unop("&mut ", self)
 +    }
 +
 +    /// Convenience method to create the `*<expr>` suggestion.
 +    pub fn deref(self) -> Sugg<'static> {
 +        make_unop("*", self)
 +    }
 +
 +    /// Convenience method to create the `&*<expr>` suggestion. Currently this
 +    /// is needed because `sugg.deref().addr()` produces an unnecessary set of
 +    /// parentheses around the deref.
 +    pub fn addr_deref(self) -> Sugg<'static> {
 +        make_unop("&*", self)
 +    }
 +
 +    /// Convenience method to create the `&mut *<expr>` suggestion. Currently
 +    /// this is needed because `sugg.deref().mut_addr()` produces an unnecessary
 +    /// set of parentheses around the deref.
 +    pub fn mut_addr_deref(self) -> Sugg<'static> {
 +        make_unop("&mut *", self)
 +    }
 +
 +    /// Convenience method to transform suggestion into a return call
 +    pub fn make_return(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("return {self}")))
 +    }
 +
 +    /// Convenience method to transform suggestion into a block
 +    /// where the suggestion is a trailing expression
 +    pub fn blockify(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("{{ {self} }}")))
 +    }
 +
 +    /// Convenience method to prefix the expression with the `async` keyword.
 +    /// Can be used after `blockify` to create an async block.
 +    pub fn asyncify(self) -> Sugg<'static> {
 +        Sugg::NonParen(Cow::Owned(format!("async {self}")))
 +    }
 +
 +    /// Convenience method to create the `<lhs>..<rhs>` or `<lhs>...<rhs>`
 +    /// suggestion.
 +    pub fn range(self, end: &Self, limit: ast::RangeLimits) -> Sugg<'static> {
 +        match limit {
 +            ast::RangeLimits::HalfOpen => make_assoc(AssocOp::DotDot, &self, end),
 +            ast::RangeLimits::Closed => make_assoc(AssocOp::DotDotEq, &self, end),
 +        }
 +    }
 +
 +    /// Adds parentheses to any expression that might need them. Suitable to the
 +    /// `self` argument of a method call
 +    /// (e.g., to build `bar.foo()` or `(1 + 2).foo()`).
 +    #[must_use]
 +    pub fn maybe_par(self) -> Self {
 +        match self {
 +            Sugg::NonParen(..) => self,
 +            // `(x)` and `(x).y()` both don't need additional parens.
 +            Sugg::MaybeParen(sugg) => {
 +                if has_enclosing_paren(&sugg) {
 +                    Sugg::MaybeParen(sugg)
 +                } else {
 +                    Sugg::NonParen(format!("({sugg})").into())
 +                }
 +            },
 +            Sugg::BinOp(op, lhs, rhs) => {
 +                let sugg = binop_to_string(op, &lhs, &rhs);
 +                Sugg::NonParen(format!("({sugg})").into())
 +            },
 +        }
 +    }
 +}
 +
 +/// Generates a string from the operator and both sides.
 +fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
 +    match op {
 +        AssocOp::Add
 +        | AssocOp::Subtract
 +        | AssocOp::Multiply
 +        | AssocOp::Divide
 +        | AssocOp::Modulus
 +        | AssocOp::LAnd
 +        | AssocOp::LOr
 +        | AssocOp::BitXor
 +        | AssocOp::BitAnd
 +        | AssocOp::BitOr
 +        | AssocOp::ShiftLeft
 +        | AssocOp::ShiftRight
 +        | AssocOp::Equal
 +        | AssocOp::Less
 +        | AssocOp::LessEqual
 +        | AssocOp::NotEqual
 +        | AssocOp::Greater
 +        | AssocOp::GreaterEqual => {
 +            format!(
 +                "{lhs} {} {rhs}",
 +                op.to_ast_binop().expect("Those are AST ops").to_string()
 +            )
 +        },
 +        AssocOp::Assign => format!("{lhs} = {rhs}"),
 +        AssocOp::AssignOp(op) => {
 +            format!("{lhs} {}= {rhs}", token_kind_to_string(&token::BinOp(op)))
 +        },
 +        AssocOp::As => format!("{lhs} as {rhs}"),
 +        AssocOp::DotDot => format!("{lhs}..{rhs}"),
 +        AssocOp::DotDotEq => format!("{lhs}..={rhs}"),
 +        AssocOp::Colon => format!("{lhs}: {rhs}"),
 +    }
 +}
 +
 +/// Return `true` if `sugg` is enclosed in parenthesis.
 +pub fn has_enclosing_paren(sugg: impl AsRef<str>) -> bool {
 +    let mut chars = sugg.as_ref().chars();
 +    if chars.next() == Some('(') {
 +        let mut depth = 1;
 +        for c in &mut chars {
 +            if c == '(' {
 +                depth += 1;
 +            } else if c == ')' {
 +                depth -= 1;
 +            }
 +            if depth == 0 {
 +                break;
 +            }
 +        }
 +        chars.next().is_none()
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Copied from the rust standard library, and then edited
 +macro_rules! forward_binop_impls_to_ref {
 +    (impl $imp:ident, $method:ident for $t:ty, type Output = $o:ty) => {
 +        impl $imp<$t> for &$t {
 +            type Output = $o;
 +
 +            fn $method(self, other: $t) -> $o {
 +                $imp::$method(self, &other)
 +            }
 +        }
 +
 +        impl $imp<&$t> for $t {
 +            type Output = $o;
 +
 +            fn $method(self, other: &$t) -> $o {
 +                $imp::$method(&self, other)
 +            }
 +        }
 +
 +        impl $imp for $t {
 +            type Output = $o;
 +
 +            fn $method(self, other: $t) -> $o {
 +                $imp::$method(&self, &other)
 +            }
 +        }
 +    };
 +}
 +
 +impl Add for &Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn add(self, rhs: &Sugg<'_>) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::Add, self, rhs)
 +    }
 +}
 +
 +impl Sub for &Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn sub(self, rhs: &Sugg<'_>) -> Sugg<'static> {
 +        make_binop(ast::BinOpKind::Sub, self, rhs)
 +    }
 +}
 +
 +forward_binop_impls_to_ref!(impl Add, add for Sugg<'_>, type Output = Sugg<'static>);
 +forward_binop_impls_to_ref!(impl Sub, sub for Sugg<'_>, type Output = Sugg<'static>);
 +
 +impl Neg for Sugg<'_> {
 +    type Output = Sugg<'static>;
 +    fn neg(self) -> Sugg<'static> {
 +        make_unop("-", self)
 +    }
 +}
 +
 +impl<'a> Not for Sugg<'a> {
 +    type Output = Sugg<'a>;
 +    fn not(self) -> Sugg<'a> {
 +        use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual};
 +
 +        if let Sugg::BinOp(op, lhs, rhs) = self {
 +            let to_op = match op {
 +                Equal => NotEqual,
 +                NotEqual => Equal,
 +                Less => GreaterEqual,
 +                GreaterEqual => Less,
 +                Greater => LessEqual,
 +                LessEqual => Greater,
 +                _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
 +            };
 +            Sugg::BinOp(to_op, lhs, rhs)
 +        } else {
 +            make_unop("!", self)
 +        }
 +    }
 +}
 +
 +/// Helper type to display either `foo` or `(foo)`.
 +struct ParenHelper<T> {
 +    /// `true` if parentheses are needed.
 +    paren: bool,
 +    /// The main thing to display.
 +    wrapped: T,
 +}
 +
 +impl<T> ParenHelper<T> {
 +    /// Builds a `ParenHelper`.
 +    fn new(paren: bool, wrapped: T) -> Self {
 +        Self { paren, wrapped }
 +    }
 +}
 +
 +impl<T: Display> Display for ParenHelper<T> {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
 +        if self.paren {
 +            write!(f, "({})", self.wrapped)
 +        } else {
 +            self.wrapped.fmt(f)
 +        }
 +    }
 +}
 +
 +/// Builds the string for `<op><expr>` adding parenthesis when necessary.
 +///
 +/// For convenience, the operator is taken as a string because all unary
 +/// operators have the same
 +/// precedence.
 +pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
 +    Sugg::MaybeParen(format!("{op}{}", expr.maybe_par()).into())
 +}
 +
 +/// Builds the string for `<lhs> <op> <rhs>` adding parenthesis when necessary.
 +///
 +/// Precedence of shift operator relative to other arithmetic operation is
 +/// often confusing so
 +/// parenthesis will always be added for a mix of these.
 +pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
 +    /// Returns `true` if the operator is a shift operator `<<` or `>>`.
 +    fn is_shift(op: AssocOp) -> bool {
 +        matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight)
 +    }
 +
 +    /// Returns `true` if the operator is an arithmetic operator
 +    /// (i.e., `+`, `-`, `*`, `/`, `%`).
 +    fn is_arith(op: AssocOp) -> bool {
 +        matches!(
 +            op,
 +            AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus
 +        )
 +    }
 +
 +    /// Returns `true` if the operator `op` needs parenthesis with the operator
 +    /// `other` in the direction `dir`.
 +    fn needs_paren(op: AssocOp, other: AssocOp, dir: Associativity) -> bool {
 +        other.precedence() < op.precedence()
 +            || (other.precedence() == op.precedence()
 +                && ((op != other && associativity(op) != dir)
 +                    || (op == other && associativity(op) != Associativity::Both)))
 +            || is_shift(op) && is_arith(other)
 +            || is_shift(other) && is_arith(op)
 +    }
 +
 +    let lhs_paren = if let Sugg::BinOp(lop, _, _) = *lhs {
 +        needs_paren(op, lop, Associativity::Left)
 +    } else {
 +        false
 +    };
 +
 +    let rhs_paren = if let Sugg::BinOp(rop, _, _) = *rhs {
 +        needs_paren(op, rop, Associativity::Right)
 +    } else {
 +        false
 +    };
 +
 +    let lhs = ParenHelper::new(lhs_paren, lhs).to_string();
 +    let rhs = ParenHelper::new(rhs_paren, rhs).to_string();
 +    Sugg::BinOp(op, lhs.into(), rhs.into())
 +}
 +
 +/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`.
 +pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
 +    make_assoc(AssocOp::from_ast_binop(op), lhs, rhs)
 +}
 +
 +#[derive(PartialEq, Eq, Clone, Copy)]
 +/// Operator associativity.
 +enum Associativity {
 +    /// The operator is both left-associative and right-associative.
 +    Both,
 +    /// The operator is left-associative.
 +    Left,
 +    /// The operator is not associative.
 +    None,
 +    /// The operator is right-associative.
 +    Right,
 +}
 +
 +/// Returns the associativity/fixity of an operator. The difference with
 +/// `AssocOp::fixity` is that an operator can be both left and right associative
 +/// (such as `+`: `a + b + c == (a + b) + c == a + (b + c)`.
 +///
 +/// Chained `as` and explicit `:` type coercion never need inner parenthesis so
 +/// they are considered
 +/// associative.
 +#[must_use]
 +fn associativity(op: AssocOp) -> Associativity {
 +    use rustc_ast::util::parser::AssocOp::{
 +        Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Colon, Divide, DotDot, DotDotEq, Equal, Greater,
 +        GreaterEqual, LAnd, LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract,
 +    };
 +
 +    match op {
 +        Assign | AssignOp(_) => Associativity::Right,
 +        Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As | Colon => Associativity::Both,
 +        Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight
 +        | Subtract => Associativity::Left,
 +        DotDot | DotDotEq => Associativity::None,
 +    }
 +}
 +
 +/// Converts a `hir::BinOp` to the corresponding assigning binary operator.
 +fn hirbinop2assignop(op: hir::BinOp) -> AssocOp {
 +    use rustc_ast::token::BinOpToken::{And, Caret, Minus, Or, Percent, Plus, Shl, Shr, Slash, Star};
 +
 +    AssocOp::AssignOp(match op.node {
 +        hir::BinOpKind::Add => Plus,
 +        hir::BinOpKind::BitAnd => And,
 +        hir::BinOpKind::BitOr => Or,
 +        hir::BinOpKind::BitXor => Caret,
 +        hir::BinOpKind::Div => Slash,
 +        hir::BinOpKind::Mul => Star,
 +        hir::BinOpKind::Rem => Percent,
 +        hir::BinOpKind::Shl => Shl,
 +        hir::BinOpKind::Shr => Shr,
 +        hir::BinOpKind::Sub => Minus,
 +
 +        hir::BinOpKind::And
 +        | hir::BinOpKind::Eq
 +        | hir::BinOpKind::Ge
 +        | hir::BinOpKind::Gt
 +        | hir::BinOpKind::Le
 +        | hir::BinOpKind::Lt
 +        | hir::BinOpKind::Ne
 +        | hir::BinOpKind::Or => panic!("This operator does not exist"),
 +    })
 +}
 +
 +/// Converts an `ast::BinOp` to the corresponding assigning binary operator.
 +fn astbinop2assignop(op: ast::BinOp) -> AssocOp {
 +    use rustc_ast::ast::BinOpKind::{
 +        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
 +    };
 +    use rustc_ast::token::BinOpToken;
 +
 +    AssocOp::AssignOp(match op.node {
 +        Add => BinOpToken::Plus,
 +        BitAnd => BinOpToken::And,
 +        BitOr => BinOpToken::Or,
 +        BitXor => BinOpToken::Caret,
 +        Div => BinOpToken::Slash,
 +        Mul => BinOpToken::Star,
 +        Rem => BinOpToken::Percent,
 +        Shl => BinOpToken::Shl,
 +        Shr => BinOpToken::Shr,
 +        Sub => BinOpToken::Minus,
 +        And | Eq | Ge | Gt | Le | Lt | Ne | Or => panic!("This operator does not exist"),
 +    })
 +}
 +
 +/// Returns the indentation before `span` if there are nothing but `[ \t]`
 +/// before it on its line.
 +fn indentation<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    let lo = cx.sess().source_map().lookup_char_pos(span.lo());
 +    lo.file
 +        .get_line(lo.line - 1 /* line numbers in `Loc` are 1-based */)
 +        .and_then(|line| {
 +            if let Some((pos, _)) = line.char_indices().find(|&(_, c)| c != ' ' && c != '\t') {
 +                // We can mix char and byte positions here because we only consider `[ \t]`.
 +                if lo.col == CharPos(pos) {
 +                    Some(line[..pos].into())
 +                } else {
 +                    None
 +                }
 +            } else {
 +                None
 +            }
 +        })
 +}
 +
 +/// Convenience extension trait for `Diagnostic`.
 +pub trait DiagnosticExt<T: LintContext> {
 +    /// Suggests to add an attribute to an item.
 +    ///
 +    /// Correctly handles indentation of the attribute and item.
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_item_with_attr(cx, item, "#[derive(Default)]");
 +    /// ```
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    );
 +
 +    /// Suggest to add an item before another.
 +    ///
 +    /// The item should not be indented (except for inner indentation).
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_prepend_item(cx, item,
 +    /// "fn foo() {
 +    ///     bar();
 +    /// }");
 +    /// ```
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability);
 +
 +    /// Suggest to completely remove an item.
 +    ///
 +    /// This will remove an item and all following whitespace until the next non-whitespace
 +    /// character. This should work correctly if item is on the same indentation level as the
 +    /// following item.
 +    ///
 +    /// # Example
 +    ///
 +    /// ```rust,ignore
 +    /// diag.suggest_remove_item(cx, item, "remove this")
 +    /// ```
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability);
 +}
 +
 +impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
 +    fn suggest_item_with_attr<D: Display + ?Sized>(
 +        &mut self,
 +        cx: &T,
 +        item: Span,
 +        msg: &str,
 +        attr: &D,
 +        applicability: Applicability,
 +    ) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability);
 +        }
 +    }
 +
 +    fn suggest_prepend_item(&mut self, cx: &T, item: Span, msg: &str, new_item: &str, applicability: Applicability) {
 +        if let Some(indent) = indentation(cx, item) {
 +            let span = item.with_hi(item.lo());
 +
 +            let mut first = true;
 +            let new_item = new_item
 +                .lines()
 +                .map(|l| {
 +                    if first {
 +                        first = false;
 +                        format!("{l}\n")
 +                    } else {
 +                        format!("{indent}{l}\n")
 +                    }
 +                })
 +                .collect::<String>();
 +
 +            self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability);
 +        }
 +    }
 +
 +    fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability) {
 +        let mut remove_span = item;
 +        let fmpos = cx.sess().source_map().lookup_byte_offset(remove_span.hi());
 +
 +        if let Some(ref src) = fmpos.sf.src {
 +            let non_whitespace_offset = src[fmpos.pos.to_usize()..].find(|c| c != ' ' && c != '\t' && c != '\n');
 +
 +            if let Some(non_whitespace_offset) = non_whitespace_offset {
 +                remove_span = remove_span
 +                    .with_hi(remove_span.hi() + BytePos(non_whitespace_offset.try_into().expect("offset too large")));
 +            }
 +        }
 +
 +        self.span_suggestion(remove_span, msg, "", applicability);
 +    }
 +}
 +
 +/// Suggestion results for handling closure
 +/// args dereferencing and borrowing
 +pub struct DerefClosure {
 +    /// confidence on the built suggestion
 +    pub applicability: Applicability,
 +    /// gradually built suggestion
 +    pub suggestion: String,
 +}
 +
 +/// Build suggestion gradually by handling closure arg specific usages,
 +/// such as explicit deref and borrowing cases.
 +/// Returns `None` if no such use cases have been triggered in closure body
 +///
 +/// note: this only works on single line immutable closures with exactly one input parameter.
 +pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option<DerefClosure> {
 +    if let hir::ExprKind::Closure(&Closure { fn_decl, body, .. }) = closure.kind {
 +        let closure_body = cx.tcx.hir().body(body);
 +        // is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
 +        // a type annotation is present if param `kind` is different from `TyKind::Infer`
 +        let closure_arg_is_type_annotated_double_ref = if let TyKind::Rptr(_, MutTy { ty, .. }) = fn_decl.inputs[0].kind
 +        {
 +            matches!(ty.kind, TyKind::Rptr(_, MutTy { .. }))
 +        } else {
 +            false
 +        };
 +
 +        let mut visitor = DerefDelegate {
 +            cx,
 +            closure_span: closure.span,
 +            closure_arg_is_type_annotated_double_ref,
 +            next_pos: closure.span.lo(),
 +            suggestion_start: String::new(),
 +            applicability: Applicability::MachineApplicable,
 +        };
 +
 +        let fn_def_id = cx.tcx.hir().local_def_id(closure.hir_id);
 +        let infcx = cx.tcx.infer_ctxt().build();
 +        ExprUseVisitor::new(&mut visitor, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
 +            .consume_body(closure_body);
 +
 +        if !visitor.suggestion_start.is_empty() {
 +            return Some(DerefClosure {
 +                applicability: visitor.applicability,
 +                suggestion: visitor.finish(),
 +            });
 +        }
 +    }
 +    None
 +}
 +
 +/// Visitor struct used for tracking down
 +/// dereferencing and borrowing of closure's args
 +struct DerefDelegate<'a, 'tcx> {
 +    /// The late context of the lint
 +    cx: &'a LateContext<'tcx>,
 +    /// The span of the input closure to adapt
 +    closure_span: Span,
 +    /// Indicates if the arg of the closure is a type annotated double reference
 +    closure_arg_is_type_annotated_double_ref: bool,
 +    /// last position of the span to gradually build the suggestion
 +    next_pos: BytePos,
 +    /// starting part of the gradually built suggestion
 +    suggestion_start: String,
 +    /// confidence on the built suggestion
 +    applicability: Applicability,
 +}
 +
 +impl<'tcx> DerefDelegate<'_, 'tcx> {
 +    /// build final suggestion:
 +    /// - create the ending part of suggestion
 +    /// - concatenate starting and ending parts
 +    /// - potentially remove needless borrowing
 +    pub fn finish(&mut self) -> String {
 +        let end_span = Span::new(self.next_pos, self.closure_span.hi(), self.closure_span.ctxt(), None);
 +        let end_snip = snippet_with_applicability(self.cx, end_span, "..", &mut self.applicability);
 +        let sugg = format!("{}{end_snip}", self.suggestion_start);
 +        if self.closure_arg_is_type_annotated_double_ref {
 +            sugg.replacen('&', "", 1)
 +        } else {
 +            sugg
 +        }
 +    }
 +
 +    /// indicates whether the function from `parent_expr` takes its args by double reference
 +    fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
 +        let ty = match parent_expr.kind {
 +            ExprKind::MethodCall(_, receiver, call_args, _) => {
 +                if let Some(sig) = self
 +                    .cx
 +                    .typeck_results()
 +                    .type_dependent_def_id(parent_expr.hir_id)
 +                    .map(|did| self.cx.tcx.fn_sig(did).skip_binder())
 +                {
 +                    std::iter::once(receiver)
 +                        .chain(call_args.iter())
 +                        .position(|arg| arg.hir_id == cmt_hir_id)
 +                        .map(|i| sig.inputs()[i])
 +                } else {
 +                    return false;
 +                }
 +            },
 +            ExprKind::Call(func, call_args) => {
 +                if let Some(sig) = expr_sig(self.cx, func) {
 +                    call_args
 +                        .iter()
 +                        .position(|arg| arg.hir_id == cmt_hir_id)
 +                        .and_then(|i| sig.input(i))
 +                        .map(ty::Binder::skip_binder)
 +                } else {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        };
 +
 +        ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
 +    }
 +}
 +
 +impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
 +        if let PlaceBase::Local(id) = cmt.place.base {
 +            let map = self.cx.tcx.hir();
 +            let span = map.span(cmt.hir_id);
 +            let start_span = Span::new(self.next_pos, span.lo(), span.ctxt(), None);
 +            let mut start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
 +
 +            // identifier referring to the variable currently triggered (i.e.: `fp`)
 +            let ident_str = map.name(id).to_string();
 +            // full identifier that includes projection (i.e.: `fp.field`)
 +            let ident_str_with_proj = snippet(self.cx, span, "..").to_string();
 +
 +            if cmt.place.projections.is_empty() {
 +                // handle item without any projection, that needs an explicit borrowing
 +                // i.e.: suggest `&x` instead of `x`
 +                let _ = write!(self.suggestion_start, "{start_snip}&{ident_str}");
 +            } else {
 +                // cases where a parent `Call` or `MethodCall` is using the item
 +                // i.e.: suggest `.contains(&x)` for `.find(|x| [1, 2, 3].contains(x)).is_none()`
 +                //
 +                // Note about method calls:
 +                // - compiler automatically dereference references if the target type is a reference (works also for
 +                //   function call)
 +                // - `self` arguments in the case of `x.is_something()` are also automatically (de)referenced, and
 +                //   no projection should be suggested
 +                if let Some(parent_expr) = get_parent_expr_for_hir(self.cx, cmt.hir_id) {
 +                    match &parent_expr.kind {
 +                        // given expression is the self argument and will be handled completely by the compiler
 +                        // i.e.: `|x| x.is_something()`
 +                        ExprKind::MethodCall(_, self_expr, ..) if self_expr.hir_id == cmt.hir_id => {
 +                            let _ = write!(self.suggestion_start, "{start_snip}{ident_str_with_proj}");
 +                            self.next_pos = span.hi();
 +                            return;
 +                        },
 +                        // item is used in a call
 +                        // i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
 +                        ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [call_args @ ..], _) => {
 +                            let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
 +                            let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
 +
 +                            if matches!(arg_ty_kind, ty::Ref(_, _, Mutability::Not)) {
 +                                // suggest ampersand if call function is taking args by double reference
 +                                let takes_arg_by_double_ref =
 +                                    self.func_takes_arg_by_double_ref(parent_expr, cmt.hir_id);
 +
 +                                // compiler will automatically dereference field or index projection, so no need
 +                                // to suggest ampersand, but full identifier that includes projection is required
 +                                let has_field_or_index_projection =
 +                                    cmt.place.projections.iter().any(|proj| {
 +                                        matches!(proj.kind, ProjectionKind::Field(..) | ProjectionKind::Index)
 +                                    });
 +
 +                                // no need to bind again if the function doesn't take arg by double ref
 +                                // and if the item is already a double ref
 +                                let ident_sugg = if !call_args.is_empty()
 +                                    && !takes_arg_by_double_ref
 +                                    && (self.closure_arg_is_type_annotated_double_ref || has_field_or_index_projection)
 +                                {
 +                                    let ident = if has_field_or_index_projection {
 +                                        ident_str_with_proj
 +                                    } else {
 +                                        ident_str
 +                                    };
 +                                    format!("{start_snip}{ident}")
 +                                } else {
 +                                    format!("{start_snip}&{ident_str}")
 +                                };
 +                                self.suggestion_start.push_str(&ident_sugg);
 +                                self.next_pos = span.hi();
 +                                return;
 +                            }
 +
 +                            self.applicability = Applicability::Unspecified;
 +                        },
 +                        _ => (),
 +                    }
 +                }
 +
 +                let mut replacement_str = ident_str;
 +                let mut projections_handled = false;
 +                cmt.place.projections.iter().enumerate().for_each(|(i, proj)| {
 +                    match proj.kind {
 +                        // Field projection like `|v| v.foo`
 +                        // no adjustment needed here, as field projections are handled by the compiler
 +                        ProjectionKind::Field(..) => match cmt.place.ty_before_projection(i).kind() {
 +                            ty::Adt(..) | ty::Tuple(_) => {
 +                                replacement_str = ident_str_with_proj.clone();
 +                                projections_handled = true;
 +                            },
 +                            _ => (),
 +                        },
 +                        // Index projection like `|x| foo[x]`
 +                        // the index is dropped so we can't get it to build the suggestion,
 +                        // so the span is set-up again to get more code, using `span.hi()` (i.e.: `foo[x]`)
 +                        // instead of `span.lo()` (i.e.: `foo`)
 +                        ProjectionKind::Index => {
 +                            let start_span = Span::new(self.next_pos, span.hi(), span.ctxt(), None);
 +                            start_snip = snippet_with_applicability(self.cx, start_span, "..", &mut self.applicability);
 +                            replacement_str.clear();
 +                            projections_handled = true;
 +                        },
 +                        // note: unable to trigger `Subslice` kind in tests
 +                        ProjectionKind::Subslice => (),
 +                        ProjectionKind::Deref => {
 +                            // Explicit derefs are typically handled later on, but
 +                            // some items do not need explicit deref, such as array accesses,
 +                            // so we mark them as already processed
 +                            // i.e.: don't suggest `*sub[1..4].len()` for `|sub| sub[1..4].len() == 3`
 +                            if let ty::Ref(_, inner, _) = cmt.place.ty_before_projection(i).kind() {
 +                                if matches!(inner.kind(), ty::Ref(_, innermost, _) if innermost.is_array()) {
 +                                    projections_handled = true;
 +                                }
 +                            }
 +                        },
 +                    }
 +                });
 +
 +                // handle `ProjectionKind::Deref` by removing one explicit deref
 +                // if no special case was detected (i.e.: suggest `*x` instead of `**x`)
 +                if !projections_handled {
 +                    let last_deref = cmt
 +                        .place
 +                        .projections
 +                        .iter()
 +                        .rposition(|proj| proj.kind == ProjectionKind::Deref);
 +
 +                    if let Some(pos) = last_deref {
 +                        let mut projections = cmt.place.projections.clone();
 +                        projections.truncate(pos);
 +
 +                        for item in projections {
 +                            if item.kind == ProjectionKind::Deref {
 +                                replacement_str = format!("*{replacement_str}");
 +                            }
 +                        }
 +                    }
 +                }
 +
 +                let _ = write!(self.suggestion_start, "{start_snip}{replacement_str}");
 +            }
 +            self.next_pos = span.hi();
 +        }
 +    }
 +
 +    fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn fake_read(&mut self, _: &PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::Sugg;
 +
 +    use rustc_ast::util::parser::AssocOp;
 +    use std::borrow::Cow;
 +
 +    const SUGGESTION: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("function_call()"));
 +
 +    #[test]
 +    fn make_return_transform_sugg_into_a_return_call() {
 +        assert_eq!("return function_call()", SUGGESTION.make_return().to_string());
 +    }
 +
 +    #[test]
 +    fn blockify_transforms_sugg_into_a_block() {
 +        assert_eq!("{ function_call() }", SUGGESTION.blockify().to_string());
 +    }
 +
 +    #[test]
 +    fn binop_maybe_par() {
 +        let sugg = Sugg::BinOp(AssocOp::Add, "1".into(), "1".into());
 +        assert_eq!("(1 + 1)", sugg.maybe_par().to_string());
 +
 +        let sugg = Sugg::BinOp(AssocOp::Add, "(1 + 1)".into(), "(1 + 1)".into());
 +        assert_eq!("((1 + 1) + (1 + 1))", sugg.maybe_par().to_string());
 +    }
 +    #[test]
 +    fn not_op() {
 +        use AssocOp::{Add, Equal, Greater, GreaterEqual, LAnd, LOr, Less, LessEqual, NotEqual};
 +
 +        fn test_not(op: AssocOp, correct: &str) {
 +            let sugg = Sugg::BinOp(op, "x".into(), "y".into());
 +            assert_eq!((!sugg).to_string(), correct);
 +        }
 +
 +        // Invert the comparison operator.
 +        test_not(Equal, "x != y");
 +        test_not(NotEqual, "x == y");
 +        test_not(Less, "x >= y");
 +        test_not(LessEqual, "x > y");
 +        test_not(Greater, "x <= y");
 +        test_not(GreaterEqual, "x < y");
 +
 +        // Other operators are inverted like !(..).
 +        test_not(Add, "!(x + y)");
 +        test_not(LAnd, "!(x && y)");
 +        test_not(LOr, "!(x || y)");
 +    }
 +}
index 2ceda3511fe44642b1a822dcddd5eb8857e8da2b,0000000000000000000000000000000000000000..bfb2d472a393cf0525955de8f38dce24e0f270b2
mode 100644,000000..100644
--- /dev/null
@@@ -1,1083 -1,0 +1,1093 @@@
- use rustc_infer::infer::{TyCtxtInferExt, type_variable::{TypeVariableOrigin, TypeVariableOriginKind}};
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use core::ops::ControlFlow;
 +use rustc_ast::ast::Mutability;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +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};
-     implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params.iter().map(|&arg| Some(arg)))
++use rustc_infer::infer::{
++    type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
++    TyCtxtInferExt,
++};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::{ConstValue, Scalar};
 +use rustc_middle::ty::{
 +    self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
 +    ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
 +    VariantDef, VariantDiscr,
 +};
 +use rustc_middle::ty::{GenericArg, GenericArgKind};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 +use rustc_target::abi::{Size, VariantIdx};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 +use std::iter;
 +
 +use crate::{match_def_path, path_res, paths};
 +
 +// Checks if the given type implements copy.
 +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
 +}
 +
 +/// This checks whether a given type is known to implement Debug.
 +pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    cx.tcx
 +        .get_diagnostic_item(sym::Debug)
 +        .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
 +}
 +
 +/// Checks whether a type can be partially moved.
 +pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if has_drop(cx, ty) || is_copy(cx, ty) {
 +        return false;
 +    }
 +    match ty.kind() {
 +        ty::Param(_) => false,
 +        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
 +        _ => true,
 +    }
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 +/// constructor.
 +pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
 +/// constructor of the same type.
 +///
 +/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
 +/// will also return `true`.
 +pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => {
 +            if inner_ty == needle {
 +                return true;
 +            }
 +
 +            if inner_ty.ty_adt_def() == needle.ty_adt_def() {
 +                return true;
 +            }
 +
 +            if let ty::Opaque(def_id, _) = *inner_ty.kind() {
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    match predicate.kind().skip_binder() {
 +                        // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
 +                        // and check substituions to find `U`.
 +                        ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
 +                            if trait_predicate
 +                                .trait_ref
 +                                .substs
 +                                .types()
 +                                .skip(1) // Skip the implicit `Self` generic parameter
 +                                .any(|ty| contains_ty_adt_constructor_opaque(cx, ty, needle))
 +                            {
 +                                return true;
 +                            }
 +                        },
 +                        // For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
 +                        // so we check the term for `U`.
 +                        ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => {
 +                            if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
 +                                if contains_ty_adt_constructor_opaque(cx, ty, needle) {
 +                                    return true;
 +                                }
 +                            };
 +                        },
 +                        _ => (),
 +                    }
 +                }
 +            }
 +
 +            false
 +        },
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Resolves `<T as Iterator>::Item` for `T`
 +/// Do not invoke without first verifying that the type implements `Iterator`
 +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .get_diagnostic_item(sym::Iterator)
 +        .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item"))
 +}
 +
 +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
 +/// implements a trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
 +        _ => None,
 +    }
 +}
 +
 +/// Returns true if ty has `iter` or `iter_mut` methods
 +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
 +    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
 +    // exists and has the desired signature. Unfortunately FnCtxt is not exported
 +    // so we can't use its `lookup_method` method.
 +    let into_iter_collections: &[Symbol] = &[
 +        sym::Vec,
 +        sym::Option,
 +        sym::Result,
 +        sym::BTreeMap,
 +        sym::BTreeSet,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::BinaryHeap,
 +        sym::HashSet,
 +        sym::HashMap,
 +        sym::PathBuf,
 +        sym::Path,
 +        sym::Receiver,
 +    ];
 +
 +    let ty_to_check = match probably_ref_ty.kind() {
 +        ty::Ref(_, ty_to_check, _) => *ty_to_check,
 +        _ => probably_ref_ty,
 +    };
 +
 +    let def_id = match ty_to_check.kind() {
 +        ty::Array(..) => return Some(sym::array),
 +        ty::Slice(..) => return Some(sym::slice),
 +        ty::Adt(adt, _) => adt.did(),
 +        _ => return None,
 +    };
 +
 +    for &name in into_iter_collections {
 +        if cx.tcx.is_diagnostic_item(name, def_id) {
 +            return Some(cx.tcx.item_name(def_id));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a type implements a trait.
 +/// The function returns false in case the type contains an inference variable.
 +///
 +/// See:
 +/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
 +/// * [Common tools for writing lints] for an example how to use this function and other options.
 +///
 +/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 +pub fn implements_trait<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
-     let ty_params = tcx.mk_substs(ty_params.into_iter().map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())));
++    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.
 +pub fn implements_trait_with_env<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    param_env: ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
 +) -> bool {
 +    // Clippy shouldn't have infer types
 +    assert!(!ty.needs_infer());
 +
 +    let ty = tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
 +    let infcx = tcx.infer_ctxt().build();
 +    let orig = TypeVariableOrigin {
 +        kind: TypeVariableOriginKind::MiscVariable,
 +        span: DUMMY_SP,
 +    };
-             PredicateKind::Clause(ty::Clause::Projection(p)) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
++    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()
 +}
 +
 +/// Checks whether this type implements `Drop`.
 +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.ty_adt_def() {
 +        Some(def) => def.has_dtor(cx.tcx),
 +        None => false,
 +    }
 +}
 +
 +// Returns whether the type has #[must_use] attribute
 +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
 +        ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
 +        ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
 +            // for the Array case we don't need to care for the len == 0 case
 +            // because we don't want to lint functions returning empty arrays
 +            is_must_use_ty(cx, *ty)
 +        },
 +        ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
 +        ty::Opaque(def_id, _) => {
 +            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
 +                if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() {
 +                    if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        ty::Dynamic(binder, _, _) => {
 +            for predicate in binder.iter() {
 +                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
 +                    if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
 +// this function can be removed once the `normalize` method does not panic when normalization does
 +// not succeed
 +/// Checks if `Ty` is normalizable. This function is useful
 +/// to avoid crashes on `layout_of`.
 +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
 +    is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
 +}
 +
 +fn is_normalizable_helper<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    cache: &mut FxHashMap<Ty<'tcx>, bool>,
 +) -> bool {
 +    if let Some(&cached_result) = cache.get(&ty) {
 +        return cached_result;
 +    }
 +    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
 +    cache.insert(ty, false);
 +    let infcx = cx.tcx.infer_ctxt().build();
 +    let cause = rustc_middle::traits::ObligationCause::dummy();
 +    let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
 +        match ty.kind() {
 +            ty::Adt(def, substs) => def.variants().iter().all(|variant| {
 +                variant
 +                    .fields
 +                    .iter()
 +                    .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
 +            }),
 +            _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
 +                GenericArgKind::Type(inner_ty) if inner_ty != ty => {
 +                    is_normalizable_helper(cx, param_env, inner_ty, cache)
 +                },
 +                _ => true, // if inner_ty == ty, we've already checked it
 +            }),
 +        }
 +    } else {
 +        false
 +    };
 +    cache.insert(ty, result);
 +    result
 +}
 +
 +/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
 +/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
 +/// tuples and slices of primitive type) see `is_recursively_primitive_type`
 +pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
 +    matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
 +}
 +
 +/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
 +/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
 +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
 +        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
 +        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
 +        ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is a reference equals to a diagnostic item
 +pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
 +            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +            _ => false,
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a diagnostic item. To check if a type implements a
 +/// trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// ---
 +///
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a lang item.
 +///
 +/// Returns `false` if the `LangItem` is not defined.
 +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Return `true` if the passed `typ` is `isize` or `usize`.
 +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
 +    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +}
 +
 +/// Checks if type is struct, enum or union type with the given def path.
 +///
 +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the drop order for a type matters. Some std types implement drop solely to
 +/// deallocate memory. For these types, and composites containing them, changing the drop order
 +/// won't result in any observable side effects.
 +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
 +        if !seen.insert(ty) {
 +            return false;
 +        }
 +        if !ty.has_significant_drop(cx.tcx, cx.param_env) {
 +            false
 +        }
 +        // Check for std types which implement drop, but only for memory allocation.
 +        else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
 +            || matches!(
 +                get_type_diagnostic_name(cx, ty),
 +                Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
 +            )
 +            || match_type(cx, ty, &paths::WEAK_RC)
 +            || match_type(cx, ty, &paths::WEAK_ARC)
 +        {
 +            // Check all of the generic arguments.
 +            if let ty::Adt(_, subs) = ty.kind() {
 +                subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
 +            } else {
 +                true
 +            }
 +        } else if !cx
 +            .tcx
 +            .lang_items()
 +            .drop_trait()
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +        {
 +            // This type doesn't implement drop, so no side effects here.
 +            // Check if any component type has any.
 +            match ty.kind() {
 +                ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
 +                ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
 +                ty::Adt(adt, subs) => adt
 +                    .all_fields()
 +                    .map(|f| f.ty(cx.tcx, subs))
 +                    .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
 +                _ => true,
 +            }
 +        } else {
 +            true
 +        }
 +    }
 +
 +    needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
 +        if let ty::Ref(_, ty, _) = ty.kind() {
 +            peel(*ty, count + 1)
 +        } else {
 +            (ty, count)
 +        }
 +    }
 +    peel(ty, 0)
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type, the number of references
 +/// removed, and whether the pointer is ultimately mutable or not.
 +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
 +    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
 +            ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
 +            _ => (ty, count, mutability),
 +        }
 +    }
 +    f(ty, 0, Mutability::Mut)
 +}
 +
 +/// Returns `true` if the given type is an `unsafe` function.
 +pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
 +        _ => false,
 +    }
 +}
 +
 +/// Returns the base type for HIR references and pointers.
 +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
 +    match ty.kind {
 +        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
 +        _ => ty,
 +    }
 +}
 +
 +/// Returns the base type for references and raw pointers, and count reference
 +/// depth.
 +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, _) => inner(*ty, depth + 1),
 +            _ => (ty, depth),
 +        }
 +    }
 +    inner(ty, 0)
 +}
 +
 +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
 +/// otherwise returns `false`
 +pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 +    match (&a.kind(), &b.kind()) {
 +        (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
 +            if did_a != did_b {
 +                return false;
 +            }
 +
 +            substs_a
 +                .iter()
 +                .zip(substs_b.iter())
 +                .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
 +                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
 +                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
 +                        same_type_and_consts(type_a, type_b)
 +                    },
 +                    _ => true,
 +                })
 +        },
 +        _ => a == b,
 +    }
 +}
 +
 +/// Checks if a given type looks safe to be uninitialized.
 +pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
 +        ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
 +        ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Gets an iterator over all predicates which apply to the given item.
 +pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
 +    let mut next_id = Some(id);
 +    iter::from_fn(move || {
 +        next_id.take().map(|id| {
 +            let preds = tcx.predicates_of(id);
 +            next_id = preds.parent;
 +            preds.predicates.iter()
 +        })
 +    })
 +    .flatten()
 +}
 +
 +/// A signature for a function like type.
 +#[derive(Clone, Copy)]
 +pub enum ExprFnSig<'tcx> {
 +    Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
 +    Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
 +    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
 +}
 +impl<'tcx> ExprFnSig<'tcx> {
 +    /// Gets the argument type at the given offset. This will return `None` when the index is out of
 +    /// bounds only for variadic functions, otherwise this will panic.
 +    pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
 +            Self::Sig(sig, _) => {
 +                if sig.c_variadic() {
 +                    sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
 +                } else {
 +                    Some(sig.input(i))
 +                }
 +            },
 +            Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
 +            Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
 +        }
 +    }
 +
 +    /// Gets the argument type at the given offset. For closures this will also get the type as
 +    /// written. This will return `None` when the index is out of bounds only for variadic
 +    /// functions, otherwise this will panic.
 +    pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
 +        match self {
 +            Self::Sig(sig, _) => {
 +                if sig.c_variadic() {
 +                    sig.inputs()
 +                        .map_bound(|inputs| inputs.get(i).copied())
 +                        .transpose()
 +                        .map(|arg| (None, arg))
 +                } else {
 +                    Some((None, sig.input(i)))
 +                }
 +            },
 +            Self::Closure(decl, sig) => Some((
 +                decl.and_then(|decl| decl.inputs.get(i)),
 +                sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
 +            )),
 +            Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
 +        }
 +    }
 +
 +    /// Gets the result type, if one could be found. Note that the result type of a trait may not be
 +    /// specified.
 +    pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
 +            Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
 +            Self::Trait(_, output, _) => output,
 +        }
 +    }
 +
 +    pub fn predicates_id(&self) -> Option<DefId> {
 +        if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
 +            id
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// If the expression is function like, get the signature for it.
 +pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
 +    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
 +        Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
 +    } else {
 +        ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
 +    }
 +}
 +
 +/// If the type is function like, get the signature for it.
 +pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
 +    if ty.is_box() {
 +        return ty_sig(cx, ty.boxed_ty());
 +    }
 +    match *ty.kind() {
 +        ty::Closure(id, subs) => {
 +            let decl = id
 +                .as_local()
 +                .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
 +            Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
 +        },
 +        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
 +        ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)),
 +        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
 +        ty::Dynamic(bounds, _, _) => {
 +            let lang_items = cx.tcx.lang_items();
 +            match bounds.principal() {
 +                Some(bound)
 +                    if Some(bound.def_id()) == lang_items.fn_trait()
 +                        || Some(bound.def_id()) == lang_items.fn_once_trait()
 +                        || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
 +                {
 +                    let output = bounds
 +                        .projection_bounds()
 +                        .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
 +                        .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
 +                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
 +                },
 +                _ => None,
 +            }
 +        },
 +        ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
 +            Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
 +            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
 +        },
 +        ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
 +        _ => None,
 +    }
 +}
 +
 +fn sig_from_bounds<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    predicates: &'tcx [Predicate<'tcx>],
 +    predicates_id: Option<DefId>,
 +) -> Option<ExprFnSig<'tcx>> {
 +    let mut inputs = None;
 +    let mut output = None;
 +    let lang_items = cx.tcx.lang_items();
 +
 +    for pred in predicates {
 +        match pred.kind().skip_binder() {
 +            PredicateKind::Clause(ty::Clause::Trait(p))
 +                if (lang_items.fn_trait() == Some(p.def_id())
 +                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                    || lang_items.fn_once_trait() == Some(p.def_id()))
 +                    && p.self_ty() == ty =>
 +            {
 +                let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
 +                if inputs.map_or(false, |inputs| i != inputs) {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                inputs = Some(i);
 +            },
 +            PredicateKind::Clause(ty::Clause::Projection(p))
 +                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
 +                    && p.projection_ty.self_ty() == ty =>
 +            {
 +                if output.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                output = Some(pred.kind().rebind(p.term.ty().unwrap()));
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
 +}
 +
 +fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
 +    let mut inputs = None;
 +    let mut output = None;
 +    let lang_items = cx.tcx.lang_items();
 +
 +    for (pred, _) in cx
 +        .tcx
 +        .bound_explicit_item_bounds(ty.item_def_id)
 +        .subst_iter_copied(cx.tcx, ty.substs)
 +    {
 +        match pred.kind().skip_binder() {
 +            PredicateKind::Clause(ty::Clause::Trait(p))
 +                if (lang_items.fn_trait() == Some(p.def_id())
 +                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                    || lang_items.fn_once_trait() == Some(p.def_id())) =>
 +            {
 +                let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
 +
 +                if inputs.map_or(false, |inputs| inputs != i) {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                inputs = Some(i);
 +            },
-                 "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
++            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;
 +                }
 +                output = pred.kind().rebind(p.term.ty()).transpose();
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
 +}
 +
 +#[derive(Clone, Copy)]
 +pub enum EnumValue {
 +    Unsigned(u128),
 +    Signed(i128),
 +}
 +impl core::ops::Add<u32> for EnumValue {
 +    type Output = Self;
 +    fn add(self, n: u32) -> Self::Output {
 +        match self {
 +            Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
 +            Self::Signed(x) => Self::Signed(x + i128::from(n)),
 +        }
 +    }
 +}
 +
 +/// Attempts to read the given constant as though it were an enum value.
 +#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
 +pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
 +    if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
 +        match tcx.type_of(id).kind() {
 +            ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
 +                1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
 +                2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
 +                4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
 +                8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
 +                16 => value.assert_bits(Size::from_bytes(16)) as i128,
 +                _ => return None,
 +            })),
 +            ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
 +                1 => value.assert_bits(Size::from_bytes(1)),
 +                2 => value.assert_bits(Size::from_bytes(2)),
 +                4 => value.assert_bits(Size::from_bytes(4)),
 +                8 => value.assert_bits(Size::from_bytes(8)),
 +                16 => value.assert_bits(Size::from_bytes(16)),
 +                _ => return None,
 +            })),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Gets the value of the given variant.
 +pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
 +    let variant = &adt.variant(i);
 +    match variant.discr {
 +        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
 +        VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
 +            VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
 +            VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
 +        },
 +    }
 +}
 +
 +/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
 +/// platform specific `libc::<platform>::c_void` types in libc.
 +pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    if let ty::Adt(adt, _) = ty.kind()
 +        && let &[krate, .., name] = &*cx.get_def_path(adt.did())
 +        && let sym::libc | sym::core | sym::std = krate
 +        && name.as_str() == "c_void"
 +    {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn for_each_top_level_late_bound_region<B>(
 +    ty: Ty<'_>,
 +    f: impl FnMut(BoundRegion) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<F> {
 +        index: u32,
 +        f: F,
 +    }
 +    impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
 +        type BreakTy = B;
 +        fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
 +            if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
 +                (self.f)(bound)
 +            } else {
 +                ControlFlow::Continue(())
 +            }
 +        }
 +        fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
 +            self.index += 1;
 +            let res = t.super_visit_with(self);
 +            self.index -= 1;
 +            res
 +        }
 +    }
 +    ty.visit_with(&mut V { index: 0, f })
 +}
 +
 +pub struct AdtVariantInfo {
 +    pub ind: usize,
 +    pub size: u64,
 +
 +    /// (ind, size)
 +    pub fields_size: Vec<(usize, u64)>,
 +}
 +
 +impl AdtVariantInfo {
 +    /// Returns ADT variants ordered by size
 +    pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List<GenericArg<'tcx>>) -> Vec<Self> {
 +        let mut variants_size = adt
 +            .variants()
 +            .iter()
 +            .enumerate()
 +            .map(|(i, variant)| {
 +                let mut fields_size = variant
 +                    .fields
 +                    .iter()
 +                    .enumerate()
 +                    .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
 +                    .collect::<Vec<_>>();
 +                fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size)));
 +
 +                Self {
 +                    ind: i,
 +                    size: fields_size.iter().map(|(_, size)| size).sum(),
 +                    fields_size,
 +                }
 +            })
 +            .collect::<Vec<_>>();
 +        variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
 +        variants_size
 +    }
 +}
 +
 +/// Gets the struct or enum variant from the given `Res`
 +pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
 +    match res {
 +        Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
 +        Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
 +        Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
 +        Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
 +            let var_id = cx.tcx.parent(id);
 +            Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
 +        },
 +        Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
 +        _ => None,
 +    }
 +}
 +
 +/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
 +pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
 +    let ty::Param(ty) = *ty.kind() else {
 +        return false;
 +    };
 +    let lang = tcx.lang_items();
 +    let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
 +        = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
 +    else {
 +        return false;
 +    };
 +    predicates
 +        .iter()
 +        .try_fold(false, |found, p| {
 +            if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder()
 +            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
 +            && ty.index == self_ty.index
 +        {
 +            // This should use `super_traits_of`, but that's a private function.
 +            if p.trait_ref.def_id == fn_once_id {
 +                return Some(true);
 +            } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
 +                return None;
 +            }
 +        }
 +            Some(found)
 +        })
 +        .unwrap_or(false)
 +}
 +
 +/// Comes up with an "at least" guesstimate for the type's size, not taking into
 +/// account the layout of type parameters.
 +pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
 +    use rustc_middle::ty::layout::LayoutOf;
 +    if !is_normalizable(cx, cx.param_env, ty) {
 +        return 0;
 +    }
 +    match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
 +        (Ok(size), _) => size,
 +        (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(),
 +        (Err(_), ty::Array(t, n)) => {
 +            n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
 +        },
 +        (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
 +            .variants()
 +            .iter()
 +            .map(|v| {
 +                v.fields
 +                    .iter()
 +                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
 +                    .sum::<u64>()
 +            })
 +            .sum(),
 +        (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
 +            .variants()
 +            .iter()
 +            .map(|v| {
 +                v.fields
 +                    .iter()
 +                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
 +                    .sum::<u64>()
 +            })
 +            .max()
 +            .unwrap_or_default(),
 +        (Err(_), ty::Adt(def, subst)) if def.is_union() => def
 +            .variants()
 +            .iter()
 +            .map(|v| {
 +                v.fields
 +                    .iter()
 +                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
 +                    .max()
 +                    .unwrap_or_default()
 +            })
 +            .max()
 +            .unwrap_or_default(),
 +        (Err(_), _) => 0,
 +    }
 +}
 +
 +/// Makes the projection type for the named associated type in the given impl or trait impl.
 +///
 +/// This function is for associated types which are "known" to exist, and as such, will only return
 +/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
 +/// enabled this will check that the named associated type exists, the correct number of
 +/// substitutions are given, and that the correct kinds of substitutions are given (lifetime,
 +/// constant or type). This will not check if type normalization would succeed.
 +pub fn make_projection<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    container_id: DefId,
 +    assoc_ty: Symbol,
 +    substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
 +) -> Option<ProjectionTy<'tcx>> {
 +    fn helper<'tcx>(
 +        tcx: TyCtxt<'tcx>,
 +        container_id: DefId,
 +        assoc_ty: Symbol,
 +        substs: SubstsRef<'tcx>,
 +    ) -> Option<ProjectionTy<'tcx>> {
 +        let Some(assoc_item) = tcx
 +            .associated_items(container_id)
 +            .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id)
 +        else {
 +            debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`");
 +            return None;
 +        };
 +        #[cfg(debug_assertions)]
 +        {
 +            let generics = tcx.generics_of(assoc_item.def_id);
 +            let generic_count = generics.parent_count + generics.params.len();
 +            let params = generics
 +                .parent
 +                .map_or([].as_slice(), |id| &*tcx.generics_of(id).params)
 +                .iter()
 +                .chain(&generics.params)
 +                .map(|x| &x.kind);
 +
 +            debug_assert!(
 +                generic_count == substs.len(),
-                     the given arguments are: `{:#?}`",
++                "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
 +                    note: the expected parameters are: {:#?}\n\
-                 generic_count,
++                    the given arguments are: `{substs:#?}`",
 +                assoc_item.def_id,
 +                substs.len(),
-                 substs,
 +                params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
-                     "mismatched subst type at index {}: expected a {}, found `{:?}`\n\
 +            );
 +
 +            if let Some((idx, (param, arg))) = params
 +                .clone()
 +                .zip(substs.iter().map(GenericArg::unpack))
 +                .enumerate()
 +                .find(|(_, (param, arg))| {
 +                    !matches!(
 +                        (param, arg),
 +                        (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
 +                            | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
 +                            | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_))
 +                    )
 +                })
 +            {
 +                debug_assert!(
 +                    false,
-                         the given arguments are {:#?}",
-                     idx,
++                    "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
 +                        note: the expected parameters are {:#?}\n\
-                     arg,
-                     params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
-                     substs,
++                        the given arguments are {substs:#?}",
 +                    param.descr(),
++                    params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
 +                );
 +            }
 +        }
 +
 +        Some(ProjectionTy {
 +            substs,
 +            item_def_id: assoc_item.def_id,
 +        })
 +    }
 +    helper(
 +        tcx,
 +        container_id,
 +        assoc_ty,
 +        tcx.mk_substs(substs.into_iter().map(Into::into)),
 +    )
 +}
 +
 +/// Normalizes the named associated type in the given impl or trait impl.
 +///
 +/// This function is for associated types which are "known" to be valid with the given
 +/// substitutions, and as such, will only return `None` when debug assertions are disabled in order
 +/// to prevent ICE's. With debug assertions enabled this will check that that type normalization
 +/// succeeds as well as everything checked by `make_projection`.
 +pub fn make_normalized_projection<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    param_env: ParamEnv<'tcx>,
 +    container_id: DefId,
 +    assoc_ty: Symbol,
 +    substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
 +) -> Option<Ty<'tcx>> {
 +    fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: ProjectionTy<'tcx>) -> Option<Ty<'tcx>> {
 +        #[cfg(debug_assertions)]
 +        if let Some((i, subst)) = ty
 +            .substs
 +            .iter()
 +            .enumerate()
 +            .find(|(_, subst)| subst.has_late_bound_regions())
 +        {
 +            debug_assert!(
 +                false,
 +                "substs contain late-bound region at index `{i}` which can't be normalized.\n\
 +                    use `TyCtxt::erase_late_bound_regions`\n\
 +                    note: subst is `{subst:#?}`",
 +            );
 +            return None;
 +        }
 +        match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) {
 +            Ok(ty) => Some(ty),
 +            Err(e) => {
 +                debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
 +                None
 +            },
 +        }
 +    }
 +    helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
 +}
index d4294f18fd5019c0eed128e9321f8caca80b0109,0000000000000000000000000000000000000000..863fb60fcfca1fb09aee2de418dcd9c0743d7855
mode 100644,000000..100644
--- /dev/null
@@@ -1,726 -1,0 +1,726 @@@
-     struct WithStmtGuarg<'a, F> {
 +use crate::ty::needs_ordered_drop;
 +use crate::{get_enclosing_block, path_to_local_id};
 +use core::ops::ControlFlow;
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 +use rustc_hir::{
 +    AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath,
 +    Stmt, UnOp, UnsafeSource, Unsafety,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
 +use rustc_span::Span;
 +
 +mod internal {
 +    /// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
 +    /// for only two types. `()` always descends. `Descend` allows controlled descent.
 +    pub trait Continue {
 +        fn descend(&self) -> bool;
 +    }
 +}
 +use internal::Continue;
 +
 +impl Continue for () {
 +    fn descend(&self) -> bool {
 +        true
 +    }
 +}
 +
 +/// Allows for controlled descent when using visitor functions. Use `()` instead when always
 +/// descending into child nodes.
 +#[derive(Clone, Copy)]
 +pub enum Descend {
 +    Yes,
 +    No,
 +}
 +impl From<bool> for Descend {
 +    fn from(from: bool) -> Self {
 +        if from { Self::Yes } else { Self::No }
 +    }
 +}
 +impl Continue for Descend {
 +    fn descend(&self) -> bool {
 +        matches!(self, Self::Yes)
 +    }
 +}
 +
 +/// A type which can be visited.
 +pub trait Visitable<'tcx> {
 +    /// Calls the corresponding `visit_*` function on the visitor.
 +    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
 +}
 +macro_rules! visitable_ref {
 +    ($t:ident, $f:ident) => {
 +        impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
 +            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
 +                visitor.$f(self);
 +            }
 +        }
 +    };
 +}
 +visitable_ref!(Arm, visit_arm);
 +visitable_ref!(Block, visit_block);
 +visitable_ref!(Body, visit_body);
 +visitable_ref!(Expr, visit_expr);
 +visitable_ref!(Stmt, visit_stmt);
 +
 +/// Calls the given function once for each expression contained. This does not enter any bodies or
 +/// nested items.
 +pub fn for_each_expr<'tcx, B, C: Continue>(
 +    node: impl Visitable<'tcx>,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
 +) -> Option<B> {
 +    struct V<B, F> {
 +        f: F,
 +        res: Option<B>,
 +    }
 +    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if self.res.is_some() {
 +                return;
 +            }
 +            match (self.f)(e) {
 +                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
 +                ControlFlow::Break(b) => self.res = Some(b),
 +                ControlFlow::Continue(_) => (),
 +            }
 +        }
 +
 +        // Avoid unnecessary `walk_*` calls.
 +        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
 +        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
 +        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
 +        // Avoid monomorphising all `visit_*` functions.
 +        fn visit_nested_item(&mut self, _: ItemId) {}
 +    }
 +    let mut v = V { f, res: None };
 +    node.visit(&mut v);
 +    v.res
 +}
 +
 +/// Calls the given function once for each expression contained. This will enter bodies, but not
 +/// nested items.
 +pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
 +    cx: &LateContext<'tcx>,
 +    node: impl Visitable<'tcx>,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
 +) -> Option<B> {
 +    struct V<'tcx, B, F> {
 +        tcx: TyCtxt<'tcx>,
 +        f: F,
 +        res: Option<B>,
 +    }
 +    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if self.res.is_some() {
 +                return;
 +            }
 +            match (self.f)(e) {
 +                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
 +                ControlFlow::Break(b) => self.res = Some(b),
 +                ControlFlow::Continue(_) => (),
 +            }
 +        }
 +
 +        // Only walk closures
 +        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +        // Avoid unnecessary `walk_*` calls.
 +        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
 +        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
 +        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
 +        // Avoid monomorphising all `visit_*` functions.
 +        fn visit_nested_item(&mut self, _: ItemId) {}
 +    }
 +    let mut v = V {
 +        tcx: cx.tcx,
 +        f,
 +        res: None,
 +    };
 +    node.visit(&mut v);
 +    v.res
 +}
 +
 +/// returns `true` if expr contains match expr desugared from try
 +fn contains_try(expr: &hir::Expr<'_>) -> bool {
 +    for_each_expr(expr, |e| {
 +        if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_some()
 +}
 +
 +pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
 +where
 +    F: FnMut(&'hir hir::Expr<'hir>) -> bool,
 +{
 +    struct RetFinder<F> {
 +        in_stmt: bool,
 +        failed: bool,
 +        cb: F,
 +    }
 +
-         fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
++    struct WithStmtGuard<'a, F> {
 +        val: &'a mut RetFinder<F>,
 +        prev_in_stmt: bool,
 +    }
 +
 +    impl<F> RetFinder<F> {
-             WithStmtGuarg {
++        fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
 +            let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
-     impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
++            WithStmtGuard {
 +                val: self,
 +                prev_in_stmt,
 +            }
 +        }
 +    }
 +
-     impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
++    impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
 +        type Target = RetFinder<F>;
 +
 +        fn deref(&self) -> &Self::Target {
 +            self.val
 +        }
 +    }
 +
-     impl<F> Drop 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 WithStmtGuard<'_, F> {
 +        fn drop(&mut self) {
 +            self.val.in_stmt = self.prev_in_stmt;
 +        }
 +    }
 +
 +    impl<'hir, F: FnMut(&'hir hir::Expr<'hir>) -> bool> intravisit::Visitor<'hir> for RetFinder<F> {
 +        fn visit_stmt(&mut self, stmt: &'hir hir::Stmt<'_>) {
 +            intravisit::walk_stmt(&mut *self.inside_stmt(true), stmt);
 +        }
 +
 +        fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
 +            if self.failed {
 +                return;
 +            }
 +            if self.in_stmt {
 +                match expr.kind {
 +                    hir::ExprKind::Ret(Some(expr)) => self.inside_stmt(false).visit_expr(expr),
 +                    _ => intravisit::walk_expr(self, expr),
 +                }
 +            } else {
 +                match expr.kind {
 +                    hir::ExprKind::If(cond, then, else_opt) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        self.visit_expr(then);
 +                        if let Some(el) = else_opt {
 +                            self.visit_expr(el);
 +                        }
 +                    },
 +                    hir::ExprKind::Match(cond, arms, _) => {
 +                        self.inside_stmt(true).visit_expr(cond);
 +                        for arm in arms {
 +                            self.visit_expr(arm.body);
 +                        }
 +                    },
 +                    hir::ExprKind::Block(..) => intravisit::walk_expr(self, expr),
 +                    hir::ExprKind::Ret(Some(expr)) => self.visit_expr(expr),
 +                    _ => self.failed |= !(self.cb)(expr),
 +                }
 +            }
 +        }
 +    }
 +
 +    !contains_try(expr) && {
 +        let mut ret_finder = RetFinder {
 +            in_stmt: false,
 +            failed: false,
 +            cb: callback,
 +        };
 +        ret_finder.visit_expr(expr);
 +        !ret_finder.failed
 +    }
 +}
 +
 +/// Checks if the given resolved path is used in the given body.
 +pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
 +    for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| {
 +        if let ExprKind::Path(p) = &e.kind {
 +            if cx.qpath_res(p, e.hir_id) == res {
 +                return ControlFlow::Break(());
 +            }
 +        }
 +        ControlFlow::Continue(())
 +    })
 +    .is_some()
 +}
 +
 +/// Checks if the given local is used.
 +pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
 +    for_each_expr_with_closures(cx, visitable, |e| {
 +        if path_to_local_id(e, id) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_some()
 +}
 +
 +/// Checks if the given expression is a constant.
 +pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_const: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.is_const {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::ConstBlock(_) => return,
 +                ExprKind::Call(
 +                    &Expr {
 +                        kind: ExprKind::Path(ref p),
 +                        hir_id,
 +                        ..
 +                    },
 +                    _,
 +                ) if self
 +                    .cx
 +                    .qpath_res(p, hir_id)
 +                    .opt_def_id()
 +                    .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
 +                ExprKind::MethodCall(..)
 +                    if self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| self.cx.tcx.is_const_fn_raw(id)) => {},
 +                ExprKind::Binary(_, lhs, rhs)
 +                    if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
 +                        && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
 +                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
 +                ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
 +                ExprKind::Index(base, _)
 +                    if matches!(
 +                        self.cx.typeck_results().expr_ty(base).peel_refs().kind(),
 +                        ty::Slice(_) | ty::Array(..)
 +                    ) => {},
 +                ExprKind::Path(ref p)
 +                    if matches!(
 +                        self.cx.qpath_res(p, e.hir_id),
 +                        Res::Def(
 +                            DefKind::Const
 +                                | DefKind::AssocConst
 +                                | DefKind::AnonConst
 +                                | DefKind::ConstParam
 +                                | DefKind::Ctor(..)
 +                                | DefKind::Fn
 +                                | DefKind::AssocFn,
 +                            _
 +                        ) | Res::SelfCtor(_)
 +                    ) => {},
 +
 +                ExprKind::AddrOf(..)
 +                | ExprKind::Array(_)
 +                | ExprKind::Block(..)
 +                | ExprKind::Cast(..)
 +                | ExprKind::DropTemps(_)
 +                | ExprKind::Field(..)
 +                | ExprKind::If(..)
 +                | ExprKind::Let(..)
 +                | ExprKind::Lit(_)
 +                | ExprKind::Match(..)
 +                | ExprKind::Repeat(..)
 +                | ExprKind::Struct(..)
 +                | ExprKind::Tup(_)
 +                | ExprKind::Type(..) => (),
 +
 +                _ => {
 +                    self.is_const = false;
 +                    return;
 +                },
 +            }
 +            walk_expr(self, e);
 +        }
 +    }
 +
 +    let mut v = V { cx, is_const: true };
 +    v.visit_expr(e);
 +    v.is_const
 +}
 +
 +/// Checks if the given expression performs an unsafe operation outside of an unsafe block.
 +pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        is_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.is_unsafe {
 +                return;
 +            }
 +            match e.kind {
 +                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
 +                    self.is_unsafe = true;
 +                },
 +                ExprKind::MethodCall(..)
 +                    if self
 +                        .cx
 +                        .typeck_results()
 +                        .type_dependent_def_id(e.hir_id)
 +                        .map_or(false, |id| self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe) =>
 +                {
 +                    self.is_unsafe = true;
 +                },
 +                ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
 +                    ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
 +                    ty::FnPtr(sig) if sig.unsafety() == Unsafety::Unsafe => self.is_unsafe = true,
 +                    _ => walk_expr(self, e),
 +                },
 +                ExprKind::Path(ref p)
 +                    if self
 +                        .cx
 +                        .qpath_res(p, e.hir_id)
 +                        .opt_def_id()
 +                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
 +                {
 +                    self.is_unsafe = true;
 +                },
 +                _ => walk_expr(self, e),
 +            }
 +        }
 +        fn visit_block(&mut self, b: &'tcx Block<'_>) {
 +            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
 +                walk_block(self, b);
 +            }
 +        }
 +        fn visit_nested_item(&mut self, id: ItemId) {
 +            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
 +                self.is_unsafe = i.unsafety == Unsafety::Unsafe;
 +            }
 +        }
 +    }
 +    let mut v = V { cx, is_unsafe: false };
 +    v.visit_expr(e);
 +    v.is_unsafe
 +}
 +
 +/// Checks if the given expression contains an unsafe block
 +pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        found_unsafe: bool,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_block(&mut self, b: &'tcx Block<'_>) {
 +            if self.found_unsafe {
 +                return;
 +            }
 +            if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.found_unsafe = true;
 +                return;
 +            }
 +            walk_block(self, b);
 +        }
 +    }
 +    let mut v = V {
 +        cx,
 +        found_unsafe: false,
 +    };
 +    v.visit_expr(e);
 +    v.found_unsafe
 +}
 +
 +/// Runs the given function for each sub-expression producing the final value consumed by the parent
 +/// of the give expression.
 +///
 +/// e.g. for the following expression
 +/// ```rust,ignore
 +/// if foo {
 +///     f(0)
 +/// } else {
 +///     1 + 1
 +/// }
 +/// ```
 +/// this will pass both `f(0)` and `1+1` to the given function.
 +pub fn for_each_value_source<'tcx, B>(
 +    e: &'tcx Expr<'tcx>,
 +    f: &mut impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    match e.kind {
 +        ExprKind::Block(Block { expr: Some(e), .. }, _) => for_each_value_source(e, f),
 +        ExprKind::Match(_, arms, _) => {
 +            for arm in arms {
 +                for_each_value_source(arm.body, f)?;
 +            }
 +            ControlFlow::Continue(())
 +        },
 +        ExprKind::If(_, if_expr, Some(else_expr)) => {
 +            for_each_value_source(if_expr, f)?;
 +            for_each_value_source(else_expr, f)
 +        },
 +        ExprKind::DropTemps(e) => for_each_value_source(e, f),
 +        _ => f(e),
 +    }
 +}
 +
 +/// Runs the given function for each path expression referencing the given local which occur after
 +/// the given expression.
 +pub fn for_each_local_use_after_expr<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    local_id: HirId,
 +    expr_id: HirId,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<'cx, 'tcx, F, B> {
 +        cx: &'cx LateContext<'tcx>,
 +        local_id: HirId,
 +        expr_id: HirId,
 +        found: bool,
 +        res: ControlFlow<B>,
 +        f: F,
 +    }
 +    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if !self.found {
 +                if e.hir_id == self.expr_id {
 +                    self.found = true;
 +                } else {
 +                    walk_expr(self, e);
 +                }
 +                return;
 +            }
 +            if self.res.is_break() {
 +                return;
 +            }
 +            if path_to_local_id(e, self.local_id) {
 +                self.res = (self.f)(e);
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(b) = get_enclosing_block(cx, local_id) {
 +        let mut v = V {
 +            cx,
 +            local_id,
 +            expr_id,
 +            found: false,
 +            res: ControlFlow::Continue(()),
 +            f,
 +        };
 +        v.visit_block(b);
 +        v.res
 +    } else {
 +        ControlFlow::Continue(())
 +    }
 +}
 +
 +// Calls the given function for every unconsumed temporary created by the expression. Note the
 +// function is only guaranteed to be called for types which need to be dropped, but it may be called
 +// for other types.
 +#[allow(clippy::too_many_lines)]
 +pub fn for_each_unconsumed_temporary<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    // Todo: Handle partially consumed values.
 +    fn helper<'tcx, B>(
 +        typeck: &'tcx TypeckResults<'tcx>,
 +        consume: bool,
 +        e: &'tcx Expr<'tcx>,
 +        f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
 +    ) -> ControlFlow<B> {
 +        if !consume
 +            || matches!(
 +                typeck.expr_adjustments(e),
 +                [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
 +            )
 +        {
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, p))
 +                    if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
 +                {
 +                    f(typeck.expr_ty(e))?;
 +                },
 +                ExprKind::Path(_)
 +                | ExprKind::Unary(UnOp::Deref, _)
 +                | ExprKind::Index(..)
 +                | ExprKind::Field(..)
 +                | ExprKind::AddrOf(..) => (),
 +                _ => f(typeck.expr_ty(e))?,
 +            }
 +        }
 +        match e.kind {
 +            ExprKind::AddrOf(_, _, e)
 +            | ExprKind::Field(e, _)
 +            | ExprKind::Unary(UnOp::Deref, e)
 +            | ExprKind::Match(e, ..)
 +            | ExprKind::Let(&Let { init: e, .. }) => {
 +                helper(typeck, false, e, f)?;
 +            },
 +            ExprKind::Block(&Block { expr: Some(e), .. }, _)
 +            | ExprKind::Box(e)
 +            | ExprKind::Cast(e, _)
 +            | ExprKind::Unary(_, e) => {
 +                helper(typeck, true, e, f)?;
 +            },
 +            ExprKind::Call(callee, args) => {
 +                helper(typeck, true, callee, f)?;
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::MethodCall(_, receiver, args, _) => {
 +                helper(typeck, true, receiver, f)?;
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::Tup(args) | ExprKind::Array(args) => {
 +                for arg in args {
 +                    helper(typeck, true, arg, f)?;
 +                }
 +            },
 +            ExprKind::Index(borrowed, consumed)
 +            | ExprKind::Assign(borrowed, consumed, _)
 +            | ExprKind::AssignOp(_, borrowed, consumed) => {
 +                helper(typeck, false, borrowed, f)?;
 +                helper(typeck, true, consumed, f)?;
 +            },
 +            ExprKind::Binary(_, lhs, rhs) => {
 +                helper(typeck, true, lhs, f)?;
 +                helper(typeck, true, rhs, f)?;
 +            },
 +            ExprKind::Struct(_, fields, default) => {
 +                for field in fields {
 +                    helper(typeck, true, field.expr, f)?;
 +                }
 +                if let Some(default) = default {
 +                    helper(typeck, false, default, f)?;
 +                }
 +            },
 +            ExprKind::If(cond, then, else_expr) => {
 +                helper(typeck, true, cond, f)?;
 +                helper(typeck, true, then, f)?;
 +                if let Some(else_expr) = else_expr {
 +                    helper(typeck, true, else_expr, f)?;
 +                }
 +            },
 +            ExprKind::Type(e, _) => {
 +                helper(typeck, consume, e, f)?;
 +            },
 +
 +            // Either drops temporaries, jumps out of the current expression, or has no sub expression.
 +            ExprKind::DropTemps(_)
 +            | ExprKind::Ret(_)
 +            | ExprKind::Break(..)
 +            | ExprKind::Yield(..)
 +            | ExprKind::Block(..)
 +            | ExprKind::Loop(..)
 +            | ExprKind::Repeat(..)
 +            | ExprKind::Lit(_)
 +            | ExprKind::ConstBlock(_)
 +            | ExprKind::Closure { .. }
 +            | ExprKind::Path(_)
 +            | ExprKind::Continue(_)
 +            | ExprKind::InlineAsm(_)
 +            | ExprKind::Err => (),
 +        }
 +        ControlFlow::Continue(())
 +    }
 +    helper(cx.typeck_results(), true, e, &mut f)
 +}
 +
 +pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
 +    for_each_unconsumed_temporary(cx, e, |ty| {
 +        if needs_ordered_drop(cx, ty) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    })
 +    .is_break()
 +}
 +
 +/// Runs the given function for each path expression referencing the given local which occur after
 +/// the given expression.
 +pub fn for_each_local_assignment<'tcx, B>(
 +    cx: &LateContext<'tcx>,
 +    local_id: HirId,
 +    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<'cx, 'tcx, F, B> {
 +        cx: &'cx LateContext<'tcx>,
 +        local_id: HirId,
 +        res: ControlFlow<B>,
 +        f: F,
 +    }
 +    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
 +            if let ExprKind::Assign(lhs, rhs, _) = e.kind
 +                && self.res.is_continue()
 +                && path_to_local_id(lhs, self.local_id)
 +            {
 +                self.res = (self.f)(rhs);
 +                self.visit_expr(rhs);
 +            } else {
 +                walk_expr(self, e);
 +            }
 +        }
 +    }
 +
 +    if let Some(b) = get_enclosing_block(cx, local_id) {
 +        let mut v = V {
 +            cx,
 +            local_id,
 +            res: ControlFlow::Continue(()),
 +            f,
 +        };
 +        v.visit_block(b);
 +        v.res
 +    } else {
 +        ControlFlow::Continue(())
 +    }
 +}
index ee8ab7c1d7cbbc60395caddeb15bc8d3dcf24d58,0000000000000000000000000000000000000000..bd49f096072630309ef3b38ab333429135ef5e6f
mode 100644,000000..100644
--- /dev/null
@@@ -1,829 -1,0 +1,828 @@@
-                 "target/lintcheck/sources/{}-{}/{}",
-                 crate_name, crate_version, span.file_name
 +// Run clippy on a fixed set of crates and collect the warnings.
 +// This helps observing the impact clippy changes have on a set of real-world code (and not just our
 +// testsuite).
 +//
 +// When a new lint is introduced, we can search the results for new warnings and check for false
 +// positives.
 +
 +#![allow(clippy::collapsible_else_if)]
 +
 +mod config;
 +mod driver;
 +mod recursive;
 +
 +use crate::config::LintcheckConfig;
 +use crate::recursive::LintcheckServer;
 +
 +use std::collections::{HashMap, HashSet};
 +use std::env;
 +use std::env::consts::EXE_SUFFIX;
 +use std::fmt::Write as _;
 +use std::fs;
 +use std::io::ErrorKind;
 +use std::path::{Path, PathBuf};
 +use std::process::Command;
 +use std::sync::atomic::{AtomicUsize, Ordering};
 +use std::thread;
 +use std::time::Duration;
 +
 +use cargo_metadata::diagnostic::{Diagnostic, DiagnosticLevel};
 +use cargo_metadata::Message;
 +use rayon::prelude::*;
 +use serde::{Deserialize, Serialize};
 +use walkdir::{DirEntry, WalkDir};
 +
 +const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 +const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 +
 +/// List of sources to check, loaded from a .toml file
 +#[derive(Debug, Serialize, Deserialize)]
 +struct SourceList {
 +    crates: HashMap<String, TomlCrate>,
 +    #[serde(default)]
 +    recursive: RecursiveOptions,
 +}
 +
 +#[derive(Debug, Serialize, Deserialize, Default)]
 +struct RecursiveOptions {
 +    ignore: HashSet<String>,
 +}
 +
 +/// A crate source stored inside the .toml
 +/// will be translated into on one of the `CrateSource` variants
 +#[derive(Debug, Serialize, Deserialize)]
 +struct TomlCrate {
 +    name: String,
 +    versions: Option<Vec<String>>,
 +    git_url: Option<String>,
 +    git_hash: Option<String>,
 +    path: Option<String>,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
 +/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
 +#[derive(Debug, Serialize, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
 +enum CrateSource {
 +    CratesIo {
 +        name: String,
 +        version: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Git {
 +        name: String,
 +        url: String,
 +        commit: String,
 +        options: Option<Vec<String>>,
 +    },
 +    Path {
 +        name: String,
 +        path: PathBuf,
 +        options: Option<Vec<String>>,
 +    },
 +}
 +
 +/// Represents the actual source code of a crate that we ran "cargo clippy" on
 +#[derive(Debug)]
 +struct Crate {
 +    version: String,
 +    name: String,
 +    // path to the extracted sources that clippy can check
 +    path: PathBuf,
 +    options: Option<Vec<String>>,
 +}
 +
 +/// A single warning that clippy issued while checking a `Crate`
 +#[derive(Debug)]
 +struct ClippyWarning {
 +    crate_name: String,
 +    file: String,
 +    line: usize,
 +    column: usize,
 +    lint_type: String,
 +    message: String,
 +    is_ice: bool,
 +}
 +
 +#[allow(unused)]
 +impl ClippyWarning {
 +    fn new(diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option<Self> {
 +        let lint_type = diag.code?.code;
 +        if !(lint_type.contains("clippy") || diag.message.contains("clippy"))
 +            || diag.message.contains("could not read cargo metadata")
 +        {
 +            return None;
 +        }
 +
 +        let span = diag.spans.into_iter().find(|span| span.is_primary)?;
 +
 +        let file = if let Ok(stripped) = Path::new(&span.file_name).strip_prefix(env!("CARGO_HOME")) {
 +            format!("$CARGO_HOME/{}", stripped.display())
 +        } else {
 +            format!(
-                 "{}/{} {}% Linting {} {}",
-                 index, total_crates_to_lint, perc, &self.name, &self.version
++                "target/lintcheck/sources/{crate_name}-{crate_version}/{}",
++                span.file_name
 +            )
 +        };
 +
 +        Some(Self {
 +            crate_name: crate_name.to_owned(),
 +            file,
 +            line: span.line_start,
 +            column: span.column_start,
 +            lint_type,
 +            message: diag.message,
 +            is_ice: diag.level == DiagnosticLevel::Ice,
 +        })
 +    }
 +
 +    fn to_output(&self, markdown: bool) -> String {
 +        let file_with_pos = format!("{}:{}:{}", &self.file, &self.line, &self.column);
 +        if markdown {
 +            let mut file = self.file.clone();
 +            if !file.starts_with('$') {
 +                file.insert_str(0, "../");
 +            }
 +
 +            let mut output = String::from("| ");
 +            let _ = write!(output, "[`{file_with_pos}`]({file}#L{})", self.line);
 +            let _ = write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.message);
 +            output.push('\n');
 +            output
 +        } else {
 +            format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.message)
 +        }
 +    }
 +}
 +
 +#[allow(clippy::result_large_err)]
 +fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
 +    const MAX_RETRIES: u8 = 4;
 +    let mut retries = 0;
 +    loop {
 +        match ureq::get(path).call() {
 +            Ok(res) => return Ok(res),
 +            Err(e) if retries >= MAX_RETRIES => return Err(e),
 +            Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"),
 +            Err(e) => return Err(e),
 +        }
 +        eprintln!("retrying in {retries} seconds...");
 +        thread::sleep(Duration::from_secs(u64::from(retries)));
 +        retries += 1;
 +    }
 +}
 +
 +impl CrateSource {
 +    /// Makes the sources available on the disk for clippy to check.
 +    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
 +    /// copies a local folder
 +    fn download_and_extract(&self) -> Crate {
 +        match self {
 +            CrateSource::CratesIo { name, version, options } => {
 +                let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
 +                let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
 +
 +                // url to download the crate from crates.io
 +                let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download");
 +                println!("Downloading and extracting {name} {version} from {url}");
 +                create_dirs(&krate_download_dir, &extract_dir);
 +
 +                let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz"));
 +                // don't download/extract if we already have done so
 +                if !krate_file_path.is_file() {
 +                    // create a file path to download and write the crate data into
 +                    let mut krate_dest = std::fs::File::create(&krate_file_path).unwrap();
 +                    let mut krate_req = get(&url).unwrap().into_reader();
 +                    // copy the crate into the file
 +                    std::io::copy(&mut krate_req, &mut krate_dest).unwrap();
 +
 +                    // unzip the tarball
 +                    let ungz_tar = flate2::read::GzDecoder::new(std::fs::File::open(&krate_file_path).unwrap());
 +                    // extract the tar archive
 +                    let mut archive = tar::Archive::new(ungz_tar);
 +                    archive.unpack(&extract_dir).expect("Failed to extract!");
 +                }
 +                // crate is extracted, return a new Krate object which contains the path to the extracted
 +                // sources that clippy can check
 +                Crate {
 +                    version: version.clone(),
 +                    name: name.clone(),
 +                    path: extract_dir.join(format!("{name}-{version}/")),
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Git {
 +                name,
 +                url,
 +                commit,
 +                options,
 +            } => {
 +                let repo_path = {
 +                    let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
 +                    // add a -git suffix in case we have the same crate from crates.io and a git repo
 +                    repo_path.push(format!("{name}-git"));
 +                    repo_path
 +                };
 +                // clone the repo if we have not done so
 +                if !repo_path.is_dir() {
 +                    println!("Cloning {url} and checking out {commit}");
 +                    if !Command::new("git")
 +                        .arg("clone")
 +                        .arg(url)
 +                        .arg(&repo_path)
 +                        .status()
 +                        .expect("Failed to clone git repo!")
 +                        .success()
 +                    {
 +                        eprintln!("Failed to clone {url} into {}", repo_path.display());
 +                    }
 +                }
 +                // check out the commit/branch/whatever
 +                if !Command::new("git")
 +                    .args(["-c", "advice.detachedHead=false"])
 +                    .arg("checkout")
 +                    .arg(commit)
 +                    .current_dir(&repo_path)
 +                    .status()
 +                    .expect("Failed to check out commit")
 +                    .success()
 +                {
 +                    eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display());
 +                }
 +
 +                Crate {
 +                    version: commit.clone(),
 +                    name: name.clone(),
 +                    path: repo_path,
 +                    options: options.clone(),
 +                }
 +            },
 +            CrateSource::Path { name, path, options } => {
 +                fn is_cache_dir(entry: &DirEntry) -> bool {
 +                    std::fs::read(entry.path().join("CACHEDIR.TAG"))
 +                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
 +                        .unwrap_or(false)
 +                }
 +
 +                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
 +                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
 +                // as a result of this filter.
 +                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
 +                if dest_crate_root.exists() {
 +                    println!("Deleting existing directory at {dest_crate_root:?}");
 +                    std::fs::remove_dir_all(&dest_crate_root).unwrap();
 +                }
 +
 +                println!("Copying {path:?} to {dest_crate_root:?}");
 +
 +                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
 +                    let entry = entry.unwrap();
 +                    let entry_path = entry.path();
 +                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
 +                    let dest_path = dest_crate_root.join(relative_entry_path);
 +                    let metadata = entry_path.symlink_metadata().unwrap();
 +
 +                    if metadata.is_dir() {
 +                        std::fs::create_dir(dest_path).unwrap();
 +                    } else if metadata.is_file() {
 +                        std::fs::copy(entry_path, dest_path).unwrap();
 +                    }
 +                }
 +
 +                Crate {
 +                    version: String::from("local"),
 +                    name: name.clone(),
 +                    path: dest_crate_root,
 +                    options: options.clone(),
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +impl Crate {
 +    /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
 +    /// issued
 +    #[allow(clippy::too_many_arguments)]
 +    fn run_clippy_lints(
 +        &self,
 +        cargo_clippy_path: &Path,
 +        clippy_driver_path: &Path,
 +        target_dir_index: &AtomicUsize,
 +        total_crates_to_lint: usize,
 +        config: &LintcheckConfig,
 +        lint_filter: &Vec<String>,
 +        server: &Option<LintcheckServer>,
 +    ) -> Vec<ClippyWarning> {
 +        // advance the atomic index by one
 +        let index = target_dir_index.fetch_add(1, Ordering::SeqCst);
 +        // "loop" the index within 0..thread_limit
 +        let thread_index = index % config.max_jobs;
 +        let perc = (index * 100) / total_crates_to_lint;
 +
 +        if config.max_jobs == 1 {
 +            println!(
-                 "{}/{} {}% Linting {} {} in target dir {:?}",
-                 index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
++                "{index}/{total_crates_to_lint} {perc}% Linting {} {}",
++                &self.name, &self.version
 +            );
 +        } else {
 +            println!(
-                     "Encountered error:\n{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
-                     error,
++                "{index}/{total_crates_to_lint} {perc}% Linting {} {} in target dir {thread_index:?}",
++                &self.name, &self.version
 +            );
 +        }
 +
 +        let cargo_clippy_path = std::fs::canonicalize(cargo_clippy_path).unwrap();
 +
 +        let shared_target_dir = clippy_project_root().join("target/lintcheck/shared_target_dir");
 +
 +        let mut cargo_clippy_args = if config.fix {
 +            vec!["--fix", "--"]
 +        } else {
 +            vec!["--", "--message-format=json", "--"]
 +        };
 +
 +        let mut clippy_args = Vec::<&str>::new();
 +        if let Some(options) = &self.options {
 +            for opt in options {
 +                clippy_args.push(opt);
 +            }
 +        } else {
 +            clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]);
 +        }
 +
 +        if lint_filter.is_empty() {
 +            clippy_args.push("--cap-lints=warn");
 +        } else {
 +            clippy_args.push("--cap-lints=allow");
 +            clippy_args.extend(lint_filter.iter().map(std::string::String::as_str));
 +        }
 +
 +        if let Some(server) = server {
 +            let target = shared_target_dir.join("recursive");
 +
 +            // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to
 +            // `clippy-driver`. We do the same thing here with a couple changes:
 +            //
 +            // `RUSTC_WRAPPER` is used instead of `RUSTC_WORKSPACE_WRAPPER` so that we can lint all crate
 +            // dependencies rather than only workspace members
 +            //
 +            // The wrapper is set to the `lintcheck` so we can force enable linting and ignore certain crates
 +            // (see `crate::driver`)
 +            let status = Command::new("cargo")
 +                .arg("check")
 +                .arg("--quiet")
 +                .current_dir(&self.path)
 +                .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__"))
 +                .env("CARGO_TARGET_DIR", target)
 +                .env("RUSTC_WRAPPER", env::current_exe().unwrap())
 +                // Pass the absolute path so `crate::driver` can find `clippy-driver`, as it's executed in various
 +                // different working directories
 +                .env("CLIPPY_DRIVER", clippy_driver_path)
 +                .env("LINTCHECK_SERVER", server.local_addr.to_string())
 +                .status()
 +                .expect("failed to run cargo");
 +
 +            assert_eq!(status.code(), Some(0));
 +
 +            return Vec::new();
 +        }
 +
 +        cargo_clippy_args.extend(clippy_args);
 +
 +        let all_output = Command::new(&cargo_clippy_path)
 +            // use the looping index to create individual target dirs
 +            .env("CARGO_TARGET_DIR", shared_target_dir.join(format!("_{thread_index:?}")))
 +            .args(&cargo_clippy_args)
 +            .current_dir(&self.path)
 +            .output()
 +            .unwrap_or_else(|error| {
 +                panic!(
++                    "Encountered error:\n{error:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
 +                    &cargo_clippy_path.display(),
 +                    &self.path.display()
 +                );
 +            });
 +        let stdout = String::from_utf8_lossy(&all_output.stdout);
 +        let stderr = String::from_utf8_lossy(&all_output.stderr);
 +        let status = &all_output.status;
 +
 +        if !status.success() {
 +            eprintln!(
 +                "\nWARNING: bad exit status after checking {} {} \n",
 +                self.name, self.version
 +            );
 +        }
 +
 +        if config.fix {
 +            if let Some(stderr) = stderr
 +                .lines()
 +                .find(|line| line.contains("failed to automatically apply fixes suggested by rustc to crate"))
 +            {
 +                let subcrate = &stderr[63..];
 +                println!(
 +                    "ERROR: failed to apply some suggetion to {} / to (sub)crate {subcrate}",
 +                    self.name
 +                );
 +            }
 +            // fast path, we don't need the warnings anyway
 +            return Vec::new();
 +        }
 +
 +        // get all clippy warnings and ICEs
 +        let warnings: Vec<ClippyWarning> = Message::parse_stream(stdout.as_bytes())
 +            .filter_map(|msg| match msg {
 +                Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
 +                _ => None,
 +            })
 +            .collect();
 +
 +        warnings
 +    }
 +}
 +
 +/// Builds clippy inside the repo to make sure we have a clippy executable we can use.
 +fn build_clippy() {
 +    let status = Command::new("cargo")
 +        .arg("build")
 +        .status()
 +        .expect("Failed to build clippy!");
 +    if !status.success() {
 +        eprintln!("Error: Failed to compile Clippy!");
 +        std::process::exit(1);
 +    }
 +}
 +
 +/// Read a `lintcheck_crates.toml` file
 +fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
 +    let toml_content: String =
 +        std::fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
 +    let crate_list: SourceList =
 +        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display()));
 +    // parse the hashmap of the toml file into a list of crates
 +    let tomlcrates: Vec<TomlCrate> = crate_list.crates.into_values().collect();
 +
 +    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
 +    // multiple Cratesources)
 +    let mut crate_sources = Vec::new();
 +    for tk in tomlcrates {
 +        if let Some(ref path) = tk.path {
 +            crate_sources.push(CrateSource::Path {
 +                name: tk.name.clone(),
 +                path: PathBuf::from(path),
 +                options: tk.options.clone(),
 +            });
 +        } else if let Some(ref versions) = tk.versions {
 +            // if we have multiple versions, save each one
 +            for ver in versions.iter() {
 +                crate_sources.push(CrateSource::CratesIo {
 +                    name: tk.name.clone(),
 +                    version: ver.to_string(),
 +                    options: tk.options.clone(),
 +                });
 +            }
 +        } else if tk.git_url.is_some() && tk.git_hash.is_some() {
 +            // otherwise, we should have a git source
 +            crate_sources.push(CrateSource::Git {
 +                name: tk.name.clone(),
 +                url: tk.git_url.clone().unwrap(),
 +                commit: tk.git_hash.clone().unwrap(),
 +                options: tk.options.clone(),
 +            });
 +        } else {
 +            panic!("Invalid crate source: {tk:?}");
 +        }
 +
 +        // if we have a version as well as a git data OR only one git data, something is funky
 +        if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
 +            || tk.git_hash.is_some() != tk.git_url.is_some()
 +        {
 +            eprintln!("tomlkrate: {tk:?}");
 +            assert_eq!(
 +                tk.git_hash.is_some(),
 +                tk.git_url.is_some(),
 +                "Error: Encountered TomlCrate with only one of git_hash and git_url!"
 +            );
 +            assert!(
 +                tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()),
 +                "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"
 +            );
 +            unreachable!("Failed to translate TomlCrate into CrateSource!");
 +        }
 +    }
 +    // sort the crates
 +    crate_sources.sort();
 +
 +    (crate_sources, crate_list.recursive)
 +}
 +
 +/// Generate a short list of occurring lints-types and their count
 +fn gather_stats(clippy_warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
 +    // count lint type occurrences
 +    let mut counter: HashMap<&String, usize> = HashMap::new();
 +    clippy_warnings
 +        .iter()
 +        .for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1);
 +
 +    // collect into a tupled list for sorting
 +    let mut stats: Vec<(&&String, &usize)> = counter.iter().map(|(lint, count)| (lint, count)).collect();
 +    // sort by "000{count} {clippy::lintname}"
 +    // to not have a lint with 200 and 2 warnings take the same spot
 +    stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}"));
 +
 +    let mut header = String::from("| lint                                               | count |\n");
 +    header.push_str("| -------------------------------------------------- | ----- |\n");
 +    let stats_string = stats
 +        .iter()
 +        .map(|(lint, count)| format!("| {lint:<50} |  {count:>4} |\n"))
 +        .fold(header, |mut table, line| {
 +            table.push_str(&line);
 +            table
 +        });
 +
 +    (stats_string, counter)
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn main() {
 +    // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive`
 +    if let Ok(addr) = env::var("LINTCHECK_SERVER") {
 +        driver::drive(&addr);
 +    }
 +
 +    // assert that we launch lintcheck from the repo root (via cargo lintcheck)
 +    if std::fs::metadata("lintcheck/Cargo.toml").is_err() {
 +        eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively.");
 +        std::process::exit(3);
 +    }
 +
 +    let config = LintcheckConfig::new();
 +
 +    println!("Compiling clippy...");
 +    build_clippy();
 +    println!("Done compiling");
 +
 +    let cargo_clippy_path = fs::canonicalize(format!("target/debug/cargo-clippy{EXE_SUFFIX}")).unwrap();
 +    let clippy_driver_path = fs::canonicalize(format!("target/debug/clippy-driver{EXE_SUFFIX}")).unwrap();
 +
 +    // assert that clippy is found
 +    assert!(
 +        cargo_clippy_path.is_file(),
 +        "target/debug/cargo-clippy binary not found! {}",
 +        cargo_clippy_path.display()
 +    );
 +
 +    let clippy_ver = std::process::Command::new(&cargo_clippy_path)
 +        .arg("--version")
 +        .output()
 +        .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
 +        .expect("could not get clippy version!");
 +
 +    // download and extract the crates, then run clippy on them and collect clippy's warnings
 +    // flatten into one big list of warnings
 +
 +    let (crates, recursive_options) = read_crates(&config.sources_toml_path);
 +    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
 +
 +    let counter = AtomicUsize::new(1);
 +    let lint_filter: Vec<String> = config
 +        .lint_filter
 +        .iter()
 +        .map(|filter| {
 +            let mut filter = filter.clone();
 +            filter.insert_str(0, "--force-warn=");
 +            filter
 +        })
 +        .collect();
 +
 +    let crates: Vec<Crate> = crates
 +        .into_iter()
 +        .filter(|krate| {
 +            if let Some(only_one_crate) = &config.only {
 +                let name = match krate {
 +                    CrateSource::CratesIo { name, .. }
 +                    | CrateSource::Git { name, .. }
 +                    | CrateSource::Path { name, .. } => name,
 +                };
 +
 +                name == only_one_crate
 +            } else {
 +                true
 +            }
 +        })
 +        .map(|krate| krate.download_and_extract())
 +        .collect();
 +
 +    if crates.is_empty() {
 +        eprintln!(
 +            "ERROR: could not find crate '{}' in lintcheck/lintcheck_crates.toml",
 +            config.only.unwrap(),
 +        );
 +        std::process::exit(1);
 +    }
 +
 +    // run parallel with rayon
 +
 +    // This helps when we check many small crates with dep-trees that don't have a lot of branches in
 +    // order to achieve some kind of parallelism
 +
 +    rayon::ThreadPoolBuilder::new()
 +        .num_threads(config.max_jobs)
 +        .build_global()
 +        .unwrap();
 +
 +    let server = config.recursive.then(|| {
 +        let _ = fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive");
 +
 +        LintcheckServer::spawn(recursive_options)
 +    });
 +
 +    let mut clippy_warnings: Vec<ClippyWarning> = crates
 +        .par_iter()
 +        .flat_map(|krate| {
 +            krate.run_clippy_lints(
 +                &cargo_clippy_path,
 +                &clippy_driver_path,
 +                &counter,
 +                crates.len(),
 +                &config,
 +                &lint_filter,
 +                &server,
 +            )
 +        })
 +        .collect();
 +
 +    if let Some(server) = server {
 +        clippy_warnings.extend(server.warnings());
 +    }
 +
 +    // if we are in --fix mode, don't change the log files, terminate here
 +    if config.fix {
 +        return;
 +    }
 +
 +    // generate some stats
 +    let (stats_formatted, new_stats) = gather_stats(&clippy_warnings);
 +
 +    // grab crashes/ICEs, save the crate name and the ice message
 +    let ices: Vec<(&String, &String)> = clippy_warnings
 +        .iter()
 +        .filter(|warning| warning.is_ice)
 +        .map(|w| (&w.crate_name, &w.message))
 +        .collect();
 +
 +    let mut all_msgs: Vec<String> = clippy_warnings
 +        .iter()
 +        .map(|warn| warn.to_output(config.markdown))
 +        .collect();
 +    all_msgs.sort();
 +    all_msgs.push("\n\n### Stats:\n\n".into());
 +    all_msgs.push(stats_formatted);
 +
 +    // save the text into lintcheck-logs/logs.txt
 +    let mut text = clippy_ver; // clippy version number on top
 +    text.push_str("\n### Reports\n\n");
 +    if config.markdown {
 +        text.push_str("| file | lint | message |\n");
 +        text.push_str("| --- | --- | --- |\n");
 +    }
 +    write!(text, "{}", all_msgs.join("")).unwrap();
 +    text.push_str("\n\n### ICEs:\n");
 +    for (cratename, msg) in &ices {
 +        let _ = write!(text, "{cratename}: '{msg}'");
 +    }
 +
 +    println!("Writing logs to {}", config.lintcheck_results_path.display());
 +    fs::create_dir_all(config.lintcheck_results_path.parent().unwrap()).unwrap();
 +    fs::write(&config.lintcheck_results_path, text).unwrap();
 +
 +    print_stats(old_stats, new_stats, &config.lint_filter);
 +}
 +
 +/// read the previous stats from the lintcheck-log file
 +fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
 +    let file_content: String = match std::fs::read_to_string(file_path).ok() {
 +        Some(content) => content,
 +        None => {
 +            return HashMap::new();
 +        },
 +    };
 +
 +    let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
 +
 +    lines
 +        .iter()
 +        .skip_while(|line| line.as_str() != "### Stats:")
 +        // Skipping the table header and the `Stats:` label
 +        .skip(4)
 +        .take_while(|line| line.starts_with("| "))
 +        .filter_map(|line| {
 +            let mut spl = line.split('|');
 +            // Skip the first `|` symbol
 +            spl.next();
 +            if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
 +                Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
 +            } else {
 +                None
 +            }
 +        })
 +        .collect::<HashMap<String, usize>>()
 +}
 +
 +/// print how lint counts changed between runs
 +fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>, lint_filter: &Vec<String>) {
 +    let same_in_both_hashmaps = old_stats
 +        .iter()
 +        .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val))
 +        .map(|(k, v)| (k.to_string(), *v))
 +        .collect::<Vec<(String, usize)>>();
 +
 +    let mut old_stats_deduped = old_stats;
 +    let mut new_stats_deduped = new_stats;
 +
 +    // remove duplicates from both hashmaps
 +    for (k, v) in &same_in_both_hashmaps {
 +        assert!(old_stats_deduped.remove(k) == Some(*v));
 +        assert!(new_stats_deduped.remove(k) == Some(*v));
 +    }
 +
 +    println!("\nStats:");
 +
 +    // list all new counts  (key is in new stats but not in old stats)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _)| old_stats_deduped.get::<str>(new_key).is_none())
 +        .for_each(|(new_key, new_value)| {
 +            println!("{new_key} 0 => {new_value}");
 +        });
 +
 +    // list all changed counts (key is in both maps but value differs)
 +    new_stats_deduped
 +        .iter()
 +        .filter(|(new_key, _new_val)| old_stats_deduped.get::<str>(new_key).is_some())
 +        .for_each(|(new_key, new_val)| {
 +            let old_val = old_stats_deduped.get::<str>(new_key).unwrap();
 +            println!("{new_key} {old_val} => {new_val}");
 +        });
 +
 +    // list all gone counts (key is in old status but not in new stats)
 +    old_stats_deduped
 +        .iter()
 +        .filter(|(old_key, _)| new_stats_deduped.get::<&String>(old_key).is_none())
 +        .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key))
 +        .for_each(|(old_key, old_value)| {
 +            println!("{old_key} {old_value} => 0");
 +        });
 +}
 +
 +/// Create necessary directories to run the lintcheck tool.
 +///
 +/// # Panics
 +///
 +/// This function panics if creating one of the dirs fails.
 +fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
 +    std::fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
 +        assert_eq!(
 +            err.kind(),
 +            ErrorKind::AlreadyExists,
 +            "cannot create lintcheck target dir"
 +        );
 +    });
 +    std::fs::create_dir(krate_download_dir).unwrap_or_else(|err| {
 +        assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir");
 +    });
 +    std::fs::create_dir(extract_dir).unwrap_or_else(|err| {
 +        assert_eq!(
 +            err.kind(),
 +            ErrorKind::AlreadyExists,
 +            "cannot create crate extraction dir"
 +        );
 +    });
 +}
 +
 +/// Returns the path to the Clippy project directory
 +#[must_use]
 +fn clippy_project_root() -> &'static Path {
 +    Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap()
 +}
 +
 +#[test]
 +fn lintcheck_test() {
 +    let args = [
 +        "run",
 +        "--target-dir",
 +        "lintcheck/target",
 +        "--manifest-path",
 +        "./lintcheck/Cargo.toml",
 +        "--",
 +        "--crates-toml",
 +        "lintcheck/test_sources.toml",
 +    ];
 +    let status = std::process::Command::new("cargo")
 +        .args(args)
 +        .current_dir("..") // repo root
 +        .status();
 +    //.output();
 +
 +    assert!(status.unwrap().success());
 +}
index a806c1564796ae2a5bb3b5fd5b0fde0659e17d9e,0000000000000000000000000000000000000000..19fee38db46e642eb18f600b1a3b7d8b6e8a814f
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-11-21"
 +[toolchain]
++channel = "nightly-2022-12-01"
 +components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index ad6132a49baaec685db1d7b62033b7c820b950d3,0000000000000000000000000000000000000000..9ec4df8e651b1bf3f7756656f8548f5c2ec8e9c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,333 -1,0 +1,338 @@@
 +#![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)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_interface;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +
 +use rustc_interface::interface;
 +use rustc_session::parse::ParseSess;
 +use rustc_span::symbol::Symbol;
 +use rustc_tools_util::VersionInfo;
 +
 +use std::borrow::Cow;
 +use std::env;
 +use std::ops::Deref;
 +use std::panic;
 +use std::path::Path;
 +use std::process::exit;
 +use std::sync::LazyLock;
 +
 +/// If a command-line option matches `find_arg`, then apply the predicate `pred` on its value. If
 +/// true, then return it. The parameter is assumed to be either `--arg=value` or `--arg value`.
 +fn arg_value<'a, T: Deref<Target = str>>(
 +    args: &'a [T],
 +    find_arg: &str,
 +    pred: impl Fn(&str) -> bool,
 +) -> Option<&'a str> {
 +    let mut args = args.iter().map(Deref::deref);
 +    while let Some(arg) = args.next() {
 +        let mut arg = arg.splitn(2, '=');
 +        if arg.next() != Some(find_arg) {
 +            continue;
 +        }
 +
 +        match arg.next().or_else(|| args.next()) {
 +            Some(v) if pred(v) => return Some(v),
 +            _ => {},
 +        }
 +    }
 +    None
 +}
 +
 +#[test]
 +fn test_arg_value() {
 +    let args = &["--bar=bar", "--foobar", "123", "--foo"];
 +
 +    assert_eq!(arg_value(&[] as &[&str], "--foobar", |_| true), None);
 +    assert_eq!(arg_value(args, "--bar", |_| false), None);
 +    assert_eq!(arg_value(args, "--bar", |_| true), Some("bar"));
 +    assert_eq!(arg_value(args, "--bar", |p| p == "bar"), Some("bar"));
 +    assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
 +    assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
 +    assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
 +    assert_eq!(arg_value(args, "--foobar", |p| p.contains("12")), Some("123"));
 +    assert_eq!(arg_value(args, "--foo", |_| true), None);
 +}
 +
 +fn track_clippy_args(parse_sess: &mut ParseSess, args_env_var: &Option<String>) {
 +    parse_sess.env_depinfo.get_mut().insert((
 +        Symbol::intern("CLIPPY_ARGS"),
 +        args_env_var.as_deref().map(Symbol::intern),
 +    ));
 +}
 +
 +/// Track files that may be accessed at runtime in `file_depinfo` so that cargo will re-run clippy
 +/// when any of them are modified
 +fn track_files(parse_sess: &mut ParseSess, conf_path_string: Option<String>) {
 +    let file_depinfo = parse_sess.file_depinfo.get_mut();
 +
 +    // Used by `clippy::cargo` lints and to determine the MSRV. `cargo clippy` executes `clippy-driver`
 +    // with the current directory set to `CARGO_MANIFEST_DIR` so a relative path is fine
 +    if Path::new("Cargo.toml").exists() {
 +        file_depinfo.insert(Symbol::intern("Cargo.toml"));
 +    }
 +
 +    // `clippy.toml`
 +    if let Some(path) = conf_path_string {
 +        file_depinfo.insert(Symbol::intern(&path));
 +    }
 +
 +    // 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()
 +        {
 +            file_depinfo.insert(Symbol::intern(current_exe));
 +        }
 +    }
 +}
 +
 +struct DefaultCallbacks;
 +impl rustc_driver::Callbacks for DefaultCallbacks {}
 +
 +/// This is different from `DefaultCallbacks` that it will inform Cargo to track the value of
 +/// `CLIPPY_ARGS` environment variable.
 +struct RustcCallbacks {
 +    clippy_args_var: Option<String>,
 +}
 +
 +impl rustc_driver::Callbacks for RustcCallbacks {
 +    fn config(&mut self, config: &mut interface::Config) {
 +        let clippy_args_var = self.clippy_args_var.take();
 +        config.parse_sess_created = Some(Box::new(move |parse_sess| {
 +            track_clippy_args(parse_sess, &clippy_args_var);
 +        }));
 +    }
 +}
 +
 +struct ClippyCallbacks {
 +    clippy_args_var: Option<String>,
 +}
 +
 +impl rustc_driver::Callbacks for ClippyCallbacks {
 +    // JUSTIFICATION: necessary in clippy driver to set `mir_opt_level`
 +    #[allow(rustc::bad_opt_access)]
 +    fn config(&mut self, config: &mut interface::Config) {
 +        let conf_path = clippy_lints::lookup_conf_file();
 +        let conf_path_string = if let Ok(Some(path)) = &conf_path {
 +            path.to_str().map(String::from)
 +        } else {
 +            None
 +        };
 +
 +        let previous = config.register_lints.take();
 +        let clippy_args_var = self.clippy_args_var.take();
 +        config.parse_sess_created = Some(Box::new(move |parse_sess| {
 +            track_clippy_args(parse_sess, &clippy_args_var);
 +            track_files(parse_sess, conf_path_string);
 +        }));
 +        config.register_lints = Some(Box::new(move |sess, lint_store| {
 +            // technically we're ~guaranteed that this is none but might as well call anything that
 +            // is there already. Certainly it can't hurt.
 +            if let Some(previous) = &previous {
 +                (previous)(sess, lint_store);
 +            }
 +
 +            let conf = clippy_lints::read_conf(sess, &conf_path);
 +            clippy_lints::register_plugins(lint_store, sess, &conf);
 +            clippy_lints::register_pre_expansion_lints(lint_store, sess, &conf);
 +            clippy_lints::register_renamed(lint_store);
 +        }));
 +
 +        // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
 +        // run on the unoptimized MIR. On the other hand this results in some false negatives. If
 +        // MIR passes can be enabled / disabled separately, we should figure out, what passes to
 +        // use for Clippy.
 +        config.opts.unstable_opts.mir_opt_level = Some(0);
 +    }
 +}
 +
 +fn display_help() {
 +    println!(
 +        "\
 +Checks a package to catch common mistakes and improve your Rust code.
 +
 +Usage:
 +    cargo clippy [options] [--] [<opts>...]
 +
 +Common options:
 +    -h, --help               Print this message
 +        --rustc              Pass all args to rustc
 +    -V, --version            Print version info and exit
 +
 +Other options are the same as `cargo check`.
 +
 +To allow or deny a lint from the command line you can use `cargo clippy --`
 +with:
 +
 +    -W --warn OPT       Set lint warnings
 +    -A --allow OPT      Set lint allowed
 +    -D --deny OPT       Set lint denied
 +    -F --forbid OPT     Set lint forbidden
 +
 +You can use tool lints to allow or deny lints from your code, eg.:
 +
 +    #[allow(clippy::needless_lifetimes)]
 +"
 +    );
 +}
 +
 +const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
 +
 +type PanicCallback = dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static;
 +static ICE_HOOK: LazyLock<Box<PanicCallback>> = LazyLock::new(|| {
 +    let hook = panic::take_hook();
 +    panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
 +    hook
 +});
 +
 +fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
 +    // Invoke our ICE handler, which prints the actual panic message and optionally a backtrace
 +    (*ICE_HOOK)(info);
 +
 +    // Separate the output with an empty line
 +    eprintln!();
 +
 +    let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
 +    let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
 +        rustc_errors::ColorConfig::Auto,
 +        None,
 +        None,
 +        fallback_bundle,
 +        false,
 +        false,
 +        None,
 +        false,
 +        false,
 +    ));
 +    let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
 +
 +    // a .span_bug or .bug call has already printed what
 +    // it wants to print.
 +    if !info.payload().is::<rustc_errors::ExplicitBug>() {
 +        let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
 +        handler.emit_diagnostic(&mut d);
 +    }
 +
 +    let version_info = rustc_tools_util::get_version_info!();
 +
 +    let xs: Vec<Cow<'static, str>> = vec![
 +        "the compiler unexpectedly panicked. this is a bug.".into(),
 +        format!("we would appreciate a bug report: {bug_report_url}").into(),
 +        format!("Clippy version: {version_info}").into(),
 +    ];
 +
 +    for note in &xs {
 +        handler.note_without_error(note.as_ref());
 +    }
 +
 +    // If backtraces are enabled, also print the query stack
 +    let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
 +
 +    let num_frames = if backtrace { None } else { Some(2) };
 +
 +    interface::try_print_query_stack(&handler, num_frames);
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +pub fn main() {
 +    rustc_driver::init_rustc_env_logger();
 +    LazyLock::force(&ICE_HOOK);
 +    exit(rustc_driver::catch_with_exit_code(move || {
 +        let mut orig_args: Vec<String> = env::args().collect();
 +
 +        let sys_root_env = std::env::var("SYSROOT").ok();
 +        let pass_sysroot_env_if_given = |args: &mut Vec<String>, sys_root_env| {
 +            if let Some(sys_root) = sys_root_env {
 +                args.extend(vec!["--sysroot".into(), sys_root]);
 +            };
 +        };
 +
 +        // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc"
 +        // for example `clippy-driver --rustc --version` will print the rustc version that clippy-driver
 +        // uses
 +        if let Some(pos) = orig_args.iter().position(|arg| arg == "--rustc") {
 +            orig_args.remove(pos);
 +            orig_args[0] = "rustc".to_string();
 +
 +            let mut args: Vec<String> = orig_args.clone();
 +            pass_sysroot_env_if_given(&mut args, sys_root_env);
 +
 +            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
 +        }
 +
 +        if orig_args.iter().any(|a| a == "--version" || a == "-V") {
 +            let version_info = rustc_tools_util::get_version_info!();
 +            println!("{version_info}");
 +            exit(0);
 +        }
 +
 +        // Setting RUSTC_WRAPPER causes Cargo to pass 'rustc' as the first argument.
 +        // We're invoking the compiler programmatically, so we ignore this/
 +        let wrapper_mode = orig_args.get(1).map(Path::new).and_then(Path::file_stem) == Some("rustc".as_ref());
 +
 +        if wrapper_mode {
 +            // we still want to be able to invoke it normally though
 +            orig_args.remove(1);
 +        }
 +
 +        if !wrapper_mode && (orig_args.iter().any(|a| a == "--help" || a == "-h") || orig_args.len() == 1) {
 +            display_help();
 +            exit(0);
 +        }
 +
 +        let mut args: Vec<String> = orig_args.clone();
 +        pass_sysroot_env_if_given(&mut args, sys_root_env);
 +
 +        let mut no_deps = false;
 +        let clippy_args_var = env::var("CLIPPY_ARGS").ok();
 +        let clippy_args = clippy_args_var
 +            .as_deref()
 +            .unwrap_or_default()
 +            .split("__CLIPPY_HACKERY__")
 +            .filter_map(|s| match s {
 +                "" => None,
 +                "--no-deps" => {
 +                    no_deps = true;
 +                    None
 +                },
 +                _ => Some(s.to_string()),
 +            })
 +            .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()])
 +            .collect::<Vec<String>>();
 +
 +        // We enable Clippy if one of the following conditions is met
 +        // - IF Clippy is run on its test suite OR
 +        // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
 +        //    - IF `--no-deps` is not set (`!no_deps`) OR
 +        //    - IF `--no-deps` is set and Clippy is run on the specified primary package
 +        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some()
 +            && arg_value(&orig_args, "--force-warn", |val| val.contains("clippy::")).is_none();
 +        let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
 +
 +        let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package);
 +        if clippy_enabled {
 +            args.extend(clippy_args);
 +            rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
 +        } else {
 +            rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
 +        }
 +    }))
 +}
index 900a8fffd4080cc9a6894c502b2b968ef0f1ff5a,0000000000000000000000000000000000000000..08634063a5754a775c385ff4ce4e03b48f9a556f
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,40 @@@
- use rustc_semver::RustcVersion;
 +// run-rustfix
 +
 +#![deny(clippy::internal)]
 +#![allow(clippy::missing_clippy_version_attribute)]
 +#![feature(rustc_private)]
 +
 +extern crate rustc_ast;
 +extern crate rustc_hir;
 +extern crate rustc_lint;
 +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};
-     msrv: Option<RustcVersion>,
 +
 +declare_lint! {
 +    pub TEST_LINT,
 +    Warn,
 +    ""
 +}
 +
 +struct Pass {
++    msrv: Msrv,
 +}
 +
 +impl_lint_pass!(Pass => [TEST_LINT]);
 +
 +impl LateLintPass<'_> for Pass {
 +    extract_msrv_attr!(LateContext);
 +    fn check_expr(&mut self, _: &LateContext<'_>, _: &Expr<'_>) {}
 +}
 +
 +impl EarlyLintPass for Pass {
 +    extract_msrv_attr!(EarlyContext);
 +    fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {}
 +}
 +
 +fn main() {}
index 4bc8164db67b016513dfcba17210d10403894382,0000000000000000000000000000000000000000..f8af77e6d395c05201895d362eedb232a7f08108
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,38 @@@
- use rustc_semver::RustcVersion;
 +// run-rustfix
 +
 +#![deny(clippy::internal)]
 +#![allow(clippy::missing_clippy_version_attribute)]
 +#![feature(rustc_private)]
 +
 +extern crate rustc_ast;
 +extern crate rustc_hir;
 +extern crate rustc_lint;
 +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};
-     msrv: Option<RustcVersion>,
 +
 +declare_lint! {
 +    pub TEST_LINT,
 +    Warn,
 +    ""
 +}
 +
 +struct Pass {
++    msrv: Msrv,
 +}
 +
 +impl_lint_pass!(Pass => [TEST_LINT]);
 +
 +impl LateLintPass<'_> for Pass {
 +    fn check_expr(&mut self, _: &LateContext<'_>, _: &Expr<'_>) {}
 +}
 +
 +impl EarlyLintPass for Pass {
 +    fn check_expr(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Expr) {}
 +}
 +
 +fn main() {}
index 8bfc060e991064d98905105f93fa9a398a709351,0000000000000000000000000000000000000000..2a240cc249b0c768f084dab5b6f220ae53a59399
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
- 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
 +   |
 +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
 +   |
 +LL |     const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "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
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b95e806aae24fa347a6d5e9c63b96e6c4c87d6da
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++allow-mixed-uninlined-format-args = false
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aa8b45b5fe7d428bbabd2af16bf8e104aa34b385
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad2e4863ee8ed3ee5dfa4e05e10889e80ace2a42
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ee941762196151209d43f978516dc2024801d0ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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
++
index f91d285c2e0eefcc3d06aea471a6f5b59a14caa2,0000000000000000000000000000000000000000..01a5e962c9491ea0d52cd89707f4c1d3c3ec4e74
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,52 @@@
 +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
 +           arithmetic-side-effects-allowed
 +           array-size-threshold
 +           avoid-breaking-exported-api
 +           await-holding-invalid-types
 +           blacklisted-names
 +           cargo-ignore-publish
 +           cognitive-complexity-threshold
 +           cyclomatic-complexity-threshold
 +           disallowed-macros
 +           disallowed-methods
 +           disallowed-names
 +           disallowed-types
 +           doc-valid-idents
 +           enable-raw-pointer-heuristic-for-send
 +           enforced-import-renames
 +           enum-variant-name-threshold
 +           enum-variant-size-threshold
 +           ignore-interior-mutability
 +           large-error-threshold
 +           literal-representation-threshold
 +           matches-for-let-else
 +           max-fn-params-bools
 +           max-include-file-size
 +           max-struct-bools
 +           max-suggested-slice-pattern-length
 +           max-trait-bounds
 +           msrv
 +           pass-by-value-size-limit
 +           single-char-binding-names-threshold
 +           standard-macro-braces
 +           third-party
 +           too-large-for-stack
 +           too-many-arguments-threshold
 +           too-many-lines-threshold
 +           trivial-copy-size-limit
 +           type-complexity-threshold
 +           unreadable-literal-lint-fractions
 +           upper-case-acronyms-aggressive
 +           vec-box-size-threshold
 +           verbose-bit-mask-threshold
 +           warn-on-all-wildcard-imports
 +       at line 5 column 1
 +
 +error: aborting due to previous error
 +
index 079b7c000dce1e7f953418a356fe2f538d44ad61,0000000000000000000000000000000000000000..adcbd4d5134d340180d994835720ee39ec11a9af
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,79 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// edition:2018
 +// aux-build:macro_rules.rs
 +
-     #![clippy::msrv = "1.25"]
 +#![feature(exclusive_range_pattern)]
 +#![feature(stmt_expr_attributes)]
 +#![warn(clippy::almost_complete_letter_range)]
 +#![allow(ellipsis_inclusive_range_patterns)]
 +#![allow(clippy::needless_parens_on_range_literals)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +macro_rules! a {
 +    () => {
 +        'a'
 +    };
 +}
 +
 +macro_rules! b {
 +    () => {
 +        let _ = 'a'..='z';
 +    };
 +}
 +
 +fn main() {
 +    #[rustfmt::skip]
 +    {
 +        let _ = ('a') ..='z';
 +        let _ = 'A' ..= ('Z');
 +    }
 +
 +    let _ = 'b'..'z';
 +    let _ = 'B'..'Z';
 +
 +    let _ = (b'a')..=(b'z');
 +    let _ = b'A'..=b'Z';
 +
 +    let _ = b'b'..b'z';
 +    let _ = b'B'..b'Z';
 +
 +    let _ = a!()..='z';
 +
 +    let _ = match 0u8 {
 +        b'a'..=b'z' if true => 1,
 +        b'A'..=b'Z' if true => 2,
 +        b'b'..b'z' => 3,
 +        b'B'..b'Z' => 4,
 +        _ => 5,
 +    };
 +
 +    let _ = match 'x' {
 +        'a'..='z' if true => 1,
 +        'A'..='Z' if true => 2,
 +        'b'..'z' => 3,
 +        'B'..'Z' => 4,
 +        _ => 5,
 +    };
 +
 +    almost_complete_letter_range!();
 +    b!();
 +}
 +
++#[clippy::msrv = "1.25"]
 +fn _under_msrv() {
-     #![clippy::msrv = "1.26"]
 +    let _ = match 'a' {
 +        'a'...'z' => 1,
 +        _ => 2,
 +    };
 +}
 +
++#[clippy::msrv = "1.26"]
 +fn _meets_msrv() {
 +    let _ = 'a'..='z';
 +    let _ = match 'a' {
 +        'a'..='z' => 1,
 +        _ => 2,
 +    };
 +}
index a66900a976efa92d549a8a194c78d3e376a98cdb,0000000000000000000000000000000000000000..9979316eca4290a4d6718cb17182b112c7760257
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,79 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// edition:2018
 +// aux-build:macro_rules.rs
 +
-     #![clippy::msrv = "1.25"]
 +#![feature(exclusive_range_pattern)]
 +#![feature(stmt_expr_attributes)]
 +#![warn(clippy::almost_complete_letter_range)]
 +#![allow(ellipsis_inclusive_range_patterns)]
 +#![allow(clippy::needless_parens_on_range_literals)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +macro_rules! a {
 +    () => {
 +        'a'
 +    };
 +}
 +
 +macro_rules! b {
 +    () => {
 +        let _ = 'a'..'z';
 +    };
 +}
 +
 +fn main() {
 +    #[rustfmt::skip]
 +    {
 +        let _ = ('a') ..'z';
 +        let _ = 'A' .. ('Z');
 +    }
 +
 +    let _ = 'b'..'z';
 +    let _ = 'B'..'Z';
 +
 +    let _ = (b'a')..(b'z');
 +    let _ = b'A'..b'Z';
 +
 +    let _ = b'b'..b'z';
 +    let _ = b'B'..b'Z';
 +
 +    let _ = a!()..'z';
 +
 +    let _ = match 0u8 {
 +        b'a'..b'z' if true => 1,
 +        b'A'..b'Z' if true => 2,
 +        b'b'..b'z' => 3,
 +        b'B'..b'Z' => 4,
 +        _ => 5,
 +    };
 +
 +    let _ = match 'x' {
 +        'a'..'z' if true => 1,
 +        'A'..'Z' if true => 2,
 +        'b'..'z' => 3,
 +        'B'..'Z' => 4,
 +        _ => 5,
 +    };
 +
 +    almost_complete_letter_range!();
 +    b!();
 +}
 +
++#[clippy::msrv = "1.25"]
 +fn _under_msrv() {
-     #![clippy::msrv = "1.26"]
 +    let _ = match 'a' {
 +        'a'..'z' => 1,
 +        _ => 2,
 +    };
 +}
 +
++#[clippy::msrv = "1.26"]
 +fn _meets_msrv() {
 +    let _ = 'a'..'z';
 +    let _ = match 'a' {
 +        'a'..'z' => 1,
 +        _ => 2,
 +    };
 +}
index 3de44c72c1b9d35ad50773e16b143200162da5dd,0000000000000000000000000000000000000000..9abf6d6c5e7d00c4fea180c6219eb7a3804a4cc2
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,113 @@@
-   --> $DIR/almost_complete_letter_range.rs:30:17
 +error: almost complete ascii letter range
-   --> $DIR/almost_complete_letter_range.rs:31:17
++  --> $DIR/almost_complete_letter_range.rs:29:17
 +   |
 +LL |         let _ = ('a') ..'z';
 +   |                 ^^^^^^--^^^
 +   |                       |
 +   |                       help: use an inclusive range: `..=`
 +   |
 +   = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
 +
 +error: almost complete ascii letter range
-   --> $DIR/almost_complete_letter_range.rs:37:13
++  --> $DIR/almost_complete_letter_range.rs:30:17
 +   |
 +LL |         let _ = 'A' .. ('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:36:13
 +   |
 +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:37:13
 +   |
 +LL |     let _ = b'A'..b'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:42:13
 +   |
 +LL |     let _ = a!()..'z';
 +   |             ^^^^--^^^
 +   |                 |
 +   |                 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:45:9
 +   |
 +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:54:9
++  --> $DIR/almost_complete_letter_range.rs:46:9
 +   |
 +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:55:9
++  --> $DIR/almost_complete_letter_range.rs:53:9
 +   |
 +LL |         'a'..'z' if true => 1,
 +   |         ^^^--^^^
 +   |            |
 +   |            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:54:9
 +   |
 +LL |         'A'..'Z' if true => 2,
 +   |         ^^^--^^^
 +   |            |
 +   |            help: use an inclusive range: `..=`
 +
 +error: almost complete ascii letter range
-   --> $DIR/almost_complete_letter_range.rs:68:9
++  --> $DIR/almost_complete_letter_range.rs:22:17
 +   |
 +LL |         let _ = 'a'..'z';
 +   |                 ^^^--^^^
 +   |                    |
 +   |                    help: use an inclusive range: `..=`
 +...
 +LL |     b!();
 +   |     ---- in this macro invocation
 +   |
 +   = 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:75:13
++  --> $DIR/almost_complete_letter_range.rs:67:9
 +   |
 +LL |         'a'..'z' => 1,
 +   |         ^^^--^^^
 +   |            |
 +   |            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:74:13
 +   |
 +LL |     let _ = 'a'..'z';
 +   |             ^^^--^^^
 +   |                |
 +   |                help: use an inclusive range: `..=`
 +
 +error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:76:9
 +   |
 +LL |         'a'..'z' => 1,
 +   |         ^^^--^^^
 +   |            |
 +   |            help: use an inclusive range: `..=`
 +
 +error: aborting due to 13 previous errors
 +
index e6bf944c7a5ecbb3cc08836e9a236c82b34424b9,0000000000000000000000000000000000000000..8676b562b4f9e864611d290253895122f2e9bf42
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,45 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.50"]
 +#![warn(clippy::cast_abs_to_unsigned)]
 +#![allow(clippy::uninlined_format_args, unused)]
 +
 +fn main() {
 +    let x: i32 = -42;
 +    let y: u32 = x.unsigned_abs();
 +    println!("The absolute value of {} is {}", x, y);
 +
 +    let a: i32 = -3;
 +    let _: usize = a.unsigned_abs() as usize;
 +    let _: usize = a.unsigned_abs() as _;
 +    let _ = a.unsigned_abs() as usize;
 +
 +    let a: i64 = -3;
 +    let _ = a.unsigned_abs() as usize;
 +    let _ = a.unsigned_abs() as u8;
 +    let _ = a.unsigned_abs() as u16;
 +    let _ = a.unsigned_abs() as u32;
 +    let _ = a.unsigned_abs();
 +    let _ = a.unsigned_abs() as u128;
 +
 +    let a: isize = -3;
 +    let _ = a.unsigned_abs();
 +    let _ = a.unsigned_abs() as u8;
 +    let _ = a.unsigned_abs() as u16;
 +    let _ = a.unsigned_abs() as u32;
 +    let _ = a.unsigned_abs() as u64;
 +    let _ = a.unsigned_abs() as u128;
 +
 +    let _ = (x as i64 - y as i64).unsigned_abs() as u32;
 +}
 +
++#[clippy::msrv = "1.50"]
 +fn msrv_1_50() {
-     #![clippy::msrv = "1.51"]
 +    let x: i32 = 10;
 +    assert_eq!(10u32, x.abs() as u32);
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn msrv_1_51() {
 +    let x: i32 = 10;
 +    assert_eq!(10u32, x.unsigned_abs());
 +}
index c87320b5209db8165714a1a1f7f2b837b6380287,0000000000000000000000000000000000000000..5775af874f8fcec64593fd0915d92202f7f98238
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,45 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.50"]
 +#![warn(clippy::cast_abs_to_unsigned)]
 +#![allow(clippy::uninlined_format_args, unused)]
 +
 +fn main() {
 +    let x: i32 = -42;
 +    let y: u32 = x.abs() as u32;
 +    println!("The absolute value of {} is {}", x, y);
 +
 +    let a: i32 = -3;
 +    let _: usize = a.abs() as usize;
 +    let _: usize = a.abs() as _;
 +    let _ = a.abs() as usize;
 +
 +    let a: i64 = -3;
 +    let _ = a.abs() as usize;
 +    let _ = a.abs() as u8;
 +    let _ = a.abs() as u16;
 +    let _ = a.abs() as u32;
 +    let _ = a.abs() as u64;
 +    let _ = a.abs() as u128;
 +
 +    let a: isize = -3;
 +    let _ = a.abs() as usize;
 +    let _ = a.abs() as u8;
 +    let _ = a.abs() as u16;
 +    let _ = a.abs() as u32;
 +    let _ = a.abs() as u64;
 +    let _ = a.abs() as u128;
 +
 +    let _ = (x as i64 - y as i64).abs() as u32;
 +}
 +
++#[clippy::msrv = "1.50"]
 +fn msrv_1_50() {
-     #![clippy::msrv = "1.51"]
 +    let x: i32 = 10;
 +    assert_eq!(10u32, x.abs() as u32);
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn msrv_1_51() {
 +    let x: i32 = 10;
 +    assert_eq!(10u32, x.abs() as u32);
 +}
index 1b39c554b03845d12d1c13cdfabfc23c48949a79,0000000000000000000000000000000000000000..4668554f4511c2bfc2320d261b883dfcfd7c1636
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,112 @@@
-   --> $DIR/cast_abs_to_unsigned.rs:9:18
 +error: casting the result of `i32::abs()` to u32
-   --> $DIR/cast_abs_to_unsigned.rs:13:20
++  --> $DIR/cast_abs_to_unsigned.rs:8:18
 +   |
 +LL |     let y: u32 = x.abs() as u32;
 +   |                  ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
 +   |
 +   = 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:14: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:15:13
++  --> $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:18: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:19: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:20: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:21: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:22: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:23: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:26: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:27: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:28: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:29: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:30: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:31: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:33: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:47:23
++  --> $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:44:23
 +   |
 +LL |     assert_eq!(10u32, x.abs() as u32);
 +   |                       ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
 +
 +error: aborting due to 18 previous errors
 +
index af13b755e310a55224e5048af11931621070106a,0000000000000000000000000000000000000000..13b3cf838c9f02c2a75ee76b362670a416e945a0
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,52 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.27"]
 +#![allow(dead_code)]
 +#![warn(clippy::cast_lossless)]
 +
 +fn main() {
 +    // Test clippy::cast_lossless with casts to integer types
 +    let _ = u8::from(true);
 +    let _ = u16::from(true);
 +    let _ = u32::from(true);
 +    let _ = u64::from(true);
 +    let _ = u128::from(true);
 +    let _ = usize::from(true);
 +
 +    let _ = i8::from(true);
 +    let _ = i16::from(true);
 +    let _ = i32::from(true);
 +    let _ = i64::from(true);
 +    let _ = i128::from(true);
 +    let _ = isize::from(true);
 +
 +    // Test with an expression wrapped in parens
 +    let _ = u16::from(true | false);
 +}
 +
 +// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
 +// so we skip the lint if the expression is in a const fn.
 +// See #3656
 +const fn abc(input: bool) -> u32 {
 +    input as u32
 +}
 +
 +// Same as the above issue. We can't suggest `::from` in const fns in impls
 +mod cast_lossless_in_impl {
 +    struct A;
 +
 +    impl A {
 +        pub const fn convert(x: bool) -> u64 {
 +            x as u64
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.27"]
 +fn msrv_1_27() {
-     #![clippy::msrv = "1.28"]
 +    let _ = true as u8;
 +}
 +
++#[clippy::msrv = "1.28"]
 +fn msrv_1_28() {
 +    let _ = u8::from(true);
 +}
index 3b06af899c60d0a5118b671a8a7066714da9a36d,0000000000000000000000000000000000000000..3eed2135562c8b7c3de500699b97cb06aa3ea50f
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,52 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.27"]
 +#![allow(dead_code)]
 +#![warn(clippy::cast_lossless)]
 +
 +fn main() {
 +    // Test clippy::cast_lossless with casts to integer types
 +    let _ = true as u8;
 +    let _ = true as u16;
 +    let _ = true as u32;
 +    let _ = true as u64;
 +    let _ = true as u128;
 +    let _ = true as usize;
 +
 +    let _ = true as i8;
 +    let _ = true as i16;
 +    let _ = true as i32;
 +    let _ = true as i64;
 +    let _ = true as i128;
 +    let _ = true as isize;
 +
 +    // Test with an expression wrapped in parens
 +    let _ = (true | false) as u16;
 +}
 +
 +// The lint would suggest using `u32::from(input)` here but the `XX::from` function is not const,
 +// so we skip the lint if the expression is in a const fn.
 +// See #3656
 +const fn abc(input: bool) -> u32 {
 +    input as u32
 +}
 +
 +// Same as the above issue. We can't suggest `::from` in const fns in impls
 +mod cast_lossless_in_impl {
 +    struct A;
 +
 +    impl A {
 +        pub const fn convert(x: bool) -> u64 {
 +            x as u64
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.27"]
 +fn msrv_1_27() {
-     #![clippy::msrv = "1.28"]
 +    let _ = true as u8;
 +}
 +
++#[clippy::msrv = "1.28"]
 +fn msrv_1_28() {
 +    let _ = true as u8;
 +}
index 768b033d10a28add2d6b2383346567d39edd0fc6,0000000000000000000000000000000000000000..ce240b70f891d2624c0a7a1c4ec255106a12a68f
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,88 @@@
-   --> $DIR/cast_lossless_bool.rs:9:13
 +error: casting `bool` to `u8` is more cleanly stated with `u8::from(_)`
-   --> $DIR/cast_lossless_bool.rs:10:13
++  --> $DIR/cast_lossless_bool.rs:8:13
 +   |
 +LL |     let _ = true as u8;
 +   |             ^^^^^^^^^^ help: try: `u8::from(true)`
 +   |
 +   = 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:11: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:12: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:13: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:14: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:16: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:17: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:18: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:19: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:20: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:21: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:24: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:54: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:51:13
 +   |
 +LL |     let _ = true as u8;
 +   |             ^^^^^^^^^^ help: try: `u8::from(true)`
 +
 +error: aborting due to 14 previous errors
 +
index 8a5645b22ed19ddf82512b2c51944e7f92736555,0000000000000000000000000000000000000000..b970b1209b6546a90fb05658810e029a06a6f8a5
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,43 @@@
- #![feature(stmt_expr_attributes, custom_inner_attributes)]
 +// run-rustfix
-     #![clippy::msrv = "1.29"]
++#![feature(stmt_expr_attributes)]
 +
 +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::deprecated_cfg_attr)]
 +
 +// This doesn't get linted, see known problems
 +#![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +#[rustfmt::skip]
 +trait Foo
 +{
 +fn foo(
 +);
 +}
 +
 +fn skip_on_statements() {
 +    #[rustfmt::skip]
 +    5+3;
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    foo::f();
 +}
 +
 +mod foo {
 +    #![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +    pub fn f() {}
 +}
 +
++#[clippy::msrv = "1.29"]
 +fn msrv_1_29() {
-     #![clippy::msrv = "1.30"]
 +    #[cfg_attr(rustfmt, rustfmt::skip)]
 +    1+29;
 +}
 +
++#[clippy::msrv = "1.30"]
 +fn msrv_1_30() {
 +    #[rustfmt::skip]
 +    1+30;
 +}
index 2fb140efae7680085af0ea9eab5c089edb1fd1c6,0000000000000000000000000000000000000000..0a8e6a89d8a0f510a162fbe9ca3d6dcedd028c7d
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,43 @@@
- #![feature(stmt_expr_attributes, custom_inner_attributes)]
 +// run-rustfix
-     #![clippy::msrv = "1.29"]
++#![feature(stmt_expr_attributes)]
 +
 +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::deprecated_cfg_attr)]
 +
 +// This doesn't get linted, see known problems
 +#![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +#[rustfmt::skip]
 +trait Foo
 +{
 +fn foo(
 +);
 +}
 +
 +fn skip_on_statements() {
 +    #[cfg_attr(rustfmt, rustfmt::skip)]
 +    5+3;
 +}
 +
 +#[cfg_attr(rustfmt, rustfmt_skip)]
 +fn main() {
 +    foo::f();
 +}
 +
 +mod foo {
 +    #![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +    pub fn f() {}
 +}
 +
++#[clippy::msrv = "1.29"]
 +fn msrv_1_29() {
-     #![clippy::msrv = "1.30"]
 +    #[cfg_attr(rustfmt, rustfmt::skip)]
 +    1+29;
 +}
 +
++#[clippy::msrv = "1.30"]
 +fn msrv_1_30() {
 +    #[cfg_attr(rustfmt, rustfmt::skip)]
 +    1+30;
 +}
index 08df7b2b39a0c26569df08e183e2f3a481c9f0d4,0000000000000000000000000000000000000000..524a2bf72484bf1d4a0d1a13dc791cd31385fb4b
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
-   --> $DIR/cfg_attr_rustfmt.rs:43:5
 +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
 +  --> $DIR/cfg_attr_rustfmt.rs:18:5
 +   |
 +LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
 +   |
 +   = note: `-D clippy::deprecated-cfg-attr` implied by `-D warnings`
 +
 +error: `cfg_attr` is deprecated for rustfmt and got replaced by tool attributes
 +  --> $DIR/cfg_attr_rustfmt.rs:22:1
 +   |
 +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:41:5
 +   |
 +LL |     #[cfg_attr(rustfmt, rustfmt::skip)]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `#[rustfmt::skip]`
 +
 +error: aborting due to 3 previous errors
 +
index f936957cb40c682d7d6bdbb538fddacb07c943fc,0000000000000000000000000000000000000000..e279ba3147bba3420159b3cccb6a0bc5181efda0
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,92 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.33"]
 +#![allow(
 +    clippy::cast_lossless,
 +    unused,
 +    // Int::max_value will be deprecated in the future
 +    deprecated,
 +)]
 +#![warn(clippy::checked_conversions)]
 +
 +// Positive tests
 +
 +// Signed to unsigned
 +
 +pub fn i64_to_u32(value: i64) {
 +    let _ = u32::try_from(value).is_ok();
 +    let _ = u32::try_from(value).is_ok();
 +}
 +
 +pub fn i64_to_u16(value: i64) {
 +    let _ = u16::try_from(value).is_ok();
 +    let _ = u16::try_from(value).is_ok();
 +}
 +
 +pub fn isize_to_u8(value: isize) {
 +    let _ = u8::try_from(value).is_ok();
 +    let _ = u8::try_from(value).is_ok();
 +}
 +
 +// Signed to signed
 +
 +pub fn i64_to_i32(value: i64) {
 +    let _ = i32::try_from(value).is_ok();
 +    let _ = i32::try_from(value).is_ok();
 +}
 +
 +pub fn i64_to_i16(value: i64) {
 +    let _ = i16::try_from(value).is_ok();
 +    let _ = i16::try_from(value).is_ok();
 +}
 +
 +// Unsigned to X
 +
 +pub fn u32_to_i32(value: u32) {
 +    let _ = i32::try_from(value).is_ok();
 +    let _ = i32::try_from(value).is_ok();
 +}
 +
 +pub fn usize_to_isize(value: usize) {
 +    let _ = isize::try_from(value).is_ok() && value as i32 == 5;
 +    let _ = isize::try_from(value).is_ok() && value as i32 == 5;
 +}
 +
 +pub fn u32_to_u16(value: u32) {
 +    let _ = u16::try_from(value).is_ok() && value as i32 == 5;
 +    let _ = u16::try_from(value).is_ok() && value as i32 == 5;
 +}
 +
 +// Negative tests
 +
 +pub fn no_i64_to_i32(value: i64) {
 +    let _ = value <= (i32::max_value() as i64) && value >= 0;
 +    let _ = value <= (i32::MAX as i64) && value >= 0;
 +}
 +
 +pub fn no_isize_to_u8(value: isize) {
 +    let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
 +    let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
 +}
 +
 +pub fn i8_to_u8(value: i8) {
 +    let _ = value >= 0;
 +}
 +
 +// Do not lint
 +pub const fn issue_8898(i: u32) -> bool {
 +    i <= i32::MAX as u32
 +}
 +
++#[clippy::msrv = "1.33"]
 +fn msrv_1_33() {
-     #![clippy::msrv = "1.34"]
 +    let value: i64 = 33;
 +    let _ = value <= (u32::MAX as i64) && value >= 0;
 +}
 +
++#[clippy::msrv = "1.34"]
 +fn msrv_1_34() {
 +    let value: i64 = 34;
 +    let _ = u32::try_from(value).is_ok();
 +}
 +
 +fn main() {}
index 77aec713ff3169079e3562ba9d413116196079d8,0000000000000000000000000000000000000000..9d7a40995c3735b82daecc483e1d566c40a8709e
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,92 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.33"]
 +#![allow(
 +    clippy::cast_lossless,
 +    unused,
 +    // Int::max_value will be deprecated in the future
 +    deprecated,
 +)]
 +#![warn(clippy::checked_conversions)]
 +
 +// Positive tests
 +
 +// Signed to unsigned
 +
 +pub fn i64_to_u32(value: i64) {
 +    let _ = value <= (u32::max_value() as i64) && value >= 0;
 +    let _ = value <= (u32::MAX as i64) && value >= 0;
 +}
 +
 +pub fn i64_to_u16(value: i64) {
 +    let _ = value <= i64::from(u16::max_value()) && value >= 0;
 +    let _ = value <= i64::from(u16::MAX) && value >= 0;
 +}
 +
 +pub fn isize_to_u8(value: isize) {
 +    let _ = value <= (u8::max_value() as isize) && value >= 0;
 +    let _ = value <= (u8::MAX as isize) && value >= 0;
 +}
 +
 +// Signed to signed
 +
 +pub fn i64_to_i32(value: i64) {
 +    let _ = value <= (i32::max_value() as i64) && value >= (i32::min_value() as i64);
 +    let _ = value <= (i32::MAX as i64) && value >= (i32::MIN as i64);
 +}
 +
 +pub fn i64_to_i16(value: i64) {
 +    let _ = value <= i64::from(i16::max_value()) && value >= i64::from(i16::min_value());
 +    let _ = value <= i64::from(i16::MAX) && value >= i64::from(i16::MIN);
 +}
 +
 +// Unsigned to X
 +
 +pub fn u32_to_i32(value: u32) {
 +    let _ = value <= i32::max_value() as u32;
 +    let _ = value <= i32::MAX as u32;
 +}
 +
 +pub fn usize_to_isize(value: usize) {
 +    let _ = value <= isize::max_value() as usize && value as i32 == 5;
 +    let _ = value <= isize::MAX as usize && value as i32 == 5;
 +}
 +
 +pub fn u32_to_u16(value: u32) {
 +    let _ = value <= u16::max_value() as u32 && value as i32 == 5;
 +    let _ = value <= u16::MAX as u32 && value as i32 == 5;
 +}
 +
 +// Negative tests
 +
 +pub fn no_i64_to_i32(value: i64) {
 +    let _ = value <= (i32::max_value() as i64) && value >= 0;
 +    let _ = value <= (i32::MAX as i64) && value >= 0;
 +}
 +
 +pub fn no_isize_to_u8(value: isize) {
 +    let _ = value <= (u8::max_value() as isize) && value >= (u8::min_value() as isize);
 +    let _ = value <= (u8::MAX as isize) && value >= (u8::MIN as isize);
 +}
 +
 +pub fn i8_to_u8(value: i8) {
 +    let _ = value >= 0;
 +}
 +
 +// Do not lint
 +pub const fn issue_8898(i: u32) -> bool {
 +    i <= i32::MAX as u32
 +}
 +
++#[clippy::msrv = "1.33"]
 +fn msrv_1_33() {
-     #![clippy::msrv = "1.34"]
 +    let value: i64 = 33;
 +    let _ = value <= (u32::MAX as i64) && value >= 0;
 +}
 +
++#[clippy::msrv = "1.34"]
 +fn msrv_1_34() {
 +    let value: i64 = 34;
 +    let _ = value <= (u32::MAX as i64) && value >= 0;
 +}
 +
 +fn main() {}
index b2bf7af8daf87a8df2b6a723a1b72ee53dc882c8,0000000000000000000000000000000000000000..273ead73bda580a27607ef7362eb3654c06efe10
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,106 @@@
-   --> $DIR/checked_conversions.rs:17:13
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:18: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()`
 +   |
 +   = note: `-D clippy::checked-conversions` implied by `-D warnings`
 +
 +error: checked cast can be simplified
-   --> $DIR/checked_conversions.rs:22: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:23: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:27: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:28: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:34: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:35: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:39: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:40: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:46: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:47: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:51: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:52: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:56: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:57: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:92: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:89:13
 +   |
 +LL |     let _ = value <= (u32::MAX as i64) && value >= 0;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `u32::try_from(value).is_ok()`
 +
 +error: aborting due to 17 previous errors
 +
index 42ed232d1001cd3dd8c52b3efff71b8d747daab2,0000000000000000000000000000000000000000..ecbfc1feedf62f4f1a8d812bb270d9b9193719cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,35 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.34"]
 +#![warn(clippy::cloned_instead_of_copied)]
 +#![allow(unused)]
 +
 +fn main() {
 +    // yay
 +    let _ = [1].iter().copied();
 +    let _ = vec!["hi"].iter().copied();
 +    let _ = Some(&1).copied();
 +    let _ = Box::new([1].iter()).copied();
 +    let _ = Box::new(Some(&1)).copied();
 +
 +    // nay
 +    let _ = [String::new()].iter().cloned();
 +    let _ = Some(&String::new()).cloned();
 +}
 +
++#[clippy::msrv = "1.34"]
 +fn msrv_1_34() {
-     #![clippy::msrv = "1.35"]
 +    let _ = [1].iter().cloned();
 +    let _ = Some(&1).cloned();
 +}
 +
++#[clippy::msrv = "1.35"]
 +fn msrv_1_35() {
-     #![clippy::msrv = "1.36"]
 +    let _ = [1].iter().cloned();
 +    let _ = Some(&1).copied(); // Option::copied needs 1.35
 +}
 +
++#[clippy::msrv = "1.36"]
 +fn msrv_1_36() {
 +    let _ = [1].iter().copied(); // Iterator::copied needs 1.36
 +    let _ = Some(&1).copied();
 +}
index 471bd9654cc13371fa01948040fdf25e94a4e60a,0000000000000000000000000000000000000000..163dc3dddce62de3e9d452c0bcc3fa0983196864
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,35 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.34"]
 +#![warn(clippy::cloned_instead_of_copied)]
 +#![allow(unused)]
 +
 +fn main() {
 +    // yay
 +    let _ = [1].iter().cloned();
 +    let _ = vec!["hi"].iter().cloned();
 +    let _ = Some(&1).cloned();
 +    let _ = Box::new([1].iter()).cloned();
 +    let _ = Box::new(Some(&1)).cloned();
 +
 +    // nay
 +    let _ = [String::new()].iter().cloned();
 +    let _ = Some(&String::new()).cloned();
 +}
 +
++#[clippy::msrv = "1.34"]
 +fn msrv_1_34() {
-     #![clippy::msrv = "1.35"]
 +    let _ = [1].iter().cloned();
 +    let _ = Some(&1).cloned();
 +}
 +
++#[clippy::msrv = "1.35"]
 +fn msrv_1_35() {
-     #![clippy::msrv = "1.36"]
 +    let _ = [1].iter().cloned();
 +    let _ = Some(&1).cloned(); // Option::copied needs 1.35
 +}
 +
++#[clippy::msrv = "1.36"]
 +fn msrv_1_36() {
 +    let _ = [1].iter().cloned(); // Iterator::copied needs 1.36
 +    let _ = Some(&1).cloned();
 +}
index 914c9a91e8300e048faf1bc014655c5bc073273f,0000000000000000000000000000000000000000..e0361acd956072d53a704a40f10dd3de1b998f20
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
-   --> $DIR/cloned_instead_of_copied.rs:9:24
 +error: used `cloned` where `copied` could be used instead
-   --> $DIR/cloned_instead_of_copied.rs:10:31
++  --> $DIR/cloned_instead_of_copied.rs:8:24
 +   |
 +LL |     let _ = [1].iter().cloned();
 +   |                        ^^^^^^ help: try: `copied`
 +   |
 +   = 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:11:22
++  --> $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:12:34
++  --> $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:13:32
++  --> $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:31:22
++  --> $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:37:24
++  --> $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:38:22
++  --> $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:34:22
 +   |
 +LL |     let _ = Some(&1).cloned();
 +   |                      ^^^^^^ help: try: `copied`
 +
 +error: aborting due to 8 previous errors
 +
index 3bac738acd65b90412004d73e8719ed885b8c368,0000000000000000000000000000000000000000..b63cbd8a8e6bde03c72dde203720fa44646f548f
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,28 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.16"]
 +#![allow(unused)]
 +
 +struct MyTypeNonDebug;
 +
 +#[derive(Debug)]
 +struct MyTypeDebug;
 +
 +fn main() {
 +    let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
 +    test_debug.expect_err("Testing debug type");
 +
 +    let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
 +    test_non_debug.err().expect("Testing non debug type");
 +}
 +
++#[clippy::msrv = "1.16"]
 +fn msrv_1_16() {
-     #![clippy::msrv = "1.17"]
 +    let x: Result<u32, &str> = Ok(16);
 +    x.err().expect("16");
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn msrv_1_17() {
 +    let x: Result<u32, &str> = Ok(17);
 +    x.expect_err("17");
 +}
index 6e7c47d9ad3cf09f49736c3a092577a5d2aed96e,0000000000000000000000000000000000000000..c081a745fb4069b9809a25ab53a2622c40d0ed69
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,28 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.16"]
 +#![allow(unused)]
 +
 +struct MyTypeNonDebug;
 +
 +#[derive(Debug)]
 +struct MyTypeDebug;
 +
 +fn main() {
 +    let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
 +    test_debug.err().expect("Testing debug type");
 +
 +    let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
 +    test_non_debug.err().expect("Testing non debug type");
 +}
 +
++#[clippy::msrv = "1.16"]
 +fn msrv_1_16() {
-     #![clippy::msrv = "1.17"]
 +    let x: Result<u32, &str> = Ok(16);
 +    x.err().expect("16");
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn msrv_1_17() {
 +    let x: Result<u32, &str> = Ok(17);
 +    x.err().expect("17");
 +}
index 91a6cf8de65fcd190de086f704fb7d534f374fa3,0000000000000000000000000000000000000000..82c0754cfb00b20ff49202cf9f5510284edf83cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
-   --> $DIR/err_expect.rs:13:16
 +error: called `.err().expect()` on a `Result` value
-   --> $DIR/err_expect.rs:30:7
++  --> $DIR/err_expect.rs:12:16
 +   |
 +LL |     test_debug.err().expect("Testing debug type");
 +   |                ^^^^^^^^^^^^ help: try: `expect_err`
 +   |
 +   = note: `-D clippy::err-expect` implied by `-D warnings`
 +
 +error: called `.err().expect()` on a `Result` value
++  --> $DIR/err_expect.rs:27:7
 +   |
 +LL |     x.err().expect("17");
 +   |       ^^^^^^^^^^^^ help: try: `expect_err`
 +
 +error: aborting due to 2 previous errors
 +
index a9cc80aaaf623c81f85c79be05c1c8b93b6b6ecf,0000000000000000000000000000000000000000..dc129591eac4eb7e4f9e96f9dc64f739acfad0b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,318 -1,0 +1,340 @@@
 +// run-rustfix
 +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::needless_borrow,
 +    clippy::needless_pass_by_value,
 +    clippy::no_effect,
 +    clippy::option_map_unit_fn,
 +    clippy::redundant_closure_call,
 +    clippy::uninlined_format_args
 +)]
 +
 +use std::path::{Path, PathBuf};
 +
 +macro_rules! mac {
 +    () => {
 +        foobar()
 +    };
 +}
 +
 +macro_rules! closure_mac {
 +    () => {
 +        |n| foo(n)
 +    };
 +}
 +
 +fn main() {
 +    let a = Some(1u8).map(foo);
 +    let c = Some(1u8).map(|a| {1+2; foo}(a));
 +    true.then(|| mac!()); // don't lint function in macro expansion
 +    Some(1).map(closure_mac!()); // don't lint closure in macro expansion
 +    let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
 +    let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
 +    all(&[1, 2, 3], &&2, below); //is adjusted
 +    unsafe {
 +        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
 +    }
 +
 +    // See #815
 +    let e = Some(1u8).map(|a| divergent(a));
 +    let e = Some(1u8).map(generic);
 +    let e = Some(1u8).map(generic);
 +    // See #515
 +    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
 +        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
 +
 +    // issue #7224
 +    let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
 +}
 +
 +trait TestTrait {
 +    fn trait_foo(self) -> bool;
 +    fn trait_foo_ref(&self) -> bool;
 +}
 +
 +struct TestStruct<'a> {
 +    some_ref: &'a i32,
 +}
 +
 +impl<'a> TestStruct<'a> {
 +    fn foo(self) -> bool {
 +        false
 +    }
 +    unsafe fn foo_unsafe(self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<'a> TestTrait for TestStruct<'a> {
 +    fn trait_foo(self) -> bool {
 +        false
 +    }
 +    fn trait_foo_ref(&self) -> bool {
 +        false
 +    }
 +}
 +
 +impl<'a> std::ops::Deref for TestStruct<'a> {
 +    type Target = char;
 +    fn deref(&self) -> &char {
 +        &'a'
 +    }
 +}
 +
 +fn test_redundant_closures_containing_method_calls() {
 +    let i = 10;
 +    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
 +    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
 +    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
 +    unsafe {
 +        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
 +    }
 +    let e = Some("str").map(std::string::ToString::to_string);
 +    let e = Some('a').map(char::to_uppercase);
 +    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
 +    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
 +    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
 +    let c = Some(TestStruct { some_ref: &i })
 +        .as_ref()
 +        .map(|c| c.to_ascii_uppercase());
 +
 +    fn test_different_borrow_levels<T>(t: &[&T])
 +    where
 +        T: TestTrait,
 +    {
 +        t.iter().filter(|x| x.trait_foo_ref());
 +        t.iter().map(|x| x.trait_foo_ref());
 +    }
 +}
 +
 +struct Thunk<T>(Box<dyn FnMut() -> T>);
 +
 +impl<T> Thunk<T> {
 +    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
 +        let mut option = Some(f);
 +        // This should not trigger redundant_closure (#1439)
 +        Thunk(Box::new(move || option.take().unwrap()()))
 +    }
 +
 +    fn unwrap(self) -> T {
 +        let Thunk(mut f) = self;
 +        f()
 +    }
 +}
 +
 +fn foobar() {
 +    let thunk = Thunk::new(|| println!("Hello, world!"));
 +    thunk.unwrap()
 +}
 +
 +fn foo(_: u8) {}
 +
 +fn foo2(_: u8) -> u8 {
 +    1u8
 +}
 +
 +fn all<X, F>(x: &[X], y: &X, f: F) -> bool
 +where
 +    F: Fn(&X, &X) -> bool,
 +{
 +    x.iter().all(|e| f(e, y))
 +}
 +
 +fn below(x: &u8, y: &u8) -> bool {
 +    x < y
 +}
 +
 +unsafe fn unsafe_fn(_: u8) {}
 +
 +fn divergent(_: u8) -> ! {
 +    unimplemented!()
 +}
 +
 +fn generic<T>(_: T) -> u8 {
 +    0
 +}
 +
 +fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
 +    requires_fn_once(x);
 +}
 +fn requires_fn_once<T: FnOnce()>(_: T) {}
 +
 +fn test_redundant_closure_with_function_pointer() {
 +    type FnPtrType = fn(u8);
 +    let foo_ptr: FnPtrType = foo;
 +    let a = Some(1u8).map(foo_ptr);
 +}
 +
 +fn test_redundant_closure_with_another_closure() {
 +    let closure = |a| println!("{}", a);
 +    let a = Some(1u8).map(closure);
 +}
 +
 +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
 +    // Currently f is called when result of make_lazy is called.
 +    // If the closure is removed, f will be called when make_lazy itself is
 +    // called. This changes semantics, so the closure must stay.
 +    Box::new(move |x| f()(x))
 +}
 +
 +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
 +    f(&mut "Hello".to_owned())
 +}
 +fn test_difference_in_mutability() {
 +    call(|s| s.clone());
 +}
 +
 +struct Bar;
 +impl std::ops::Deref for Bar {
 +    type Target = str;
 +    fn deref(&self) -> &str {
 +        "hi"
 +    }
 +}
 +
 +fn test_deref_with_trait_method() {
 +    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 +}
 +
 +fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
 +    let mut res = Vec::new();
 +    let mut add_to_res = |n| res.push(n);
 +    x.into_iter().for_each(&mut add_to_res);
 +    y.into_iter().for_each(&mut add_to_res);
 +    z.into_iter().for_each(add_to_res);
 +}
 +
 +fn mutable_closure_in_loop() {
 +    let mut value = 0;
 +    let mut closure = |n| value += n;
 +    for _ in 0..5 {
 +        Some(1).map(&mut closure);
 +
 +        let mut value = 0;
 +        let mut in_loop = |n| value += n;
 +        Some(1).map(in_loop);
 +    }
 +}
 +
 +fn late_bound_lifetimes() {
 +    fn take_asref_path<P: AsRef<Path>>(path: P) {}
 +
 +    fn map_str<F>(thunk: F)
 +    where
 +        F: FnOnce(&str),
 +    {
 +    }
 +
 +    fn map_str_to_path<F>(thunk: F)
 +    where
 +        F: FnOnce(&str) -> &Path,
 +    {
 +    }
 +    map_str(|s| take_asref_path(s));
 +    map_str_to_path(|s| s.as_ref());
 +}
 +
 +mod type_param_bound {
 +    trait Trait {
 +        fn fun();
 +    }
 +
 +    fn take<T: 'static>(_: T) {}
 +
 +    fn test<X: Trait>() {
 +        // don't lint, but it's questionable that rust requires a cast
 +        take(|| X::fun());
 +        take(X::fun as fn());
 +    }
 +}
 +
 +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
 +fn arc_fp() {
 +    let rc = std::rc::Rc::new(|| 7);
 +    let arc = std::sync::Arc::new(|n| n + 1);
 +    let ref_arc = &std::sync::Arc::new(|_| 5);
 +
 +    true.then(|| rc());
 +    (0..5).map(|n| arc(n));
 +    Some(4).map(|n| ref_arc(n));
 +}
 +
 +// #8460 Don't replace closures with params bounded as `ref`
 +mod bind_by_ref {
 +    struct A;
 +    struct B;
 +
 +    impl From<&A> for B {
 +        fn from(A: &A) -> Self {
 +            B
 +        }
 +    }
 +
 +    fn test() {
 +        // should not lint
 +        Some(A).map(|a| B::from(&a));
 +        // should not lint
 +        Some(A).map(|ref a| B::from(a));
 +    }
 +}
 +
 +// #7812 False positive on coerced closure
 +fn coerced_closure() {
 +    fn function_returning_unit<F: FnMut(i32)>(f: F) {}
 +    function_returning_unit(|x| std::process::exit(x));
 +
 +    fn arr() -> &'static [u8; 0] {
 +        &[]
 +    }
 +    fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
 +    slice_fn(|| arr());
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/7861
 +fn box_dyn() {
 +    fn f(_: impl Fn(usize) -> Box<dyn std::any::Any>) {}
 +    f(|x| Box::new(x));
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/5939
 +fn not_general_enough() {
 +    fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {}
 +    f(|path| std::fs::remove_file(path));
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/9369
 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() {
 +    fn takes_fn_mut(_: impl FnMut()) {}
 +    takes_fn_mut(&mut f);
 +
 +    fn takes_fn_once(_: impl FnOnce()) {}
 +    takes_fn_once(&mut f);
 +
 +    f();
 +
 +    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);
++}
index cc99906ccd6677e29488c732a9a5998842c7b542,0000000000000000000000000000000000000000..025fd6a0b7afdf1f5995c0a9d38f314d28efbb3c
mode 100644,000000..100644
--- /dev/null
@@@ -1,318 -1,0 +1,340 @@@
 +// run-rustfix
 +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::needless_borrow,
 +    clippy::needless_pass_by_value,
 +    clippy::no_effect,
 +    clippy::option_map_unit_fn,
 +    clippy::redundant_closure_call,
 +    clippy::uninlined_format_args
 +)]
 +
 +use std::path::{Path, PathBuf};
 +
 +macro_rules! mac {
 +    () => {
 +        foobar()
 +    };
 +}
 +
 +macro_rules! closure_mac {
 +    () => {
 +        |n| foo(n)
 +    };
 +}
 +
 +fn main() {
 +    let a = Some(1u8).map(|a| foo(a));
 +    let c = Some(1u8).map(|a| {1+2; foo}(a));
 +    true.then(|| mac!()); // don't lint function in macro expansion
 +    Some(1).map(closure_mac!()); // don't lint closure in macro expansion
 +    let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
 +    let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
 +    all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
 +    unsafe {
 +        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
 +    }
 +
 +    // See #815
 +    let e = Some(1u8).map(|a| divergent(a));
 +    let e = Some(1u8).map(|a| generic(a));
 +    let e = Some(1u8).map(generic);
 +    // See #515
 +    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
 +        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
 +
 +    // issue #7224
 +    let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
 +}
 +
 +trait TestTrait {
 +    fn trait_foo(self) -> bool;
 +    fn trait_foo_ref(&self) -> bool;
 +}
 +
 +struct TestStruct<'a> {
 +    some_ref: &'a i32,
 +}
 +
 +impl<'a> TestStruct<'a> {
 +    fn foo(self) -> bool {
 +        false
 +    }
 +    unsafe fn foo_unsafe(self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<'a> TestTrait for TestStruct<'a> {
 +    fn trait_foo(self) -> bool {
 +        false
 +    }
 +    fn trait_foo_ref(&self) -> bool {
 +        false
 +    }
 +}
 +
 +impl<'a> std::ops::Deref for TestStruct<'a> {
 +    type Target = char;
 +    fn deref(&self) -> &char {
 +        &'a'
 +    }
 +}
 +
 +fn test_redundant_closures_containing_method_calls() {
 +    let i = 10;
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
 +    let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
 +    unsafe {
 +        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
 +    }
 +    let e = Some("str").map(|s| s.to_string());
 +    let e = Some('a').map(|s| s.to_uppercase());
 +    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
 +    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
 +    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
 +    let c = Some(TestStruct { some_ref: &i })
 +        .as_ref()
 +        .map(|c| c.to_ascii_uppercase());
 +
 +    fn test_different_borrow_levels<T>(t: &[&T])
 +    where
 +        T: TestTrait,
 +    {
 +        t.iter().filter(|x| x.trait_foo_ref());
 +        t.iter().map(|x| x.trait_foo_ref());
 +    }
 +}
 +
 +struct Thunk<T>(Box<dyn FnMut() -> T>);
 +
 +impl<T> Thunk<T> {
 +    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
 +        let mut option = Some(f);
 +        // This should not trigger redundant_closure (#1439)
 +        Thunk(Box::new(move || option.take().unwrap()()))
 +    }
 +
 +    fn unwrap(self) -> T {
 +        let Thunk(mut f) = self;
 +        f()
 +    }
 +}
 +
 +fn foobar() {
 +    let thunk = Thunk::new(|| println!("Hello, world!"));
 +    thunk.unwrap()
 +}
 +
 +fn foo(_: u8) {}
 +
 +fn foo2(_: u8) -> u8 {
 +    1u8
 +}
 +
 +fn all<X, F>(x: &[X], y: &X, f: F) -> bool
 +where
 +    F: Fn(&X, &X) -> bool,
 +{
 +    x.iter().all(|e| f(e, y))
 +}
 +
 +fn below(x: &u8, y: &u8) -> bool {
 +    x < y
 +}
 +
 +unsafe fn unsafe_fn(_: u8) {}
 +
 +fn divergent(_: u8) -> ! {
 +    unimplemented!()
 +}
 +
 +fn generic<T>(_: T) -> u8 {
 +    0
 +}
 +
 +fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
 +    requires_fn_once(|| x());
 +}
 +fn requires_fn_once<T: FnOnce()>(_: T) {}
 +
 +fn test_redundant_closure_with_function_pointer() {
 +    type FnPtrType = fn(u8);
 +    let foo_ptr: FnPtrType = foo;
 +    let a = Some(1u8).map(|a| foo_ptr(a));
 +}
 +
 +fn test_redundant_closure_with_another_closure() {
 +    let closure = |a| println!("{}", a);
 +    let a = Some(1u8).map(|a| closure(a));
 +}
 +
 +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
 +    // Currently f is called when result of make_lazy is called.
 +    // If the closure is removed, f will be called when make_lazy itself is
 +    // called. This changes semantics, so the closure must stay.
 +    Box::new(move |x| f()(x))
 +}
 +
 +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
 +    f(&mut "Hello".to_owned())
 +}
 +fn test_difference_in_mutability() {
 +    call(|s| s.clone());
 +}
 +
 +struct Bar;
 +impl std::ops::Deref for Bar {
 +    type Target = str;
 +    fn deref(&self) -> &str {
 +        "hi"
 +    }
 +}
 +
 +fn test_deref_with_trait_method() {
 +    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 +}
 +
 +fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
 +    let mut res = Vec::new();
 +    let mut add_to_res = |n| res.push(n);
 +    x.into_iter().for_each(|x| add_to_res(x));
 +    y.into_iter().for_each(|x| add_to_res(x));
 +    z.into_iter().for_each(|x| add_to_res(x));
 +}
 +
 +fn mutable_closure_in_loop() {
 +    let mut value = 0;
 +    let mut closure = |n| value += n;
 +    for _ in 0..5 {
 +        Some(1).map(|n| closure(n));
 +
 +        let mut value = 0;
 +        let mut in_loop = |n| value += n;
 +        Some(1).map(|n| in_loop(n));
 +    }
 +}
 +
 +fn late_bound_lifetimes() {
 +    fn take_asref_path<P: AsRef<Path>>(path: P) {}
 +
 +    fn map_str<F>(thunk: F)
 +    where
 +        F: FnOnce(&str),
 +    {
 +    }
 +
 +    fn map_str_to_path<F>(thunk: F)
 +    where
 +        F: FnOnce(&str) -> &Path,
 +    {
 +    }
 +    map_str(|s| take_asref_path(s));
 +    map_str_to_path(|s| s.as_ref());
 +}
 +
 +mod type_param_bound {
 +    trait Trait {
 +        fn fun();
 +    }
 +
 +    fn take<T: 'static>(_: T) {}
 +
 +    fn test<X: Trait>() {
 +        // don't lint, but it's questionable that rust requires a cast
 +        take(|| X::fun());
 +        take(X::fun as fn());
 +    }
 +}
 +
 +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
 +fn arc_fp() {
 +    let rc = std::rc::Rc::new(|| 7);
 +    let arc = std::sync::Arc::new(|n| n + 1);
 +    let ref_arc = &std::sync::Arc::new(|_| 5);
 +
 +    true.then(|| rc());
 +    (0..5).map(|n| arc(n));
 +    Some(4).map(|n| ref_arc(n));
 +}
 +
 +// #8460 Don't replace closures with params bounded as `ref`
 +mod bind_by_ref {
 +    struct A;
 +    struct B;
 +
 +    impl From<&A> for B {
 +        fn from(A: &A) -> Self {
 +            B
 +        }
 +    }
 +
 +    fn test() {
 +        // should not lint
 +        Some(A).map(|a| B::from(&a));
 +        // should not lint
 +        Some(A).map(|ref a| B::from(a));
 +    }
 +}
 +
 +// #7812 False positive on coerced closure
 +fn coerced_closure() {
 +    fn function_returning_unit<F: FnMut(i32)>(f: F) {}
 +    function_returning_unit(|x| std::process::exit(x));
 +
 +    fn arr() -> &'static [u8; 0] {
 +        &[]
 +    }
 +    fn slice_fn(_: impl FnOnce() -> &'static [u8]) {}
 +    slice_fn(|| arr());
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/7861
 +fn box_dyn() {
 +    fn f(_: impl Fn(usize) -> Box<dyn std::any::Any>) {}
 +    f(|x| Box::new(x));
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/5939
 +fn not_general_enough() {
 +    fn f(_: impl FnMut(&Path) -> std::io::Result<()>) {}
 +    f(|path| std::fs::remove_file(path));
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/9369
 +pub fn mutable_impl_fn_mut(mut f: impl FnMut(), mut f_used_once: impl FnMut()) -> impl FnMut() {
 +    fn takes_fn_mut(_: impl FnMut()) {}
 +    takes_fn_mut(|| f());
 +
 +    fn takes_fn_once(_: impl FnOnce()) {}
 +    takes_fn_once(|| f());
 +
 +    f();
 +
 +    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());
++}
index 434706b7e258f07d70cd75e9523b4807c06a739c,0000000000000000000000000000000000000000..a521fb868607ad8de2aafb9e3085a6b6bfd12f1e
mode 100644,000000..100644
--- /dev/null
@@@ -1,138 -1,0 +1,162 @@@
- error: aborting due to 22 previous errors
 +error: redundant closure
 +  --> $DIR/eta.rs:28:27
 +   |
 +LL |     let a = Some(1u8).map(|a| foo(a));
 +   |                           ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
 +   |
 +   = note: `-D clippy::redundant-closure` implied by `-D warnings`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:32:40
 +   |
 +LL |     let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
 +   |                                        ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:33:35
 +   |
 +LL |     let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
 +   |                                   ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:34:26
 +   |
 +LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
 +   |                          ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:41:27
 +   |
 +LL |     let e = Some(1u8).map(|a| generic(a));
 +   |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:87:51
 +   |
 +LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
 +   |                                                   ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
 +   |
 +   = note: `-D clippy::redundant-closure-for-method-calls` implied by `-D warnings`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:88:51
 +   |
 +LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
 +   |                                                   ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:90:42
 +   |
 +LL |     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
 +   |                                          ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:94:29
 +   |
 +LL |     let e = Some("str").map(|s| s.to_string());
 +   |                             ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:95:27
 +   |
 +LL |     let e = Some('a').map(|s| s.to_uppercase());
 +   |                           ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:97:65
 +   |
 +LL |     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
 +   |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:160:22
 +   |
 +LL |     requires_fn_once(|| x());
 +   |                      ^^^^^^ help: replace the closure with the function itself: `x`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:167:27
 +   |
 +LL |     let a = Some(1u8).map(|a| foo_ptr(a));
 +   |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:172:27
 +   |
 +LL |     let a = Some(1u8).map(|a| closure(a));
 +   |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:204:28
 +   |
 +LL |     x.into_iter().for_each(|x| add_to_res(x));
 +   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:205:28
 +   |
 +LL |     y.into_iter().for_each(|x| add_to_res(x));
 +   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:206:28
 +   |
 +LL |     z.into_iter().for_each(|x| add_to_res(x));
 +   |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:213:21
 +   |
 +LL |         Some(1).map(|n| closure(n));
 +   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:217:21
 +   |
 +LL |         Some(1).map(|n| in_loop(n));
 +   |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:310:18
 +   |
 +LL |     takes_fn_mut(|| f());
 +   |                  ^^^^^^ help: replace the closure with the function itself: `&mut f`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:313:19
 +   |
 +LL |     takes_fn_once(|| f());
 +   |                   ^^^^^^ help: replace the closure with the function itself: `&mut f`
 +
 +error: redundant closure
 +  --> $DIR/eta.rs:317:26
 +   |
 +LL |     move || takes_fn_mut(|| f_used_once())
 +   |                          ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
 +
++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
 +
index 59ff5e4040a3d6b67443d71ea52f6f8b1eb13ba4,0000000000000000000000000000000000000000..475fae5e823b31e66a484cae692c3ed6ab0ed2b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,280 -1,0 +1,284 @@@
 +// run-rustfix
 +
 +#![feature(closure_lifetime_binder)]
 +#![warn(clippy::explicit_auto_deref)]
 +#![allow(
 +    dead_code,
 +    unused_braces,
 +    clippy::borrowed_box,
 +    clippy::needless_borrow,
 +    clippy::needless_return,
 +    clippy::ptr_arg,
 +    clippy::redundant_field_names,
 +    clippy::too_many_arguments,
 +    clippy::borrow_deref_ref,
 +    clippy::let_unit_value
 +)]
 +
 +trait CallableStr {
 +    type T: Fn(&str);
 +    fn callable_str(&self) -> Self::T;
 +}
 +impl CallableStr for () {
 +    type T = fn(&str);
 +    fn callable_str(&self) -> Self::T {
 +        fn f(_: &str) {}
 +        f
 +    }
 +}
 +impl CallableStr for i32 {
 +    type T = <() as CallableStr>::T;
 +    fn callable_str(&self) -> Self::T {
 +        ().callable_str()
 +    }
 +}
 +
 +trait CallableT<U: ?Sized> {
 +    type T: Fn(&U);
 +    fn callable_t(&self) -> Self::T;
 +}
 +impl<U: ?Sized> CallableT<U> for () {
 +    type T = fn(&U);
 +    fn callable_t(&self) -> Self::T {
 +        fn f<U: ?Sized>(_: &U) {}
 +        f::<U>
 +    }
 +}
 +impl<U: ?Sized> CallableT<U> for i32 {
 +    type T = <() as CallableT<U>>::T;
 +    fn callable_t(&self) -> Self::T {
 +        ().callable_t()
 +    }
 +}
 +
 +fn f_str(_: &str) {}
 +fn f_string(_: &String) {}
 +fn f_t<T>(_: T) {}
 +fn f_ref_t<T: ?Sized>(_: &T) {}
 +
 +fn f_str_t<T>(_: &str, _: T) {}
 +
 +fn f_box_t<T>(_: &Box<T>) {}
 +
 +extern "C" {
 +    fn var(_: u32, ...);
 +}
 +
 +fn main() {
 +    let s = String::new();
 +
 +    let _: &str = &s;
 +    let _: &str = &{ String::new() };
 +    let _: &str = &mut { String::new() };
 +    let _ = &*s; // Don't lint. Inferred type would change.
 +    let _: &_ = &*s; // Don't lint. Inferred type would change.
 +
 +    f_str(&s);
 +    f_t(&*s); // Don't lint. Inferred type would change.
 +    f_ref_t(&*s); // Don't lint. Inferred type would change.
 +
 +    f_str_t(&s, &*s); // Don't lint second param.
 +
 +    let b = Box::new(Box::new(Box::new(5)));
 +    let _: &Box<i32> = &b;
 +    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
 +
 +    f_box_t(&**b); // Don't lint. Inferred type would change.
 +
 +    let c = |_x: &str| ();
 +    c(&s);
 +
 +    let c = |_x| ();
 +    c(&*s); // Don't lint. Inferred type would change.
 +
 +    fn _f(x: &String) -> &str {
 +        x
 +    }
 +
 +    fn _f1(x: &String) -> &str {
 +        { x }
 +    }
 +
 +    fn _f2(x: &String) -> &str {
 +        { x }
 +    }
 +
 +    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
 +        x
 +    }
 +
 +    fn _f4(
 +        x: String,
 +        f1: impl Fn(&str),
 +        f2: &dyn Fn(&str),
 +        f3: fn(&str),
 +        f4: impl CallableStr,
 +        f5: <() as CallableStr>::T,
 +        f6: <i32 as CallableStr>::T,
 +        f7: &dyn CallableStr<T = fn(&str)>,
 +        f8: impl CallableT<str>,
 +        f9: <() as CallableT<str>>::T,
 +        f10: <i32 as CallableT<str>>::T,
 +        f11: &dyn CallableT<str, T = fn(&str)>,
 +    ) {
 +        f1(&x);
 +        f2(&x);
 +        f3(&x);
 +        f4.callable_str()(&x);
 +        f5(&x);
 +        f6(&x);
 +        f7.callable_str()(&x);
 +        f8.callable_t()(&x);
 +        f9(&x);
 +        f10(&x);
 +        f11.callable_t()(&x);
 +    }
 +
 +    struct S1<'a>(&'a str);
 +    let _ = S1(&s);
 +
 +    struct S2<'a> {
 +        s: &'a str,
 +    }
 +    let _ = S2 { s: &s };
 +
 +    struct S3<'a, T: ?Sized>(&'a T);
 +    let _ = S3(&*s); // Don't lint. Inferred type would change.
 +
 +    struct S4<'a, T: ?Sized> {
 +        s: &'a T,
 +    }
 +    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    enum E1<'a> {
 +        S1(&'a str),
 +        S2 { s: &'a str },
 +    }
 +    impl<'a> E1<'a> {
 +        fn m1(s: &'a String) {
 +            let _ = Self::S1(s);
 +            let _ = Self::S2 { s: s };
 +        }
 +    }
 +    let _ = E1::S1(&s);
 +    let _ = E1::S2 { s: &s };
 +
 +    enum E2<'a, T: ?Sized> {
 +        S1(&'a T),
 +        S2 { s: &'a T },
 +    }
 +    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
 +    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    let ref_s = &s;
 +    let _: &String = &*ref_s; // Don't lint reborrow.
 +    f_string(&*ref_s); // Don't lint reborrow.
 +
 +    struct S5 {
 +        foo: u32,
 +    }
 +    let b = Box::new(Box::new(S5 { foo: 5 }));
 +    let _ = b.foo;
 +    let _ = b.foo;
 +    let _ = b.foo;
 +
 +    struct S6 {
 +        foo: S5,
 +    }
 +    impl core::ops::Deref for S6 {
 +        type Target = S5;
 +        fn deref(&self) -> &Self::Target {
 +            &self.foo
 +        }
 +    }
 +    let s6 = S6 { foo: S5 { foo: 5 } };
 +    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
 +
 +    let ref_str = &"foo";
 +    let _ = f_str(ref_str);
 +    let ref_ref_str = &ref_str;
 +    let _ = f_str(ref_ref_str);
 +
 +    fn _f5(x: &u32) -> u32 {
 +        if true {
 +            *x
 +        } else {
 +            return *x;
 +        }
 +    }
 +
 +    f_str(&&ref_str); // `needless_borrow` will suggest removing both references
 +    f_str(&ref_str); // `needless_borrow` will suggest removing only one reference
 +
 +    let x = &&40;
 +    unsafe {
 +        var(0, &**x);
 +    }
 +
 +    let s = &"str";
 +    let _ = || return *s;
 +    let _ = || -> &'static str { return s };
 +
 +    struct X;
 +    struct Y(X);
 +    impl core::ops::Deref for Y {
 +        type Target = X;
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    let _: &X = &*{ Y(X) };
 +    let _: &X = &*match 0 {
 +        #[rustfmt::skip]
 +        0 => { Y(X) },
 +        _ => panic!(),
 +    };
 +    let _: &X = &*if true { Y(X) } else { panic!() };
 +
 +    fn deref_to_u<U, T: core::ops::Deref<Target = U>>(x: &T) -> &U {
 +        x
 +    }
 +
 +    let _ = |x: &'static Box<dyn Iterator<Item = u32>>| -> &'static dyn Iterator<Item = u32> { &**x };
 +    fn ret_any(x: &Box<dyn std::any::Any>) -> &dyn std::any::Any {
 +        &**x
 +    }
 +
 +    let x = String::new();
 +    let _: *const str = &*x;
 +
 +    struct S7([u32; 1]);
 +    impl core::ops::Deref for S7 {
 +        type Target = [u32; 1];
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    let x = S7([0]);
 +    let _: &[u32] = &*x;
 +
 +    let c1 = |_: &Vec<&u32>| {};
 +    let x = &&vec![&1u32];
 +    c1(x);
 +    let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> {
 +        if b {
 +            return x;
 +        }
 +        x
 +    };
 +
 +    trait WithAssoc {
 +        type Assoc: ?Sized;
 +    }
 +    impl WithAssoc for String {
 +        type Assoc = str;
 +    }
 +    fn takes_assoc<T: WithAssoc>(_: &T::Assoc) -> T {
 +        unimplemented!()
 +    }
 +    let _: String = takes_assoc(&*String::new());
++
++    // Issue #9901
++    fn takes_ref(_: &i32) {}
++    takes_ref(*Box::new(&0i32));
 +}
index bcfb60c32788642fafe8e4acedbf326e2ee19c06,0000000000000000000000000000000000000000..c1894258f4d84312d891f55472908489c5173aba
mode 100644,000000..100644
--- /dev/null
@@@ -1,280 -1,0 +1,284 @@@
 +// run-rustfix
 +
 +#![feature(closure_lifetime_binder)]
 +#![warn(clippy::explicit_auto_deref)]
 +#![allow(
 +    dead_code,
 +    unused_braces,
 +    clippy::borrowed_box,
 +    clippy::needless_borrow,
 +    clippy::needless_return,
 +    clippy::ptr_arg,
 +    clippy::redundant_field_names,
 +    clippy::too_many_arguments,
 +    clippy::borrow_deref_ref,
 +    clippy::let_unit_value
 +)]
 +
 +trait CallableStr {
 +    type T: Fn(&str);
 +    fn callable_str(&self) -> Self::T;
 +}
 +impl CallableStr for () {
 +    type T = fn(&str);
 +    fn callable_str(&self) -> Self::T {
 +        fn f(_: &str) {}
 +        f
 +    }
 +}
 +impl CallableStr for i32 {
 +    type T = <() as CallableStr>::T;
 +    fn callable_str(&self) -> Self::T {
 +        ().callable_str()
 +    }
 +}
 +
 +trait CallableT<U: ?Sized> {
 +    type T: Fn(&U);
 +    fn callable_t(&self) -> Self::T;
 +}
 +impl<U: ?Sized> CallableT<U> for () {
 +    type T = fn(&U);
 +    fn callable_t(&self) -> Self::T {
 +        fn f<U: ?Sized>(_: &U) {}
 +        f::<U>
 +    }
 +}
 +impl<U: ?Sized> CallableT<U> for i32 {
 +    type T = <() as CallableT<U>>::T;
 +    fn callable_t(&self) -> Self::T {
 +        ().callable_t()
 +    }
 +}
 +
 +fn f_str(_: &str) {}
 +fn f_string(_: &String) {}
 +fn f_t<T>(_: T) {}
 +fn f_ref_t<T: ?Sized>(_: &T) {}
 +
 +fn f_str_t<T>(_: &str, _: T) {}
 +
 +fn f_box_t<T>(_: &Box<T>) {}
 +
 +extern "C" {
 +    fn var(_: u32, ...);
 +}
 +
 +fn main() {
 +    let s = String::new();
 +
 +    let _: &str = &*s;
 +    let _: &str = &*{ String::new() };
 +    let _: &str = &mut *{ String::new() };
 +    let _ = &*s; // Don't lint. Inferred type would change.
 +    let _: &_ = &*s; // Don't lint. Inferred type would change.
 +
 +    f_str(&*s);
 +    f_t(&*s); // Don't lint. Inferred type would change.
 +    f_ref_t(&*s); // Don't lint. Inferred type would change.
 +
 +    f_str_t(&*s, &*s); // Don't lint second param.
 +
 +    let b = Box::new(Box::new(Box::new(5)));
 +    let _: &Box<i32> = &**b;
 +    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
 +
 +    f_box_t(&**b); // Don't lint. Inferred type would change.
 +
 +    let c = |_x: &str| ();
 +    c(&*s);
 +
 +    let c = |_x| ();
 +    c(&*s); // Don't lint. Inferred type would change.
 +
 +    fn _f(x: &String) -> &str {
 +        &**x
 +    }
 +
 +    fn _f1(x: &String) -> &str {
 +        { &**x }
 +    }
 +
 +    fn _f2(x: &String) -> &str {
 +        &**{ x }
 +    }
 +
 +    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
 +        &***x
 +    }
 +
 +    fn _f4(
 +        x: String,
 +        f1: impl Fn(&str),
 +        f2: &dyn Fn(&str),
 +        f3: fn(&str),
 +        f4: impl CallableStr,
 +        f5: <() as CallableStr>::T,
 +        f6: <i32 as CallableStr>::T,
 +        f7: &dyn CallableStr<T = fn(&str)>,
 +        f8: impl CallableT<str>,
 +        f9: <() as CallableT<str>>::T,
 +        f10: <i32 as CallableT<str>>::T,
 +        f11: &dyn CallableT<str, T = fn(&str)>,
 +    ) {
 +        f1(&*x);
 +        f2(&*x);
 +        f3(&*x);
 +        f4.callable_str()(&*x);
 +        f5(&*x);
 +        f6(&*x);
 +        f7.callable_str()(&*x);
 +        f8.callable_t()(&*x);
 +        f9(&*x);
 +        f10(&*x);
 +        f11.callable_t()(&*x);
 +    }
 +
 +    struct S1<'a>(&'a str);
 +    let _ = S1(&*s);
 +
 +    struct S2<'a> {
 +        s: &'a str,
 +    }
 +    let _ = S2 { s: &*s };
 +
 +    struct S3<'a, T: ?Sized>(&'a T);
 +    let _ = S3(&*s); // Don't lint. Inferred type would change.
 +
 +    struct S4<'a, T: ?Sized> {
 +        s: &'a T,
 +    }
 +    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    enum E1<'a> {
 +        S1(&'a str),
 +        S2 { s: &'a str },
 +    }
 +    impl<'a> E1<'a> {
 +        fn m1(s: &'a String) {
 +            let _ = Self::S1(&**s);
 +            let _ = Self::S2 { s: &**s };
 +        }
 +    }
 +    let _ = E1::S1(&*s);
 +    let _ = E1::S2 { s: &*s };
 +
 +    enum E2<'a, T: ?Sized> {
 +        S1(&'a T),
 +        S2 { s: &'a T },
 +    }
 +    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
 +    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
 +
 +    let ref_s = &s;
 +    let _: &String = &*ref_s; // Don't lint reborrow.
 +    f_string(&*ref_s); // Don't lint reborrow.
 +
 +    struct S5 {
 +        foo: u32,
 +    }
 +    let b = Box::new(Box::new(S5 { foo: 5 }));
 +    let _ = b.foo;
 +    let _ = (*b).foo;
 +    let _ = (**b).foo;
 +
 +    struct S6 {
 +        foo: S5,
 +    }
 +    impl core::ops::Deref for S6 {
 +        type Target = S5;
 +        fn deref(&self) -> &Self::Target {
 +            &self.foo
 +        }
 +    }
 +    let s6 = S6 { foo: S5 { foo: 5 } };
 +    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
 +
 +    let ref_str = &"foo";
 +    let _ = f_str(*ref_str);
 +    let ref_ref_str = &ref_str;
 +    let _ = f_str(**ref_ref_str);
 +
 +    fn _f5(x: &u32) -> u32 {
 +        if true {
 +            *x
 +        } else {
 +            return *x;
 +        }
 +    }
 +
 +    f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
 +    f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
 +
 +    let x = &&40;
 +    unsafe {
 +        var(0, &**x);
 +    }
 +
 +    let s = &"str";
 +    let _ = || return *s;
 +    let _ = || -> &'static str { return *s };
 +
 +    struct X;
 +    struct Y(X);
 +    impl core::ops::Deref for Y {
 +        type Target = X;
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    let _: &X = &*{ Y(X) };
 +    let _: &X = &*match 0 {
 +        #[rustfmt::skip]
 +        0 => { Y(X) },
 +        _ => panic!(),
 +    };
 +    let _: &X = &*if true { Y(X) } else { panic!() };
 +
 +    fn deref_to_u<U, T: core::ops::Deref<Target = U>>(x: &T) -> &U {
 +        &**x
 +    }
 +
 +    let _ = |x: &'static Box<dyn Iterator<Item = u32>>| -> &'static dyn Iterator<Item = u32> { &**x };
 +    fn ret_any(x: &Box<dyn std::any::Any>) -> &dyn std::any::Any {
 +        &**x
 +    }
 +
 +    let x = String::new();
 +    let _: *const str = &*x;
 +
 +    struct S7([u32; 1]);
 +    impl core::ops::Deref for S7 {
 +        type Target = [u32; 1];
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    let x = S7([0]);
 +    let _: &[u32] = &*x;
 +
 +    let c1 = |_: &Vec<&u32>| {};
 +    let x = &&vec![&1u32];
 +    c1(*x);
 +    let _ = for<'a, 'b> |x: &'a &'a Vec<&'b u32>, b: bool| -> &'a Vec<&'b u32> {
 +        if b {
 +            return *x;
 +        }
 +        *x
 +    };
 +
 +    trait WithAssoc {
 +        type Assoc: ?Sized;
 +    }
 +    impl WithAssoc for String {
 +        type Assoc = str;
 +    }
 +    fn takes_assoc<T: WithAssoc>(_: &T::Assoc) -> T {
 +        unimplemented!()
 +    }
 +    let _: String = takes_assoc(&*String::new());
++
++    // Issue #9901
++    fn takes_ref(_: &i32) {}
++    takes_ref(*Box::new(&0i32));
 +}
index 41828ddd7acde22e0d7ad9e98a7df008a05b5d7f,0000000000000000000000000000000000000000..462d46169fcbec0dff6c86ca1d848df8cee13e5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,23 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.29"]
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(unused)]
 +
 +fn main() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +
 +    let element: Option<i32> = a.iter().find_map(|s| s.parse().ok());
 +    assert_eq!(element, Some(1));
 +}
 +
++#[clippy::msrv = "1.29"]
 +fn msrv_1_29() {
-     #![clippy::msrv = "1.30"]
 +    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() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +    let _: Option<i32> = a.iter().find_map(|s| s.parse().ok());
 +}
index be492a81b45ec3e638c0087c3108705e55ffd9cb,0000000000000000000000000000000000000000..2ea00cf73072e6abecc95e73dc07c45d2d46da96
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,23 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.29"]
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(unused)]
 +
 +fn main() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +
 +    let element: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 +    assert_eq!(element, Some(1));
 +}
 +
++#[clippy::msrv = "1.29"]
 +fn msrv_1_29() {
-     #![clippy::msrv = "1.30"]
 +    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() {
 +    let a = ["1", "lol", "3", "NaN", "5"];
 +    let _: Option<i32> = a.iter().filter_map(|s| s.parse().ok()).next();
 +}
index e789efeabd5503a16eec2e7d531200f7a8875e1d,0000000000000000000000000000000000000000..a9fc6abe88facbda0ab9009ee0063ed5d14a14db
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
-   --> $DIR/filter_map_next_fixable.rs:10:32
 +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: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())`
 +   |
 +   = 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: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())`
 +
 +error: aborting due to 2 previous errors
 +
index 1cf49ca45f494d1eca6d0a02785ca04781747755,0000000000000000000000000000000000000000..125c9a69cd3fd1fdd48f8b7a37d7672d84f18a9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,84 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.40"]
 +#![warn(clippy::from_over_into)]
 +#![allow(unused)]
 +
 +// this should throw an error
 +struct StringWrapper(String);
 +
 +impl From<String> for StringWrapper {
 +    fn from(val: String) -> Self {
 +        StringWrapper(val)
 +    }
 +}
 +
 +struct SelfType(String);
 +
 +impl From<String> for SelfType {
 +    fn from(val: String) -> Self {
 +        SelfType(String::new())
 +    }
 +}
 +
 +#[derive(Default)]
 +struct X;
 +
 +impl X {
 +    const FOO: &'static str = "a";
 +}
 +
 +struct SelfKeywords;
 +
 +impl From<X> for SelfKeywords {
 +    fn from(val: X) -> Self {
 +        let _ = X::default();
 +        let _ = X::FOO;
 +        let _: X = val;
 +
 +        SelfKeywords
 +    }
 +}
 +
 +struct ExplicitPaths(bool);
 +
 +impl core::convert::From<crate::ExplicitPaths> for bool {
 +    fn from(mut val: crate::ExplicitPaths) -> Self {
 +        let in_closure = || val.0;
 +
 +        val.0 = false;
 +        val.0
 +    }
 +}
 +
 +// this is fine
 +struct A(String);
 +
 +impl From<String> for A {
 +    fn from(s: String) -> A {
 +        A(s)
 +    }
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
-     #![clippy::msrv = "1.41"]
 +    struct FromOverInto<T>(Vec<T>);
 +
 +    impl<T> Into<FromOverInto<T>> for Vec<T> {
 +        fn into(self) -> FromOverInto<T> {
 +            FromOverInto(self)
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.41"]
 +fn msrv_1_41() {
 +    struct FromOverInto<T>(Vec<T>);
 +
 +    impl<T> From<Vec<T>> for FromOverInto<T> {
 +        fn from(val: Vec<T>) -> Self {
 +            FromOverInto(val)
 +        }
 +    }
 +}
 +
 +fn main() {}
index d30f3c3fc92567ba78b18523e3b6aa2b891d5de8,0000000000000000000000000000000000000000..5aa127bfabe4b792a59ab914277c7034d7e06b2b
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,84 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.40"]
 +#![warn(clippy::from_over_into)]
 +#![allow(unused)]
 +
 +// this should throw an error
 +struct StringWrapper(String);
 +
 +impl Into<StringWrapper> for String {
 +    fn into(self) -> StringWrapper {
 +        StringWrapper(self)
 +    }
 +}
 +
 +struct SelfType(String);
 +
 +impl Into<SelfType> for String {
 +    fn into(self) -> SelfType {
 +        SelfType(Self::new())
 +    }
 +}
 +
 +#[derive(Default)]
 +struct X;
 +
 +impl X {
 +    const FOO: &'static str = "a";
 +}
 +
 +struct SelfKeywords;
 +
 +impl Into<SelfKeywords> for X {
 +    fn into(self) -> SelfKeywords {
 +        let _ = Self::default();
 +        let _ = Self::FOO;
 +        let _: Self = self;
 +
 +        SelfKeywords
 +    }
 +}
 +
 +struct ExplicitPaths(bool);
 +
 +impl core::convert::Into<bool> for crate::ExplicitPaths {
 +    fn into(mut self) -> bool {
 +        let in_closure = || self.0;
 +
 +        self.0 = false;
 +        self.0
 +    }
 +}
 +
 +// this is fine
 +struct A(String);
 +
 +impl From<String> for A {
 +    fn from(s: String) -> A {
 +        A(s)
 +    }
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
-     #![clippy::msrv = "1.41"]
 +    struct FromOverInto<T>(Vec<T>);
 +
 +    impl<T> Into<FromOverInto<T>> for Vec<T> {
 +        fn into(self) -> FromOverInto<T> {
 +            FromOverInto(self)
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.41"]
 +fn msrv_1_41() {
 +    struct FromOverInto<T>(Vec<T>);
 +
 +    impl<T> Into<FromOverInto<T>> for Vec<T> {
 +        fn into(self) -> FromOverInto<T> {
 +            FromOverInto(self)
 +        }
 +    }
 +}
 +
 +fn main() {}
index 9c2a7c04c3646a84252479dea7e42a15319d9bb8,0000000000000000000000000000000000000000..a1764a5ea12ae8f5deef37156c803d244523c4f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,75 @@@
-   --> $DIR/from_over_into.rs:10:1
 +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:9:1
 +   |
 +LL | impl Into<StringWrapper> for String {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::from-over-into` implied by `-D warnings`
 +help: replace the `Into` implentation with `From<std::string::String>`
 +   |
 +LL ~ impl From<String> for StringWrapper {
 +LL ~     fn from(val: String) -> Self {
 +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:33:1
++  --> $DIR/from_over_into.rs:17:1
 +   |
 +LL | impl Into<SelfType> for String {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: replace the `Into` implentation with `From<std::string::String>`
 +   |
 +LL ~ impl From<String> for SelfType {
 +LL ~     fn from(val: String) -> Self {
 +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:45:1
++  --> $DIR/from_over_into.rs:32:1
 +   |
 +LL | impl Into<SelfKeywords> for X {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: replace the `Into` implentation with `From<X>`
 +   |
 +LL ~ impl From<X> for SelfKeywords {
 +LL ~     fn from(val: X) -> Self {
 +LL ~         let _ = X::default();
 +LL ~         let _ = X::FOO;
 +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:80:5
++  --> $DIR/from_over_into.rs:44:1
 +   |
 +LL | impl core::convert::Into<bool> for crate::ExplicitPaths {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: `impl From<Local> for Foreign` is allowed by the orphan rules, for more information see
 +           https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence
 +help: replace the `Into` implentation with `From<ExplicitPaths>`
 +   |
 +LL ~ impl core::convert::From<crate::ExplicitPaths> for bool {
 +LL ~     fn from(mut val: crate::ExplicitPaths) -> Self {
 +LL ~         let in_closure = || val.0;
 +LL | 
 +LL ~         val.0 = false;
 +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:77:5
 +   |
 +LL |     impl<T> Into<FromOverInto<T>> for Vec<T> {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: replace the `Into` implentation with `From<std::vec::Vec<T>>`
 +   |
 +LL ~     impl<T> From<Vec<T>> for FromOverInto<T> {
 +LL ~         fn from(val: Vec<T>) -> Self {
 +LL ~             FromOverInto(val)
 +   |
 +
 +error: aborting due to 5 previous errors
 +
index 3bc3a0395244cea27e73a6bf61aaf0f8690af1c0,0000000000000000000000000000000000000000..0e89fdb0dfa28c028d5c7df3edc22ff125512386
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,114 @@@
- #![feature(custom_inner_attributes)]
 +#![warn(clippy::if_then_some_else_none)]
-     #![clippy::msrv = "1.49"]
 +
 +fn main() {
 +    // Should issue an error.
 +    let _ = if foo() {
 +        println!("true!");
 +        Some("foo")
 +    } else {
 +        None
 +    };
 +
 +    // Should issue an error when macros are used.
 +    let _ = if matches!(true, true) {
 +        println!("true!");
 +        Some(matches!(true, false))
 +    } else {
 +        None
 +    };
 +
 +    // Should issue an error. Binary expression `o < 32` should be parenthesized.
 +    let x = Some(5);
 +    let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
 +
 +    // Should issue an error. Unary expression `!x` should be parenthesized.
 +    let x = true;
 +    let _ = if !x { Some(0) } else { None };
 +
 +    // Should not issue an error since the `else` block has a statement besides `None`.
 +    let _ = if foo() {
 +        println!("true!");
 +        Some("foo")
 +    } else {
 +        eprintln!("false...");
 +        None
 +    };
 +
 +    // Should not issue an error since there are more than 2 blocks in the if-else chain.
 +    let _ = if foo() {
 +        println!("foo true!");
 +        Some("foo")
 +    } else if bar() {
 +        println!("bar true!");
 +        Some("bar")
 +    } else {
 +        None
 +    };
 +
 +    let _ = if foo() {
 +        println!("foo true!");
 +        Some("foo")
 +    } else {
 +        bar().then(|| {
 +            println!("bar true!");
 +            "bar"
 +        })
 +    };
 +
 +    // Should not issue an error since the `then` block has `None`, not `Some`.
 +    let _ = if foo() { None } else { Some("foo is false") };
 +
 +    // Should not issue an error since the `else` block doesn't use `None` directly.
 +    let _ = if foo() { Some("foo is true") } else { into_none() };
 +
 +    // Should not issue an error since the `then` block doesn't use `Some` directly.
 +    let _ = if foo() { into_some("foo") } else { None };
 +}
 +
++#[clippy::msrv = "1.49"]
 +fn _msrv_1_49() {
-     #![clippy::msrv = "1.50"]
 +    // `bool::then` was stabilized in 1.50. Do not lint this
 +    let _ = if foo() {
 +        println!("true!");
 +        Some(149)
 +    } else {
 +        None
 +    };
 +}
 +
++#[clippy::msrv = "1.50"]
 +fn _msrv_1_50() {
 +    let _ = if foo() {
 +        println!("true!");
 +        Some(150)
 +    } else {
 +        None
 +    };
 +}
 +
 +fn foo() -> bool {
 +    unimplemented!()
 +}
 +
 +fn bar() -> bool {
 +    unimplemented!()
 +}
 +
 +fn into_some<T>(v: T) -> Option<T> {
 +    Some(v)
 +}
 +
 +fn into_none<T>() -> Option<T> {
 +    None
 +}
 +
 +// Should not warn
 +fn f(b: bool, v: Option<()>) -> Option<()> {
 +    if b {
 +        v?; // This is a potential early return, is not equivalent with `bool::then`
 +
 +        Some(())
 +    } else {
 +        None
 +    }
 +}
index 24e0b5947f19dd44e436cfddc3bf45c40a816f77,0000000000000000000000000000000000000000..d728a3c31a3ba95fabc9b0fa3d587946a1eb5ab3
mode 100644,000000..100644
--- /dev/null
@@@ -1,61 -1,0 +1,61 @@@
-   --> $DIR/if_then_some_else_none.rs:6:13
 +error: this could be simplified with `bool::then`
-   --> $DIR/if_then_some_else_none.rs:14:13
++  --> $DIR/if_then_some_else_none.rs:5:13
 +   |
 +LL |       let _ = if foo() {
 +   |  _____________^
 +LL | |         println!("true!");
 +LL | |         Some("foo")
 +LL | |     } else {
 +LL | |         None
 +LL | |     };
 +   | |_____^
 +   |
 +   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })`
 +   = 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:23:28
++  --> $DIR/if_then_some_else_none.rs:13:13
 +   |
 +LL |       let _ = if matches!(true, true) {
 +   |  _____________^
 +LL | |         println!("true!");
 +LL | |         Some(matches!(true, false))
 +LL | |     } else {
 +LL | |         None
 +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:27:13
++  --> $DIR/if_then_some_else_none.rs:22:28
 +   |
 +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:82:13
++  --> $DIR/if_then_some_else_none.rs:26:13
 +   |
 +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:81:13
 +   |
 +LL |       let _ = if foo() {
 +   |  _____________^
 +LL | |         println!("true!");
 +LL | |         Some(150)
 +LL | |     } else {
 +LL | |         None
 +LL | |     };
 +   | |_____^
 +   |
 +   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })`
 +
 +error: aborting due to 5 previous errors
 +
index 331fd29b74e8edb4bcffddc33b66b3d7948e32d4,0000000000000000000000000000000000000000..f7902e6fd53893b717509873b314486a99da2c56
mode 100644,000000..100644
--- /dev/null
@@@ -1,331 -1,0 +1,328 @@@
- #![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_clamp)]
 +#![allow(
 +    unused,
 +    dead_code,
 +    clippy::unnecessary_operation,
 +    clippy::no_effect,
 +    clippy::if_same_then_else
 +)]
 +
 +use std::cmp::{max as cmp_max, min as cmp_min};
 +
 +const CONST_MAX: i32 = 10;
 +const CONST_MIN: i32 = 4;
 +
 +const CONST_F64_MAX: f64 = 10.0;
 +const CONST_F64_MIN: f64 = 4.0;
 +
 +fn main() {
 +    let (input, min, max) = (0, -2, 3);
 +    // Lint
 +    let x0 = if max < input {
 +        max
 +    } else if min > input {
 +        min
 +    } else {
 +        input
 +    };
 +
 +    let x1 = if input > max {
 +        max
 +    } else if input < min {
 +        min
 +    } else {
 +        input
 +    };
 +
 +    let x2 = if input < min {
 +        min
 +    } else if input > max {
 +        max
 +    } else {
 +        input
 +    };
 +
 +    let x3 = if min > input {
 +        min
 +    } else if max < input {
 +        max
 +    } else {
 +        input
 +    };
 +
 +    let x4 = input.max(min).min(max);
 +
 +    let x5 = input.min(max).max(min);
 +
 +    let x6 = match input {
 +        x if x > max => max,
 +        x if x < min => min,
 +        x => x,
 +    };
 +
 +    let x7 = match input {
 +        x if x < min => min,
 +        x if x > max => max,
 +        x => x,
 +    };
 +
 +    let x8 = match input {
 +        x if max < x => max,
 +        x if min > x => min,
 +        x => x,
 +    };
 +
 +    let mut x9 = input;
 +    if x9 < min {
 +        x9 = min;
 +    }
 +    if x9 > max {
 +        x9 = max;
 +    }
 +
 +    let x10 = match input {
 +        x if min > x => min,
 +        x if max < x => max,
 +        x => x,
 +    };
 +
 +    let mut x11 = input;
 +    let _ = 1;
 +    if x11 > max {
 +        x11 = max;
 +    }
 +    if x11 < min {
 +        x11 = min;
 +    }
 +
 +    let mut x12 = input;
 +    if min > x12 {
 +        x12 = min;
 +    }
 +    if max < x12 {
 +        x12 = max;
 +    }
 +
 +    let mut x13 = input;
 +    if max < x13 {
 +        x13 = max;
 +    }
 +    if min > x13 {
 +        x13 = min;
 +    }
 +
 +    let x14 = if input > CONST_MAX {
 +        CONST_MAX
 +    } else if input < CONST_MIN {
 +        CONST_MIN
 +    } else {
 +        input
 +    };
 +    {
 +        let (input, min, max) = (0.0f64, -2.0, 3.0);
 +        let x15 = if input > max {
 +            max
 +        } else if input < min {
 +            min
 +        } else {
 +            input
 +        };
 +    }
 +    {
 +        let input: i32 = cmp_min_max(1);
 +        // These can only be detected if exactly one of the arguments to the inner function is const.
 +        let x16 = cmp_max(cmp_min(input, CONST_MAX), CONST_MIN);
 +        let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
 +        let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
 +        let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
 +        let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
 +        let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
 +        let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
 +        let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
 +        let input: f64 = cmp_min_max(1) as f64;
 +        let x24 = f64::max(f64::min(input, CONST_F64_MAX), CONST_F64_MIN);
 +        let x25 = f64::min(f64::max(input, CONST_F64_MIN), CONST_F64_MAX);
 +        let x26 = f64::max(CONST_F64_MIN, f64::min(input, CONST_F64_MAX));
 +        let x27 = f64::min(CONST_F64_MAX, f64::max(input, CONST_F64_MIN));
 +        let x28 = f64::max(f64::min(CONST_F64_MAX, input), CONST_F64_MIN);
 +        let x29 = f64::min(f64::max(CONST_F64_MIN, input), CONST_F64_MAX);
 +        let x30 = f64::max(CONST_F64_MIN, f64::min(CONST_F64_MAX, input));
 +        let x31 = f64::min(CONST_F64_MAX, f64::max(CONST_F64_MIN, input));
 +    }
 +    let mut x32 = input;
 +    if x32 < min {
 +        x32 = min;
 +    } else if x32 > max {
 +        x32 = max;
 +    }
 +
 +    // It's important this be the last set of statements
 +    let mut x33 = input;
 +    if max < x33 {
 +        x33 = max;
 +    }
 +    if min > x33 {
 +        x33 = min;
 +    }
 +}
 +
 +// This code intentionally nonsense.
 +fn no_lint() {
 +    let (input, min, max) = (0, -2, 3);
 +    let x0 = if max < input {
 +        max
 +    } else if min > input {
 +        max
 +    } else {
 +        min
 +    };
 +
 +    let x1 = if input > max {
 +        max
 +    } else if input > min {
 +        min
 +    } else {
 +        max
 +    };
 +
 +    let x2 = if max < min {
 +        min
 +    } else if input > max {
 +        input
 +    } else {
 +        input
 +    };
 +
 +    let x3 = if min > input {
 +        input
 +    } else if max < input {
 +        max
 +    } else {
 +        max
 +    };
 +
 +    let x6 = match input {
 +        x if x < max => x,
 +        x if x < min => x,
 +        x => x,
 +    };
 +
 +    let x7 = match input {
 +        x if x < min => max,
 +        x if x > max => min,
 +        x => x,
 +    };
 +
 +    let x8 = match input {
 +        x if max > x => max,
 +        x if min > x => min,
 +        x => x,
 +    };
 +
 +    let mut x9 = input;
 +    if x9 > min {
 +        x9 = min;
 +    }
 +    if x9 > max {
 +        x9 = max;
 +    }
 +
 +    let x10 = match input {
 +        x if min > x => min,
 +        x if max < x => max,
 +        x => min,
 +    };
 +
 +    let mut x11 = input;
 +    if x11 > max {
 +        x11 = min;
 +    }
 +    if x11 < min {
 +        x11 = max;
 +    }
 +
 +    let mut x12 = input;
 +    if min > x12 {
 +        x12 = max * 3;
 +    }
 +    if max < x12 {
 +        x12 = min;
 +    }
 +
 +    let mut x13 = input;
 +    if max < x13 {
 +        let x13 = max;
 +    }
 +    if min > x13 {
 +        x13 = min;
 +    }
 +    let mut x14 = input;
 +    if x14 < min {
 +        x14 = 3;
 +    } else if x14 > max {
 +        x14 = max;
 +    }
 +    {
 +        let input: i32 = cmp_min_max(1);
 +        // These can only be detected if exactly one of the arguments to the inner function is const.
 +        let x16 = cmp_max(cmp_max(input, CONST_MAX), CONST_MIN);
 +        let x17 = cmp_min(cmp_min(input, CONST_MIN), CONST_MAX);
 +        let x18 = cmp_max(CONST_MIN, cmp_max(input, CONST_MAX));
 +        let x19 = cmp_min(CONST_MAX, cmp_min(input, CONST_MIN));
 +        let x20 = cmp_max(cmp_max(CONST_MAX, input), CONST_MIN);
 +        let x21 = cmp_min(cmp_min(CONST_MIN, input), CONST_MAX);
 +        let x22 = cmp_max(CONST_MIN, cmp_max(CONST_MAX, input));
 +        let x23 = cmp_min(CONST_MAX, cmp_min(CONST_MIN, input));
 +        let input: f64 = cmp_min_max(1) as f64;
 +        let x24 = f64::max(f64::max(input, CONST_F64_MAX), CONST_F64_MIN);
 +        let x25 = f64::min(f64::min(input, CONST_F64_MIN), CONST_F64_MAX);
 +        let x26 = f64::max(CONST_F64_MIN, f64::max(input, CONST_F64_MAX));
 +        let x27 = f64::min(CONST_F64_MAX, f64::min(input, CONST_F64_MIN));
 +        let x28 = f64::max(f64::max(CONST_F64_MAX, input), CONST_F64_MIN);
 +        let x29 = f64::min(f64::min(CONST_F64_MIN, input), CONST_F64_MAX);
 +        let x30 = f64::max(CONST_F64_MIN, f64::max(CONST_F64_MAX, input));
 +        let x31 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, input));
 +        let x32 = f64::min(CONST_F64_MAX, f64::min(CONST_F64_MIN, CONST_F64_MAX));
 +    }
 +}
 +
 +fn dont_tell_me_what_to_do() {
 +    let (input, min, max) = (0, -2, 3);
 +    let mut x_never = input;
 +    #[allow(clippy::manual_clamp)]
 +    if x_never < min {
 +        x_never = min;
 +    }
 +    if x_never > max {
 +        x_never = max;
 +    }
 +}
 +
 +/// Just to ensure this isn't const evaled
 +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
 +    } else if input > max {
 +        max
 +    } else {
 +        input
 +    };
 +}
 +
++#[clippy::msrv = "1.50"]
 +fn msrv_1_50() {
-     #![clippy::msrv = "1.50"]
 +    let (input, min, max) = (0, -1, 2);
 +    let _ = if input < min {
 +        min
 +    } else if input > max {
 +        max
 +    } else {
 +        input
 +    };
 +}
index 70abe28091c94ac5570761805e55f5ecfc21517f,0000000000000000000000000000000000000000..988ad1527e837aa9528ff263f28b526ee062afb0
mode 100644,000000..100644
--- /dev/null
@@@ -1,390 -1,0 +1,390 @@@
-   --> $DIR/manual_clamp.rs:77:5
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:92:5
++  --> $DIR/manual_clamp.rs:76:5
 +   |
 +LL | /     if x9 < min {
 +LL | |         x9 = min;
 +LL | |     }
 +LL | |     if x9 > max {
 +LL | |         x9 = max;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x9 = x9.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +   = note: `-D clippy::manual-clamp` implied by `-D warnings`
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:100:5
++  --> $DIR/manual_clamp.rs:91:5
 +   |
 +LL | /     if x11 > max {
 +LL | |         x11 = max;
 +LL | |     }
 +LL | |     if x11 < min {
 +LL | |         x11 = min;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x11 = x11.clamp(min, max);`
 +   |
 +   = 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:99:5
 +   |
 +LL | /     if min > x12 {
 +LL | |         x12 = min;
 +LL | |     }
 +LL | |     if max < x12 {
 +LL | |         x12 = max;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x12 = x12.clamp(min, max);`
 +   |
 +   = 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:107:5
 +   |
 +LL | /     if max < x13 {
 +LL | |         x13 = max;
 +LL | |     }
 +LL | |     if min > x13 {
 +LL | |         x13 = min;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x13 = x13.clamp(min, max);`
 +   |
 +   = 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:161:5
 +   |
 +LL | /     if max < x33 {
 +LL | |         x33 = max;
 +LL | |     }
 +LL | |     if min > x33 {
 +LL | |         x33 = min;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x33 = x33.clamp(min, max);`
 +   |
 +   = 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:21:14
 +   |
 +LL |       let x0 = if max < input {
 +   |  ______________^
 +LL | |         max
 +LL | |     } else if min > input {
 +LL | |         min
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:29:14
 +   |
 +LL |       let x1 = if input > max {
 +   |  ______________^
 +LL | |         max
 +LL | |     } else if input < min {
 +LL | |         min
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:37:14
 +   |
 +LL |       let x2 = if input < min {
 +   |  ______________^
 +LL | |         min
 +LL | |     } else if input > max {
 +LL | |         max
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:45:14
 +   |
 +LL |       let x3 = if min > input {
 +   |  ______________^
 +LL | |         min
 +LL | |     } else if max < input {
 +LL | |         max
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(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:53:14
 +   |
 +LL |     let x4 = input.max(min).min(max);
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:55:14
 +   |
 +LL |     let x5 = input.min(max).max(min);
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:57:14
 +   |
 +LL |       let x6 = match input {
 +   |  ______________^
 +LL | |         x if x > max => max,
 +LL | |         x if x < min => min,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:63:14
 +   |
 +LL |       let x7 = match input {
 +   |  ______________^
 +LL | |         x if x < min => min,
 +LL | |         x if x > max => max,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:69:14
 +   |
 +LL |       let x8 = match input {
 +   |  ______________^
 +LL | |         x if max < x => max,
 +LL | |         x if min > x => min,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:83:15
 +   |
 +LL |       let x10 = match input {
 +   |  _______________^
 +LL | |         x if min > x => min,
 +LL | |         x if max < x => max,
 +LL | |         x => x,
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = 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:114:15
 +   |
 +LL |       let x14 = if input > CONST_MAX {
 +   |  _______________^
 +LL | |         CONST_MAX
 +LL | |     } else if input < CONST_MIN {
 +LL | |         CONST_MIN
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:135:19
++  --> $DIR/manual_clamp.rs:123:19
 +   |
 +LL |           let x15 = if input > max {
 +   |  ___________________^
 +LL | |             max
 +LL | |         } else if input < min {
 +LL | |             min
 +LL | |         } else {
 +LL | |             input
 +LL | |         };
 +   | |_________^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:136: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)`
 +   |
 +   = 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:135:19
 +   |
 +LL |         let x17 = cmp_min(cmp_max(input, CONST_MIN), CONST_MAX);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, 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:136:19
 +   |
 +LL |         let x18 = cmp_max(CONST_MIN, cmp_min(input, CONST_MAX));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = 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:137:19
 +   |
 +LL |         let x19 = cmp_min(CONST_MAX, cmp_max(input, CONST_MIN));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = 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:138:19
 +   |
 +LL |         let x20 = cmp_max(cmp_min(CONST_MAX, input), CONST_MIN);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, 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:139:19
 +   |
 +LL |         let x21 = cmp_min(cmp_max(CONST_MIN, input), CONST_MAX);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = 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:140:19
 +   |
 +LL |         let x22 = cmp_max(CONST_MIN, cmp_min(CONST_MAX, input));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = 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:141:19
 +   |
 +LL |         let x23 = cmp_min(CONST_MAX, cmp_max(CONST_MIN, input));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with clamp: `input.clamp(CONST_MIN, CONST_MAX)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:145: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = 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: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)`
 +   |
 +   = note: clamp will panic if max < min, min.is_nan(), or max.is_nan()
 +   = note: clamp returns NaN if the input is NaN
 +
 +error: clamp-like pattern without using clamp function
-   --> $DIR/manual_clamp.rs:324:13
++  --> $DIR/manual_clamp.rs:153:5
 +   |
 +LL | /     if x32 < min {
 +LL | |         x32 = min;
 +LL | |     } else if x32 > max {
 +LL | |         x32 = max;
 +LL | |     }
 +   | |_____^ help: replace with clamp: `x32 = x32.clamp(min, max);`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: clamp-like pattern without using clamp function
++  --> $DIR/manual_clamp.rs:321:13
 +   |
 +LL |       let _ = if input < min {
 +   |  _____________^
 +LL | |         min
 +LL | |     } else if input > max {
 +LL | |         max
 +LL | |     } else {
 +LL | |         input
 +LL | |     };
 +   | |_____^ help: replace with clamp: `input.clamp(min, max)`
 +   |
 +   = note: clamp will panic if max < min
 +
 +error: aborting due to 35 previous errors
 +
index 765bb785994e21275752475450dc439b3e4e2300,0000000000000000000000000000000000000000..231ba83b1426130a3b6482d8e9cb938aacfced4d
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,42 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.23"]
 +#![allow(unused, dead_code)]
 +#![warn(clippy::manual_is_ascii_check)]
 +
 +fn main() {
 +    assert!('x'.is_ascii_lowercase());
 +    assert!('X'.is_ascii_uppercase());
 +    assert!(b'x'.is_ascii_lowercase());
 +    assert!(b'X'.is_ascii_uppercase());
 +
 +    let num = '2';
 +    assert!(num.is_ascii_digit());
 +    assert!(b'1'.is_ascii_digit());
 +    assert!('x'.is_ascii_alphabetic());
 +
 +    assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
 +}
 +
++#[clippy::msrv = "1.23"]
 +fn msrv_1_23() {
-     #![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.24"]
 +fn msrv_1_24() {
-     #![clippy::msrv = "1.46"]
 +    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.47"]
 +    const FOO: bool = matches!('x', '0'..='9');
 +}
 +
++#[clippy::msrv = "1.47"]
 +fn msrv_1_47() {
 +    const FOO: bool = 'x'.is_ascii_digit();
 +}
index be133161041234b251968cafde2e28208c80027b,0000000000000000000000000000000000000000..39ee6151c56f08ff53b77377e9eb916b437d00a6
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,42 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.23"]
 +#![allow(unused, dead_code)]
 +#![warn(clippy::manual_is_ascii_check)]
 +
 +fn main() {
 +    assert!(matches!('x', 'a'..='z'));
 +    assert!(matches!('X', 'A'..='Z'));
 +    assert!(matches!(b'x', b'a'..=b'z'));
 +    assert!(matches!(b'X', b'A'..=b'Z'));
 +
 +    let num = '2';
 +    assert!(matches!(num, '0'..='9'));
 +    assert!(matches!(b'1', b'0'..=b'9'));
 +    assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
 +
 +    assert!(matches!('x', 'A'..='Z' | 'a'..='z' | '_'));
 +}
 +
++#[clippy::msrv = "1.23"]
 +fn msrv_1_23() {
-     #![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.24"]
 +fn msrv_1_24() {
-     #![clippy::msrv = "1.46"]
 +    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.47"]
 +    const FOO: bool = matches!('x', '0'..='9');
 +}
 +
++#[clippy::msrv = "1.47"]
 +fn msrv_1_47() {
 +    const FOO: bool = matches!('x', '0'..='9');
 +}
index c0a9d4db1a15960f9d47c605c6321b087c270e17,0000000000000000000000000000000000000000..397cbe05c822ad7fba97b5f46d02fd569e2882fe
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,70 @@@
-   --> $DIR/manual_is_ascii_check.rs:8:13
 +error: manual check for common ascii range
-   --> $DIR/manual_is_ascii_check.rs:9:13
++  --> $DIR/manual_is_ascii_check.rs:7:13
 +   |
 +LL |     assert!(matches!('x', 'a'..='z'));
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_lowercase()`
 +   |
 +   = note: `-D clippy::manual-is-ascii-check` implied by `-D warnings`
 +
 +error: manual check for common ascii range
-   --> $DIR/manual_is_ascii_check.rs:10: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:11: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:14: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:15: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:16: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:32: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:33: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:34: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:44:23
++  --> $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:41:23
 +   |
 +LL |     const FOO: bool = matches!('x', '0'..='9');
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
 +
 +error: aborting due to 11 previous errors
 +
index 2ef40e5911af46d077b302bcb78e787bce50b5d4,0000000000000000000000000000000000000000..48a162c13602ca800b3fcfd8f2392b01181b94fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,237 -1,0 +1,251 @@@
 +#![allow(unused_braces, unused_variables, dead_code)]
 +#![allow(
 +    clippy::collapsible_else_if,
 +    clippy::unused_unit,
 +    clippy::let_unit_value,
 +    clippy::match_single_binding,
 +    clippy::never_loop
 +)]
 +#![warn(clippy::manual_let_else)]
 +
 +fn g() -> Option<()> {
 +    None
 +}
 +
 +fn main() {}
 +
 +fn fire() {
 +    let v = if let Some(v_some) = g() { v_some } else { return };
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        return;
 +    };
 +
 +    let v = if let Some(v) = g() {
 +        // Blocks around the identity should have no impact
 +        {
 +            { v }
 +        }
 +    } else {
 +        // Some computation should still make it fire
 +        g();
 +        return;
 +    };
 +
 +    // continue and break diverge
 +    loop {
 +        let v = if let Some(v_some) = g() { v_some } else { continue };
 +        let v = if let Some(v_some) = g() { v_some } else { break };
 +    }
 +
 +    // panic also diverges
 +    let v = if let Some(v_some) = g() { v_some } else { panic!() };
 +
 +    // abort also diverges
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        std::process::abort()
 +    };
 +
 +    // If whose two branches diverge also diverges
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        if true { return } else { panic!() }
 +    };
 +
 +    // Diverging after an if still makes the block diverge:
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        if true {}
 +        panic!();
 +    };
 +
 +    // A match diverges if all branches diverge:
 +    // Note: the corresponding let-else requires a ; at the end of the match
 +    // as otherwise the type checker does not turn it into a ! type.
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        match () {
 +            _ if panic!() => {},
 +            _ => panic!(),
 +        }
 +    };
 +
 +    // An if's expression can cause divergence:
 +    let v = if let Some(v_some) = g() { v_some } else { if panic!() {} };
 +
 +    // An expression of a match can cause divergence:
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        match panic!() {
 +            _ => {},
 +        }
 +    };
 +
 +    // Top level else if
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else if true {
 +        return;
 +    } else {
 +        panic!("diverge");
 +    };
 +
 +    // All match arms diverge
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        match (g(), g()) {
 +            (Some(_), None) => return,
 +            (None, Some(_)) => {
 +                if true {
 +                    return;
 +                } else {
 +                    panic!();
 +                }
 +            },
 +            _ => return,
 +        }
 +    };
 +
 +    // Tuples supported for the declared variables
 +    let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
 +        v_some
 +    } else {
 +        return;
 +    };
 +
 +    // Tuples supported for the identity block and pattern
 +    let v = if let (Some(v_some), w_some) = (g(), 0) {
 +        (w_some, v_some)
 +    } else {
 +        return;
 +    };
 +
 +    // entirely inside macro lints
 +    macro_rules! create_binding_if_some {
 +        ($n:ident, $e:expr) => {
 +            let $n = if let Some(v) = $e { v } else { return };
 +        };
 +    }
 +    create_binding_if_some!(w, g());
 +}
 +
 +fn not_fire() {
 +    let v = if let Some(v_some) = g() {
 +        // Nothing returned. Should not fire.
 +    } else {
 +        return;
 +    };
 +
 +    let w = 0;
 +    let v = if let Some(v_some) = g() {
 +        // Different variable than v_some. Should not fire.
 +        w
 +    } else {
 +        return;
 +    };
 +
 +    let v = if let Some(v_some) = g() {
 +        // Computation in then clause. Should not fire.
 +        g();
 +        v_some
 +    } else {
 +        return;
 +    };
 +
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        if false {
 +            return;
 +        }
 +        // This doesn't diverge. Should not fire.
 +        ()
 +    };
 +
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        // There is one match arm that doesn't diverge. Should not fire.
 +        match (g(), g()) {
 +            (Some(_), None) => return,
 +            (None, Some(_)) => return,
 +            (Some(_), Some(_)) => (),
 +            _ => return,
 +        }
 +    };
 +
 +    let v = if let Some(v_some) = g() {
 +        v_some
 +    } else {
 +        // loop with a break statement inside does not diverge.
 +        loop {
 +            break;
 +        }
 +    };
 +
 +    enum Uninhabited {}
 +    fn un() -> Uninhabited {
 +        panic!()
 +    }
 +    let v = if let Some(v_some) = None {
 +        v_some
 +    } else {
 +        // Don't lint if the type is uninhabited but not !
 +        un()
 +    };
 +
 +    fn question_mark() -> Option<()> {
 +        let v = if let Some(v) = g() {
 +            v
 +        } else {
 +            // Question mark does not diverge
 +            g()?
 +        };
 +        Some(v)
 +    }
 +
 +    // Macro boundary inside let
 +    macro_rules! some_or_return {
 +        ($e:expr) => {
 +            if let Some(v) = $e { v } else { return }
 +        };
 +    }
 +    let v = some_or_return!(g());
 +
 +    // Also macro boundary inside let, but inside a macro
 +    macro_rules! create_binding_if_some_nf {
 +        ($n:ident, $e:expr) => {
 +            let $n = some_or_return!($e);
 +        };
 +    }
 +    create_binding_if_some_nf!(v, g());
 +
 +    // Already a let-else
 +    let Some(a) = (if let Some(b) = Some(Some(())) { b } else { return }) else { panic!() };
 +
 +    // 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!(),
++    };
 +}
index 453b68b8bd003c8f43cadd240412ff0a146ad07b,0000000000000000000000000000000000000000..52aac6bc673d195500f6f3ddf84fccacdd9aa65b
mode 100644,000000..100644
--- /dev/null
@@@ -1,263 -1,0 +1,272 @@@
- error: aborting due to 17 previous errors
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:18:5
 +   |
 +LL |     let v = if let Some(v_some) = g() { v_some } else { return };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { return };`
 +   |
 +   = note: `-D clippy::manual-let-else` implied by `-D warnings`
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:19:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         return;
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         return;
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:25:5
 +   |
 +LL | /     let v = if let Some(v) = g() {
 +LL | |         // Blocks around the identity should have no impact
 +LL | |         {
 +LL | |             { v }
 +...  |
 +LL | |         return;
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v) = g() else {
 +LL +         // Some computation should still make it fire
 +LL +         g();
 +LL +         return;
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:38:9
 +   |
 +LL |         let v = if let Some(v_some) = g() { v_some } else { continue };
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { continue };`
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:39:9
 +   |
 +LL |         let v = if let Some(v_some) = g() { v_some } else { break };
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { break };`
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:43:5
 +   |
 +LL |     let v = if let Some(v_some) = g() { v_some } else { panic!() };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { panic!() };`
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:46:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         std::process::abort()
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         std::process::abort()
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:53:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         if true { return } else { panic!() }
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         if true { return } else { panic!() }
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:60:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         if true {}
 +LL | |         panic!();
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         if true {}
 +LL +         panic!();
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:70:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         match () {
 +...  |
 +LL | |         }
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         match () {
 +LL +             _ if panic!() => {},
 +LL +             _ => panic!(),
 +LL +         }
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:80:5
 +   |
 +LL |     let v = if let Some(v_some) = g() { v_some } else { if panic!() {} };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v_some) = g() else { if panic!() {} };`
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:83:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         match panic!() {
 +LL | |             _ => {},
 +LL | |         }
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         match panic!() {
 +LL +             _ => {},
 +LL +         }
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:92:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else if true {
 +LL | |         return;
 +LL | |     } else {
 +LL | |         panic!("diverge");
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else { if true {
 +LL +         return;
 +LL +     } else {
 +LL +         panic!("diverge");
 +LL +     } };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:101:5
 +   |
 +LL | /     let v = if let Some(v_some) = g() {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         match (g(), g()) {
 +...  |
 +LL | |         }
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g() else {
 +LL +         match (g(), g()) {
 +LL +             (Some(_), None) => return,
 +LL +             (None, Some(_)) => {
 +LL +                 if true {
 +LL +                     return;
 +LL +                 } else {
 +LL +                     panic!();
 +LL +                 }
 +LL +             },
 +LL +             _ => return,
 +LL +         }
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:118:5
 +   |
 +LL | /     let (v, w) = if let Some(v_some) = g().map(|v| (v, 42)) {
 +LL | |         v_some
 +LL | |     } else {
 +LL | |         return;
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let Some(v_some) = g().map(|v| (v, 42)) else {
 +LL +         return;
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:125:5
 +   |
 +LL | /     let v = if let (Some(v_some), w_some) = (g(), 0) {
 +LL | |         (w_some, v_some)
 +LL | |     } else {
 +LL | |         return;
 +LL | |     };
 +   | |______^
 +   |
 +help: consider writing
 +   |
 +LL ~     let (Some(v_some), w_some) = (g(), 0) else {
 +LL +         return;
 +LL +     };
 +   |
 +
 +error: this could be rewritten as `let...else`
 +  --> $DIR/manual_let_else.rs:134:13
 +   |
 +LL |             let $n = if let Some(v) = $e { v } else { return };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider writing: `let Some(v) = g() else { return };`
 +...
 +LL |     create_binding_if_some!(w, g());
 +   |     ------------------------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `create_binding_if_some` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
++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
 +
index b942fbfe93056aa45c24dc06c12678db0be8a202,0000000000000000000000000000000000000000..4cdc0546a7446ab6176a759ed867d6048ccb0bad
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,80 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
-     #![clippy::msrv = "1.37"]
 +#![warn(clippy::manual_rem_euclid)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +macro_rules! internal_rem_euclid {
 +    () => {
 +        let value: i32 = 5;
 +        let _: i32 = value.rem_euclid(4);
 +    };
 +}
 +
 +fn main() {
 +    let value: i32 = 5;
 +
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = value.rem_euclid(4);
 +    let _: i32 = 1 + value.rem_euclid(4);
 +
 +    let _: i32 = (3 + value % 4) % 4;
 +    let _: i32 = (-4 + value % -4) % -4;
 +    let _: i32 = ((5 % 4) + 4) % 4;
 +
 +    // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
 +    // integer type
 +    let not_annotated = 24;
 +    let _ = ((not_annotated % 4) + 4) % 4;
 +    let inferred: _ = 24;
 +    let _ = ((inferred % 4) + 4) % 4;
 +
 +    // For lint to apply the constant must always be on the RHS of the previous value for %
 +    let _: i32 = 4 % ((value % 4) + 4);
 +    let _: i32 = ((4 % value) + 4) % 4;
 +
 +    // Lint in internal macros
 +    internal_rem_euclid!();
 +
 +    // Do not lint in external macros
 +    manual_rem_euclid!();
 +}
 +
 +// Should lint for params too
 +pub fn rem_euclid_4(num: i32) -> i32 {
 +    num.rem_euclid(4)
 +}
 +
 +// Constant version came later, should still lint
 +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.38"]
 +    let x: i32 = 10;
 +    let _: i32 = ((x % 4) + 4) % 4;
 +}
 +
++#[clippy::msrv = "1.38"]
 +pub fn msrv_1_38() {
-     #![clippy::msrv = "1.51"]
 +    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.52"]
 +    let x: i32 = 10;
 +    let _: i32 = ((x % 4) + 4) % 4;
 +}
 +
++#[clippy::msrv = "1.52"]
 +pub const fn msrv_1_52() {
 +    let x: i32 = 10;
 +    let _: i32 = x.rem_euclid(4);
 +}
index 7462d532169f6fb890a4b62131821330fdd96b04,0000000000000000000000000000000000000000..58a9e20f38b1375c3fba248fa4b0321af34e8270
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,80 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
-     #![clippy::msrv = "1.37"]
 +#![warn(clippy::manual_rem_euclid)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +macro_rules! internal_rem_euclid {
 +    () => {
 +        let value: i32 = 5;
 +        let _: i32 = ((value % 4) + 4) % 4;
 +    };
 +}
 +
 +fn main() {
 +    let value: i32 = 5;
 +
 +    let _: i32 = ((value % 4) + 4) % 4;
 +    let _: i32 = (4 + (value % 4)) % 4;
 +    let _: i32 = (value % 4 + 4) % 4;
 +    let _: i32 = (4 + value % 4) % 4;
 +    let _: i32 = 1 + (4 + value % 4) % 4;
 +
 +    let _: i32 = (3 + value % 4) % 4;
 +    let _: i32 = (-4 + value % -4) % -4;
 +    let _: i32 = ((5 % 4) + 4) % 4;
 +
 +    // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
 +    // integer type
 +    let not_annotated = 24;
 +    let _ = ((not_annotated % 4) + 4) % 4;
 +    let inferred: _ = 24;
 +    let _ = ((inferred % 4) + 4) % 4;
 +
 +    // For lint to apply the constant must always be on the RHS of the previous value for %
 +    let _: i32 = 4 % ((value % 4) + 4);
 +    let _: i32 = ((4 % value) + 4) % 4;
 +
 +    // Lint in internal macros
 +    internal_rem_euclid!();
 +
 +    // Do not lint in external macros
 +    manual_rem_euclid!();
 +}
 +
 +// Should lint for params too
 +pub fn rem_euclid_4(num: i32) -> i32 {
 +    ((num % 4) + 4) % 4
 +}
 +
 +// Constant version came later, should still lint
 +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.38"]
 +    let x: i32 = 10;
 +    let _: i32 = ((x % 4) + 4) % 4;
 +}
 +
++#[clippy::msrv = "1.38"]
 +pub fn msrv_1_38() {
-     #![clippy::msrv = "1.51"]
 +    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.52"]
 +    let x: i32 = 10;
 +    let _: i32 = ((x % 4) + 4) % 4;
 +}
 +
++#[clippy::msrv = "1.52"]
 +pub const fn msrv_1_52() {
 +    let x: i32 = 10;
 +    let _: i32 = ((x % 4) + 4) % 4;
 +}
index d51bac03b565afaa278696da4742e04fc6a894da,0000000000000000000000000000000000000000..e3122a588b64d1d4a2d204eadcc06223c009a27b
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,69 @@@
-   --> $DIR/manual_rem_euclid.rs:20:18
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:21:18
++  --> $DIR/manual_rem_euclid.rs:19:18
 +   |
 +LL |     let _: i32 = ((value % 4) + 4) % 4;
 +   |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +   |
 +   = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
 +
 +error: manual `rem_euclid` implementation
-   --> $DIR/manual_rem_euclid.rs:22: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:23: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:24:22
++  --> $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:13: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:50:5
++  --> $DIR/manual_rem_euclid.rs:12:22
 +   |
 +LL |         let _: i32 = ((value % 4) + 4) % 4;
 +   |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
 +...
 +LL |     internal_rem_euclid!();
 +   |     ---------------------- in this macro invocation
 +   |
 +   = 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:55: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:69:18
++  --> $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:84: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:79:18
 +   |
 +LL |     let _: i32 = ((x % 4) + 4) % 4;
 +   |                  ^^^^^^^^^^^^^^^^^ help: consider using: `x.rem_euclid(4)`
 +
 +error: aborting due to 10 previous errors
 +
index fba503a20667ac4d7c35a9456aef5296f4e558e1,0000000000000000000000000000000000000000..e5ae3cf3e5033295b16936b7f71fac65269834f7
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,239 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
-     #![clippy::msrv = "1.52"]
 +#![warn(clippy::manual_retain)]
 +#![allow(unused)]
 +use std::collections::BTreeMap;
 +use std::collections::BTreeSet;
 +use std::collections::BinaryHeap;
 +use std::collections::HashMap;
 +use std::collections::HashSet;
 +use std::collections::VecDeque;
 +
 +fn main() {
 +    binary_heap_retain();
 +    btree_set_retain();
 +    btree_map_retain();
 +    hash_set_retain();
 +    hash_map_retain();
 +    string_retain();
 +    vec_deque_retain();
 +    vec_retain();
 +    _msrv_153();
 +    _msrv_126();
 +    _msrv_118();
 +}
 +
 +fn binary_heap_retain() {
 +    // NOTE: Do not lint now, because binary_heap_retain is nighyly API.
 +    // And we need to add a test case for msrv if we update this implmention.
 +    // https://github.com/rust-lang/rust/issues/71503
 +    let mut heap = BinaryHeap::from([1, 2, 3]);
 +    heap = heap.into_iter().filter(|x| x % 2 == 0).collect();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +
 +    // Do not lint, because type conversion is performed
 +    heap = heap.into_iter().filter(|x| x % 2 == 0).collect::<BinaryHeap<i8>>();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect::<BinaryHeap<i8>>();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect::<BinaryHeap<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: BinaryHeap<i8> = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: BinaryHeap<i8> = heap.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
 +fn btree_map_retain() {
 +    let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
 +    // Do lint.
 +    btree_map.retain(|k, _| k % 2 == 0);
 +    btree_map.retain(|_, &mut v| v % 2 == 0);
 +    btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
 +
 +    // Do not lint.
 +    btree_map = btree_map
 +        .into_iter()
 +        .filter(|(x, _)| x % 2 == 0)
 +        .collect::<BTreeMap<i8, i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut foobar: BTreeMap<i8, i8> = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    btree_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +}
 +
 +fn btree_set_retain() {
 +    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
 +
 +    // Do lint.
 +    btree_set.retain(|x| x % 2 == 0);
 +    btree_set.retain(|x| x % 2 == 0);
 +    btree_set.retain(|x| x % 2 == 0);
 +
 +    // Do not lint, because type conversion is performed
 +    btree_set = btree_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .copied()
 +        .collect::<BTreeSet<i8>>();
 +
 +    btree_set = btree_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .cloned()
 +        .collect::<BTreeSet<i8>>();
 +
 +    btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect::<BTreeSet<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut foobar: BTreeSet<i8> = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut bar: BTreeSet<i8> = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
 +fn hash_map_retain() {
 +    let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
 +    // Do lint.
 +    hash_map.retain(|k, _| k % 2 == 0);
 +    hash_map.retain(|_, &mut v| v % 2 == 0);
 +    hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
 +
 +    // Do not lint.
 +    hash_map = hash_map
 +        .into_iter()
 +        .filter(|(x, _)| x % 2 == 0)
 +        .collect::<HashMap<i8, i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut foobar: HashMap<i8, i8> = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    hash_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +}
 +
 +fn hash_set_retain() {
 +    let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
 +    // Do lint.
 +    hash_set.retain(|x| x % 2 == 0);
 +    hash_set.retain(|x| x % 2 == 0);
 +    hash_set.retain(|x| x % 2 == 0);
 +
 +    // Do not lint, because type conversion is performed
 +    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::<HashSet<i8>>();
 +    hash_set = hash_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .copied()
 +        .collect::<HashSet<i8>>();
 +
 +    hash_set = hash_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .cloned()
 +        .collect::<HashSet<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: HashSet<i8> = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: HashSet<i8> = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|&x| x % 2 == 0).collect();
 +}
 +
 +fn string_retain() {
 +    let mut s = String::from("foobar");
 +    // Do lint.
 +    s.retain(|c| c != 'o');
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    s = bar.chars().filter(|&c| c != 'o').to_owned().collect();
 +}
 +
 +fn vec_retain() {
 +    let mut vec = vec![0, 1, 2];
 +    // Do lint.
 +    vec.retain(|x| x % 2 == 0);
 +    vec.retain(|x| x % 2 == 0);
 +    vec.retain(|x| x % 2 == 0);
 +
 +    // Do not lint, because type conversion is performed
 +    vec = vec.into_iter().filter(|x| x % 2 == 0).collect::<Vec<i8>>();
 +    vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::<Vec<i8>>();
 +    vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect::<Vec<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: Vec<i8> = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: Vec<i8> = vec.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
 +fn vec_deque_retain() {
 +    let mut vec_deque = VecDeque::new();
 +    vec_deque.extend(1..5);
 +
 +    // Do lint.
 +    vec_deque.retain(|x| x % 2 == 0);
 +    vec_deque.retain(|x| x % 2 == 0);
 +    vec_deque.retain(|x| x % 2 == 0);
 +
 +    // Do not lint, because type conversion is performed
 +    vec_deque = vec_deque
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .copied()
 +        .collect::<VecDeque<i8>>();
 +    vec_deque = vec_deque
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .cloned()
 +        .collect::<VecDeque<i8>>();
 +    vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect::<VecDeque<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: VecDeque<i8> = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: VecDeque<i8> = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
++#[clippy::msrv = "1.52"]
 +fn _msrv_153() {
-     #![clippy::msrv = "1.25"]
 +    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();
 +
 +    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
 +    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +}
 +
++#[clippy::msrv = "1.25"]
 +fn _msrv_126() {
-     #![clippy::msrv = "1.17"]
 +    let mut s = String::from("foobar");
 +    s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn _msrv_118() {
 +    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();
 +    hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +}
index 81a849fe7684ccbb5e3fcbf6b11ae4392391f2c2,0000000000000000000000000000000000000000..1021f15edd1ea9c579aa8ceff38c17f12eb22414
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,245 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
-     #![clippy::msrv = "1.52"]
 +#![warn(clippy::manual_retain)]
 +#![allow(unused)]
 +use std::collections::BTreeMap;
 +use std::collections::BTreeSet;
 +use std::collections::BinaryHeap;
 +use std::collections::HashMap;
 +use std::collections::HashSet;
 +use std::collections::VecDeque;
 +
 +fn main() {
 +    binary_heap_retain();
 +    btree_set_retain();
 +    btree_map_retain();
 +    hash_set_retain();
 +    hash_map_retain();
 +    string_retain();
 +    vec_deque_retain();
 +    vec_retain();
 +    _msrv_153();
 +    _msrv_126();
 +    _msrv_118();
 +}
 +
 +fn binary_heap_retain() {
 +    // NOTE: Do not lint now, because binary_heap_retain is nighyly API.
 +    // And we need to add a test case for msrv if we update this implmention.
 +    // https://github.com/rust-lang/rust/issues/71503
 +    let mut heap = BinaryHeap::from([1, 2, 3]);
 +    heap = heap.into_iter().filter(|x| x % 2 == 0).collect();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +
 +    // Do not lint, because type conversion is performed
 +    heap = heap.into_iter().filter(|x| x % 2 == 0).collect::<BinaryHeap<i8>>();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect::<BinaryHeap<i8>>();
 +    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect::<BinaryHeap<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: BinaryHeap<i8> = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: BinaryHeap<i8> = heap.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
 +fn btree_map_retain() {
 +    let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
 +    // Do lint.
 +    btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +    btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
 +    btree_map = btree_map
 +        .into_iter()
 +        .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
 +        .collect();
 +
 +    // Do not lint.
 +    btree_map = btree_map
 +        .into_iter()
 +        .filter(|(x, _)| x % 2 == 0)
 +        .collect::<BTreeMap<i8, i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut foobar: BTreeMap<i8, i8> = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    btree_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +}
 +
 +fn btree_set_retain() {
 +    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
 +
 +    // Do lint.
 +    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because type conversion is performed
 +    btree_set = btree_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .copied()
 +        .collect::<BTreeSet<i8>>();
 +
 +    btree_set = btree_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .cloned()
 +        .collect::<BTreeSet<i8>>();
 +
 +    btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect::<BTreeSet<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut foobar: BTreeSet<i8> = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut bar: BTreeSet<i8> = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
 +fn hash_map_retain() {
 +    let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
 +    // Do lint.
 +    hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +    hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
 +    hash_map = hash_map
 +        .into_iter()
 +        .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
 +        .collect();
 +
 +    // Do not lint.
 +    hash_map = hash_map
 +        .into_iter()
 +        .filter(|(x, _)| x % 2 == 0)
 +        .collect::<HashMap<i8, i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut foobar: HashMap<i8, i8> = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    hash_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +}
 +
 +fn hash_set_retain() {
 +    let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
 +    // Do lint.
 +    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
 +    hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +
 +    // Do not lint, because type conversion is performed
 +    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::<HashSet<i8>>();
 +    hash_set = hash_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .copied()
 +        .collect::<HashSet<i8>>();
 +
 +    hash_set = hash_set
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .cloned()
 +        .collect::<HashSet<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: HashSet<i8> = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: HashSet<i8> = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|&x| x % 2 == 0).collect();
 +}
 +
 +fn string_retain() {
 +    let mut s = String::from("foobar");
 +    // Do lint.
 +    s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    s = bar.chars().filter(|&c| c != 'o').to_owned().collect();
 +}
 +
 +fn vec_retain() {
 +    let mut vec = vec![0, 1, 2];
 +    // Do lint.
 +    vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because type conversion is performed
 +    vec = vec.into_iter().filter(|x| x % 2 == 0).collect::<Vec<i8>>();
 +    vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::<Vec<i8>>();
 +    vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect::<Vec<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: Vec<i8> = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: Vec<i8> = vec.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
 +fn vec_deque_retain() {
 +    let mut vec_deque = VecDeque::new();
 +    vec_deque.extend(1..5);
 +
 +    // Do lint.
 +    vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because type conversion is performed
 +    vec_deque = vec_deque
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .copied()
 +        .collect::<VecDeque<i8>>();
 +    vec_deque = vec_deque
 +        .iter()
 +        .filter(|&x| x % 2 == 0)
 +        .cloned()
 +        .collect::<VecDeque<i8>>();
 +    vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect::<VecDeque<i8>>();
 +
 +    // Do not lint, because this expression is not assign.
 +    let mut bar: VecDeque<i8> = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    let mut foobar: VecDeque<i8> = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
 +
 +    // Do not lint, because it is an assignment to a different variable.
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
 +    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
 +    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
 +}
 +
++#[clippy::msrv = "1.52"]
 +fn _msrv_153() {
-     #![clippy::msrv = "1.25"]
 +    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();
 +
 +    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
 +    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
 +}
 +
++#[clippy::msrv = "1.25"]
 +fn _msrv_126() {
-     #![clippy::msrv = "1.17"]
 +    let mut s = String::from("foobar");
 +    s = s.chars().filter(|&c| c != 'o').to_owned().collect();
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn _msrv_118() {
 +    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();
 +    hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
 +}
index ec635919b48fb99814c6b908d4da38d8b4ec9084,0000000000000000000000000000000000000000..89316ce1d99818d83c236178381da84272d8d8f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,124 -1,0 +1,124 @@@
-   --> $DIR/manual_retain.rs:52:5
 +error: this expression can be written more simply using `.retain()`
-   --> $DIR/manual_retain.rs:53: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)`
 +   |
 +   = note: `-D clippy::manual-retain` implied by `-D warnings`
 +
 +error: this expression can be written more simply using `.retain()`
-   --> $DIR/manual_retain.rs:54: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:76:5
++  --> $DIR/manual_retain.rs:53:5
 +   |
 +LL | /     btree_map = btree_map
 +LL | |         .into_iter()
 +LL | |         .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
 +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:77: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:78: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:108: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:109: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:110: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:131:5
++  --> $DIR/manual_retain.rs:109:5
 +   |
 +LL | /     hash_map = hash_map
 +LL | |         .into_iter()
 +LL | |         .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
 +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:132: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:133: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:162: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:174: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:175: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:176: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:198: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:199: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:200: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: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)`
 +
 +error: aborting due to 19 previous errors
 +
index c7ca770434a318c53308c073bbb72ecc3e540749,0000000000000000000000000000000000000000..50b02019cc274f67c33d571a402c7e889f868b08
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,146 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.51"]
 +#![warn(clippy::manual_split_once)]
 +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
 +    let _ = "key=value".splitn(2, '=').nth(2);
 +    let _ = "key=value".split_once('=').unwrap().1;
 +    let _ = "key=value".split_once('=').unwrap().1;
 +    let (_, _) = "key=value".split_once('=').unwrap();
 +
 +    let s = String::from("key=value");
 +    let _ = s.split_once('=').unwrap().1;
 +
 +    let s = Box::<str>::from("key=value");
 +    let _ = s.split_once('=').unwrap().1;
 +
 +    let s = &"key=value";
 +    let _ = s.split_once('=').unwrap().1;
 +
 +    fn _f(s: &str) -> Option<&str> {
 +        let _ = s.split_once('=')?.1;
 +        let _ = s.split_once('=')?.1;
 +        let _ = s.rsplit_once('=')?.0;
 +        let _ = s.rsplit_once('=')?.0;
 +        None
 +    }
 +
 +    // Don't lint, slices don't have `split_once`
 +    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 +
 +    // `rsplitn` gives the results in the reverse order of `rsplit_once`
 +    let _ = "key=value".rsplit_once('=').unwrap().0;
 +    let (_, _) = "key=value".rsplit_once('=').map(|(x, y)| (y, x)).unwrap();
 +    let _ = s.rsplit_once('=').map(|x| x.0);
 +}
 +
 +fn indirect() -> Option<()> {
 +    let (l, r) = "a.b.c".split_once('.').unwrap();
 +    
 +    
 +
 +    let (l, r) = "a.b.c".split_once('.')?;
 +    
 +    
 +
 +    let (l, r) = "a.b.c".rsplit_once('.').unwrap();
 +    
 +    
 +
 +    let (l, r) = "a.b.c".rsplit_once('.')?;
 +    
 +    
 +
 +    // could lint, currently doesn't
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let other = 1;
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let mut mut_binding = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let tuple = (iter.next()?, iter.next()?);
 +
 +    // should not lint
 +
 +    let mut missing_unwrap = "a.b.c".splitn(2, '.');
 +    let l = missing_unwrap.next();
 +    let r = missing_unwrap.next();
 +
 +    let mut mixed_unrap = "a.b.c".splitn(2, '.');
 +    let unwrap = mixed_unrap.next().unwrap();
 +    let question_mark = mixed_unrap.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let same_name = iter.next()?;
 +    let same_name = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let shadows_existing = "d";
 +    let shadows_existing = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let becomes_shadowed = iter.next()?;
 +    let becomes_shadowed = "d";
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +    let third_usage = iter.next()?;
 +
 +    let mut n_three = "a.b.c".splitn(3, '.');
 +    let l = n_three.next()?;
 +    let r = n_three.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    {
 +        let in_block = iter.next()?;
 +    }
 +    let r = iter.next()?;
 +
 +    let mut lacks_binding = "a.b.c".splitn(2, '.');
 +    let _ = lacks_binding.next()?;
 +    let r = lacks_binding.next()?;
 +
 +    let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut assigned = "";
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let l = iter.next()?;
 +    assigned = iter.next()?;
 +
 +    None
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn _msrv_1_51() {
-     #![clippy::msrv = "1.52"]
 +    // `str::split_once` was stabilized in 1.52. Do not lint this
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let a = iter.next().unwrap();
 +    let b = iter.next().unwrap();
 +}
 +
++#[clippy::msrv = "1.52"]
 +fn _msrv_1_52() {
 +    let _ = "key=value".split_once('=').unwrap().1;
 +
 +    let (a, b) = "a.b.c".split_once('.').unwrap();
 +    
 +    
 +}
index ee2848a251ee3b2e666c49644a7768597e41782d,0000000000000000000000000000000000000000..e1e8b71a9deff62a3b716b033e777402a732d0e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,146 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.51"]
 +#![warn(clippy::manual_split_once)]
 +#![allow(unused, clippy::iter_skip_next, clippy::iter_nth_zero)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
 +    let _ = "key=value".splitn(2, '=').nth(2);
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 +    let _ = "key=value".splitn(2, '=').skip(1).next().unwrap();
 +    let (_, _) = "key=value".splitn(2, '=').next_tuple().unwrap();
 +
 +    let s = String::from("key=value");
 +    let _ = s.splitn(2, '=').nth(1).unwrap();
 +
 +    let s = Box::<str>::from("key=value");
 +    let _ = s.splitn(2, '=').nth(1).unwrap();
 +
 +    let s = &"key=value";
 +    let _ = s.splitn(2, '=').skip(1).next().unwrap();
 +
 +    fn _f(s: &str) -> Option<&str> {
 +        let _ = s.splitn(2, '=').nth(1)?;
 +        let _ = s.splitn(2, '=').skip(1).next()?;
 +        let _ = s.rsplitn(2, '=').nth(1)?;
 +        let _ = s.rsplitn(2, '=').skip(1).next()?;
 +        None
 +    }
 +
 +    // Don't lint, slices don't have `split_once`
 +    let _ = [0, 1, 2].splitn(2, |&x| x == 1).nth(1).unwrap();
 +
 +    // `rsplitn` gives the results in the reverse order of `rsplit_once`
 +    let _ = "key=value".rsplitn(2, '=').nth(1).unwrap();
 +    let (_, _) = "key=value".rsplitn(2, '=').next_tuple().unwrap();
 +    let _ = s.rsplitn(2, '=').nth(1);
 +}
 +
 +fn indirect() -> Option<()> {
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let l = iter.next().unwrap();
 +    let r = iter.next().unwrap();
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".rsplitn(2, '.');
 +    let r = iter.next().unwrap();
 +    let l = iter.next().unwrap();
 +
 +    let mut iter = "a.b.c".rsplitn(2, '.');
 +    let r = iter.next()?;
 +    let l = iter.next()?;
 +
 +    // could lint, currently doesn't
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let other = 1;
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let mut mut_binding = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let tuple = (iter.next()?, iter.next()?);
 +
 +    // should not lint
 +
 +    let mut missing_unwrap = "a.b.c".splitn(2, '.');
 +    let l = missing_unwrap.next();
 +    let r = missing_unwrap.next();
 +
 +    let mut mixed_unrap = "a.b.c".splitn(2, '.');
 +    let unwrap = mixed_unrap.next().unwrap();
 +    let question_mark = mixed_unrap.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let same_name = iter.next()?;
 +    let same_name = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let shadows_existing = "d";
 +    let shadows_existing = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let becomes_shadowed = iter.next()?;
 +    let becomes_shadowed = "d";
 +    let r = iter.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +    let third_usage = iter.next()?;
 +
 +    let mut n_three = "a.b.c".splitn(3, '.');
 +    let l = n_three.next()?;
 +    let r = n_three.next()?;
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    {
 +        let in_block = iter.next()?;
 +    }
 +    let r = iter.next()?;
 +
 +    let mut lacks_binding = "a.b.c".splitn(2, '.');
 +    let _ = lacks_binding.next()?;
 +    let r = lacks_binding.next()?;
 +
 +    let mut mapped = "a.b.c".splitn(2, '.').map(|_| "~");
 +    let l = iter.next()?;
 +    let r = iter.next()?;
 +
 +    let mut assigned = "";
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let l = iter.next()?;
 +    assigned = iter.next()?;
 +
 +    None
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn _msrv_1_51() {
-     #![clippy::msrv = "1.52"]
 +    // `str::split_once` was stabilized in 1.52. Do not lint this
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let a = iter.next().unwrap();
 +    let b = iter.next().unwrap();
 +}
 +
++#[clippy::msrv = "1.52"]
 +fn _msrv_1_52() {
 +    let _ = "key=value".splitn(2, '=').nth(1).unwrap();
 +
 +    let mut iter = "a.b.c".splitn(2, '.');
 +    let a = iter.next().unwrap();
 +    let b = iter.next().unwrap();
 +}
index 2696694680ad7abd56549743231a3b882444e28c,0000000000000000000000000000000000000000..78da5a16cc52b69366ad355a359c6e1d587594fe
mode 100644,000000..100644
--- /dev/null
@@@ -1,213 -1,0 +1,213 @@@
-   --> $DIR/manual_split_once.rs:14:13
 +error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:15: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`
 +   |
 +   = note: `-D clippy::manual-split-once` implied by `-D warnings`
 +
 +error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:16:18
++  --> $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:19:13
++  --> $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:22: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:25: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:28:17
++  --> $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:29: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:30: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:31: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:39:13
++  --> $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:40:18
++  --> $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:41:13
++  --> $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:45:5
++  --> $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:49:5
++  --> $DIR/manual_split_once.rs:44:5
 +   |
 +LL |     let mut iter = "a.b.c".splitn(2, '.');
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +LL |     let l = iter.next().unwrap();
 +   |     ----------------------------- first usage here
 +LL |     let r = iter.next().unwrap();
 +   |     ----------------------------- second usage here
 +   |
 +help: try `split_once`
 +   |
 +LL |     let (l, r) = "a.b.c".split_once('.').unwrap();
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let l = iter.next().unwrap();
 +LL +     
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let r = iter.next().unwrap();
 +LL +     
 +   |
 +
 +error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:53:5
++  --> $DIR/manual_split_once.rs:48:5
 +   |
 +LL |     let mut iter = "a.b.c".splitn(2, '.');
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +LL |     let l = iter.next()?;
 +   |     --------------------- first usage here
 +LL |     let r = iter.next()?;
 +   |     --------------------- second usage here
 +   |
 +help: try `split_once`
 +   |
 +LL |     let (l, r) = "a.b.c".split_once('.')?;
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let l = iter.next()?;
 +LL +     
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let r = iter.next()?;
 +LL +     
 +   |
 +
 +error: manual implementation of `rsplit_once`
-   --> $DIR/manual_split_once.rs:57:5
++  --> $DIR/manual_split_once.rs:52:5
 +   |
 +LL |     let mut iter = "a.b.c".rsplitn(2, '.');
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +LL |     let r = iter.next().unwrap();
 +   |     ----------------------------- first usage here
 +LL |     let l = iter.next().unwrap();
 +   |     ----------------------------- second usage here
 +   |
 +help: try `rsplit_once`
 +   |
 +LL |     let (l, r) = "a.b.c".rsplit_once('.').unwrap();
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let r = iter.next().unwrap();
 +LL +     
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let l = iter.next().unwrap();
 +LL +     
 +   |
 +
 +error: manual implementation of `rsplit_once`
-   --> $DIR/manual_split_once.rs:142:13
++  --> $DIR/manual_split_once.rs:56:5
 +   |
 +LL |     let mut iter = "a.b.c".rsplitn(2, '.');
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +LL |     let r = iter.next()?;
 +   |     --------------------- first usage here
 +LL |     let l = iter.next()?;
 +   |     --------------------- second usage here
 +   |
 +help: try `rsplit_once`
 +   |
 +LL |     let (l, r) = "a.b.c".rsplit_once('.')?;
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let r = iter.next()?;
 +LL +     
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let l = iter.next()?;
 +LL +     
 +   |
 +
 +error: manual implementation of `split_once`
-   --> $DIR/manual_split_once.rs:144:5
++  --> $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:143:5
 +   |
 +LL |     let mut iter = "a.b.c".splitn(2, '.');
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +LL |     let a = iter.next().unwrap();
 +   |     ----------------------------- first usage here
 +LL |     let b = iter.next().unwrap();
 +   |     ----------------------------- second usage here
 +   |
 +help: try `split_once`
 +   |
 +LL |     let (a, b) = "a.b.c".split_once('.').unwrap();
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let a = iter.next().unwrap();
 +LL +     
 +   |
 +help: remove the `iter` usages
 +   |
 +LL -     let b = iter.next().unwrap();
 +LL +     
 +   |
 +
 +error: aborting due to 19 previous errors
 +
index 0704ba2f933eb774dd91fedab659bec8ae1e1fcb,0000000000000000000000000000000000000000..3d56f2a0dedb2deefbc3676374964f9bd3d3bb1d
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,65 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.15"]
 +#![warn(clippy::manual_str_repeat)]
 +
 +use std::borrow::Cow;
 +use std::iter::repeat;
 +
 +fn main() {
 +    let _: String = "test".repeat(10);
 +    let _: String = "x".repeat(10);
 +    let _: String = "'".repeat(10);
 +    let _: String = "\"".repeat(10);
 +
 +    let x = "test";
 +    let count = 10;
 +    let _ = x.repeat(count + 2);
 +
 +    macro_rules! m {
 +        ($e:expr) => {{ $e }};
 +    }
 +    // FIXME: macro args are fine
 +    let _: String = repeat(m!("test")).take(m!(count)).collect();
 +
 +    let x = &x;
 +    let _: String = (*x).repeat(count);
 +
 +    macro_rules! repeat_m {
 +        ($e:expr) => {{ repeat($e) }};
 +    }
 +    // Don't lint, repeat is from a macro.
 +    let _: String = repeat_m!("test").take(count).collect();
 +
 +    let x: Box<str> = Box::from("test");
 +    let _: String = x.repeat(count);
 +
 +    #[derive(Clone)]
 +    struct S;
 +    impl FromIterator<Box<S>> for String {
 +        fn from_iter<T: IntoIterator<Item = Box<S>>>(_: T) -> Self {
 +            Self::new()
 +        }
 +    }
 +    // Don't lint, wrong box type
 +    let _: String = repeat(Box::new(S)).take(count).collect();
 +
 +    let _: String = Cow::Borrowed("test").repeat(count);
 +
 +    let x = "x".to_owned();
 +    let _: String = x.repeat(count);
 +
 +    let x = 'x';
 +    // Don't lint, not char literal
 +    let _: String = repeat(x).take(count).collect();
 +}
 +
++#[clippy::msrv = "1.15"]
 +fn _msrv_1_15() {
-     #![clippy::msrv = "1.16"]
 +    // `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() {
 +    let _: String = "test".repeat(10);
 +}
index f522be439aa0eaf0a69f5217c88c1ead4d67eee8,0000000000000000000000000000000000000000..e8240a949dbc248c4b2ad11dd2a67a42204a01d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,65 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.15"]
 +#![warn(clippy::manual_str_repeat)]
 +
 +use std::borrow::Cow;
 +use std::iter::repeat;
 +
 +fn main() {
 +    let _: String = std::iter::repeat("test").take(10).collect();
 +    let _: String = std::iter::repeat('x').take(10).collect();
 +    let _: String = std::iter::repeat('\'').take(10).collect();
 +    let _: String = std::iter::repeat('"').take(10).collect();
 +
 +    let x = "test";
 +    let count = 10;
 +    let _ = repeat(x).take(count + 2).collect::<String>();
 +
 +    macro_rules! m {
 +        ($e:expr) => {{ $e }};
 +    }
 +    // FIXME: macro args are fine
 +    let _: String = repeat(m!("test")).take(m!(count)).collect();
 +
 +    let x = &x;
 +    let _: String = repeat(*x).take(count).collect();
 +
 +    macro_rules! repeat_m {
 +        ($e:expr) => {{ repeat($e) }};
 +    }
 +    // Don't lint, repeat is from a macro.
 +    let _: String = repeat_m!("test").take(count).collect();
 +
 +    let x: Box<str> = Box::from("test");
 +    let _: String = repeat(x).take(count).collect();
 +
 +    #[derive(Clone)]
 +    struct S;
 +    impl FromIterator<Box<S>> for String {
 +        fn from_iter<T: IntoIterator<Item = Box<S>>>(_: T) -> Self {
 +            Self::new()
 +        }
 +    }
 +    // Don't lint, wrong box type
 +    let _: String = repeat(Box::new(S)).take(count).collect();
 +
 +    let _: String = repeat(Cow::Borrowed("test")).take(count).collect();
 +
 +    let x = "x".to_owned();
 +    let _: String = repeat(x).take(count).collect();
 +
 +    let x = 'x';
 +    // Don't lint, not char literal
 +    let _: String = repeat(x).take(count).collect();
 +}
 +
++#[clippy::msrv = "1.15"]
 +fn _msrv_1_15() {
-     #![clippy::msrv = "1.16"]
 +    // `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() {
 +    let _: String = std::iter::repeat("test").take(10).collect();
 +}
index c65116897164409e7b4ecb3862bfce925418b6c9,0000000000000000000000000000000000000000..bdfee7cab261e197ce9abce874836ba234448b23
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,64 @@@
-   --> $DIR/manual_str_repeat.rs:10:21
 +error: manual implementation of `str::repeat` using iterators
-   --> $DIR/manual_str_repeat.rs:11:21
++  --> $DIR/manual_str_repeat.rs:9:21
 +   |
 +LL |     let _: String = std::iter::repeat("test").take(10).collect();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
 +   |
 +   = note: `-D clippy::manual-str-repeat` implied by `-D warnings`
 +
 +error: manual implementation of `str::repeat` using iterators
-   --> $DIR/manual_str_repeat.rs:12: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:13: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:17:13
++  --> $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:26:21
++  --> $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:35: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:47: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:50: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:65: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:64:21
 +   |
 +LL |     let _: String = std::iter::repeat("test").take(10).collect();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"test".repeat(10)`
 +
 +error: aborting due to 10 previous errors
 +
index 85009d78558ba7d68abd82b3b76680fad600adf7,0000000000000000000000000000000000000000..b0b1c262aeed846f9bb6e105014cf575d4eaa5eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,82 @@@
- #![feature(custom_inner_attributes)]
 +#![warn(clippy::manual_strip)]
 +
 +fn main() {
 +    let s = "abc";
 +
 +    if s.starts_with("ab") {
 +        str::to_string(&s["ab".len()..]);
 +        s["ab".len()..].to_string();
 +
 +        str::to_string(&s[2..]);
 +        s[2..].to_string();
 +    }
 +
 +    if s.ends_with("bc") {
 +        str::to_string(&s[..s.len() - "bc".len()]);
 +        s[..s.len() - "bc".len()].to_string();
 +
 +        str::to_string(&s[..s.len() - 2]);
 +        s[..s.len() - 2].to_string();
 +    }
 +
 +    // Character patterns
 +    if s.starts_with('a') {
 +        str::to_string(&s[1..]);
 +        s[1..].to_string();
 +    }
 +
 +    // Variable prefix
 +    let prefix = "ab";
 +    if s.starts_with(prefix) {
 +        str::to_string(&s[prefix.len()..]);
 +    }
 +
 +    // Constant prefix
 +    const PREFIX: &str = "ab";
 +    if s.starts_with(PREFIX) {
 +        str::to_string(&s[PREFIX.len()..]);
 +        str::to_string(&s[2..]);
 +    }
 +
 +    // Constant target
 +    const TARGET: &str = "abc";
 +    if TARGET.starts_with(prefix) {
 +        str::to_string(&TARGET[prefix.len()..]);
 +    }
 +
 +    // String target - not mutated.
 +    let s1: String = "abc".into();
 +    if s1.starts_with("ab") {
 +        s1[2..].to_uppercase();
 +    }
 +
 +    // String target - mutated. (Don't lint.)
 +    let mut s2: String = "abc".into();
 +    if s2.starts_with("ab") {
 +        s2.push('d');
 +        s2[2..].to_uppercase();
 +    }
 +
 +    // Target not stripped. (Don't lint.)
 +    let s3 = String::from("abcd");
 +    let s4 = String::from("efgh");
 +    if s3.starts_with("ab") {
 +        s4[2..].to_string();
 +    }
 +}
 +
++#[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();
 +    }
 +}
index ad2a362f3e76ddb2d3d404952da30db254f8288b,0000000000000000000000000000000000000000..f592e898fc928d7e7d8b8c05f263cd6e8af401b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,149 -1,0 +1,149 @@@
-   --> $DIR/manual_strip.rs:8:24
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:7:5
++  --> $DIR/manual_strip.rs:7:24
 +   |
 +LL |         str::to_string(&s["ab".len()..]);
 +   |                        ^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:16:24
++  --> $DIR/manual_strip.rs:6:5
 +   |
 +LL |     if s.starts_with("ab") {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^
 +   = note: `-D clippy::manual-strip` implied by `-D warnings`
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix("ab") {
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +LL | 
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: stripping a suffix manually
-   --> $DIR/manual_strip.rs:15:5
++  --> $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:25:24
++  --> $DIR/manual_strip.rs:14:5
 +   |
 +LL |     if s.ends_with("bc") {
 +   |     ^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_suffix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_suffix("bc") {
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +LL | 
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:24:5
++  --> $DIR/manual_strip.rs:24:24
 +   |
 +LL |         str::to_string(&s[1..]);
 +   |                        ^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:32:24
++  --> $DIR/manual_strip.rs:23:5
 +   |
 +LL |     if s.starts_with('a') {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix('a') {
 +LL ~         str::to_string(<stripped>);
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:31:5
++  --> $DIR/manual_strip.rs:31:24
 +   |
 +LL |         str::to_string(&s[prefix.len()..]);
 +   |                        ^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:38:24
++  --> $DIR/manual_strip.rs:30:5
 +   |
 +LL |     if s.starts_with(prefix) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix(prefix) {
 +LL ~         str::to_string(<stripped>);
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:37:5
++  --> $DIR/manual_strip.rs:37:24
 +   |
 +LL |         str::to_string(&s[PREFIX.len()..]);
 +   |                        ^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:45:24
++  --> $DIR/manual_strip.rs:36:5
 +   |
 +LL |     if s.starts_with(PREFIX) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix(PREFIX) {
 +LL ~         str::to_string(<stripped>);
 +LL ~         str::to_string(<stripped>);
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:44:5
++  --> $DIR/manual_strip.rs:44:24
 +   |
 +LL |         str::to_string(&TARGET[prefix.len()..]);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:51:9
++  --> $DIR/manual_strip.rs:43:5
 +   |
 +LL |     if TARGET.starts_with(prefix) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = TARGET.strip_prefix(prefix) {
 +LL ~         str::to_string(<stripped>);
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:50:5
++  --> $DIR/manual_strip.rs:50:9
 +   |
 +LL |         s1[2..].to_uppercase();
 +   |         ^^^^^^^
 +   |
 +note: the prefix was tested here
-   --> $DIR/manual_strip.rs:83:9
++  --> $DIR/manual_strip.rs:49:5
 +   |
 +LL |     if s1.starts_with("ab") {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s1.strip_prefix("ab") {
 +LL ~         <stripped>.to_uppercase();
 +   |
 +
 +error: stripping a prefix manually
-   --> $DIR/manual_strip.rs:82:5
++  --> $DIR/manual_strip.rs:80:9
 +   |
 +LL |         s[1..].to_string();
 +   |         ^^^^^^
 +   |
 +note: the prefix was tested here
++  --> $DIR/manual_strip.rs:79:5
 +   |
 +LL |     if s.starts_with('a') {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +help: try using the `strip_prefix` method
 +   |
 +LL ~     if let Some(<stripped>) = s.strip_prefix('a') {
 +LL ~         <stripped>.to_string();
 +   |
 +
 +error: aborting due to 8 previous errors
 +
index 396b22a9abb390f520d911a773d688b8bb2f3992,0000000000000000000000000000000000000000..32631024ca5dca840d75fecb7487f7aceca29642
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,96 @@@
- #![feature(custom_inner_attributes)]
 +// aux-build:option_helpers.rs
 +
-     #![clippy::msrv = "1.40"]
 +#![warn(clippy::map_unwrap_or)]
 +#![allow(clippy::uninlined_format_args, clippy::unnecessary_lazy_evaluations)]
 +
 +#[macro_use]
 +extern crate option_helpers;
 +
 +use std::collections::HashMap;
 +
 +#[rustfmt::skip]
 +fn option_methods() {
 +    let opt = Some(1);
 +
 +    // Check for `option.map(_).unwrap_or(_)` use.
 +    // Single line case.
 +    let _ = opt.map(|x| x + 1)
 +        // Should lint even though this call is on a separate line.
 +        .unwrap_or(0);
 +    // Multi-line cases.
 +    let _ = opt.map(|x| {
 +        x + 1
 +    }
 +    ).unwrap_or(0);
 +    let _ = opt.map(|x| x + 1)
 +        .unwrap_or({
 +            0
 +        });
 +    // Single line `map(f).unwrap_or(None)` case.
 +    let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
 +    // Multi-line `map(f).unwrap_or(None)` cases.
 +    let _ = opt.map(|x| {
 +        Some(x + 1)
 +    }
 +    ).unwrap_or(None);
 +    let _ = opt
 +        .map(|x| Some(x + 1))
 +        .unwrap_or(None);
 +    // macro case
 +    let _ = opt_map!(opt, |x| x + 1).unwrap_or(0); // should not lint
 +
 +    // Should not lint if not copyable
 +    let id: String = "identifier".to_string();
 +    let _ = Some("prefix").map(|p| format!("{}.{}", p, id)).unwrap_or(id);
 +    // ...but DO lint if the `unwrap_or` argument is not used in the `map`
 +    let id: String = "identifier".to_string();
 +    let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
 +
 +    // Check for `option.map(_).unwrap_or_else(_)` use.
 +    // Multi-line cases.
 +    let _ = opt.map(|x| {
 +        x + 1
 +    }
 +    ).unwrap_or_else(|| 0);
 +    let _ = opt.map(|x| x + 1)
 +        .unwrap_or_else(||
 +            0
 +        );
 +}
 +
 +#[rustfmt::skip]
 +fn result_methods() {
 +    let res: Result<i32, ()> = Ok(1);
 +
 +    // Check for `result.map(_).unwrap_or_else(_)` use.
 +    // multi line cases
 +    let _ = res.map(|x| {
 +        x + 1
 +    }
 +    ).unwrap_or_else(|_e| 0);
 +    let _ = res.map(|x| x + 1)
 +        .unwrap_or_else(|_e| {
 +            0
 +        });
 +    // macro case
 +    let _ = opt_map!(res, |x| x + 1).unwrap_or_else(|_e| 0); // should not lint
 +}
 +
 +fn main() {
 +    option_methods();
 +    result_methods();
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
-     #![clippy::msrv = "1.41"]
 +    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() {
 +    let res: Result<i32, ()> = Ok(1);
 +
 +    let _ = res.map(|x| x + 1).unwrap_or_else(|_e| 0);
 +}
index d17d24a403ea5d578c01d51f725559553f2d75ee,0000000000000000000000000000000000000000..41781b050fa20a5df494c0625c2c73608d8d42d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,156 -1,0 +1,156 @@@
-   --> $DIR/map_unwrap_or.rs:18:13
 +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:17:13
 +   |
 +LL |       let _ = opt.map(|x| x + 1)
 +   |  _____________^
 +LL | |         // Should lint even though this call is on a separate line.
 +LL | |         .unwrap_or(0);
 +   | |_____________________^
 +   |
 +   = note: `-D clippy::map-unwrap-or` implied by `-D warnings`
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL -     let _ = opt.map(|x| x + 1)
 +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:26:13
++  --> $DIR/map_unwrap_or.rs:21:13
 +   |
 +LL |       let _ = opt.map(|x| {
 +   |  _____________^
 +LL | |         x + 1
 +LL | |     }
 +LL | |     ).unwrap_or(0);
 +   | |__________________^
 +   |
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL ~     let _ = opt.map_or(0, |x| {
 +LL |         x + 1
 +LL |     }
 +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:31:13
++  --> $DIR/map_unwrap_or.rs:25:13
 +   |
 +LL |       let _ = opt.map(|x| x + 1)
 +   |  _____________^
 +LL | |         .unwrap_or({
 +LL | |             0
 +LL | |         });
 +   | |__________^
 +   |
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL ~     let _ = opt.map_or({
 +LL +             0
 +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:33:13
++  --> $DIR/map_unwrap_or.rs:30:13
 +   |
 +LL |     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use `and_then(<f>)` instead
 +   |
 +LL -     let _ = opt.map(|x| Some(x + 1)).unwrap_or(None);
 +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:37:13
++  --> $DIR/map_unwrap_or.rs:32:13
 +   |
 +LL |       let _ = opt.map(|x| {
 +   |  _____________^
 +LL | |         Some(x + 1)
 +LL | |     }
 +LL | |     ).unwrap_or(None);
 +   | |_____________________^
 +   |
 +help: use `and_then(<f>)` instead
 +   |
 +LL ~     let _ = opt.and_then(|x| {
 +LL |         Some(x + 1)
 +LL |     }
 +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:48:13
++  --> $DIR/map_unwrap_or.rs:36:13
 +   |
 +LL |       let _ = opt
 +   |  _____________^
 +LL | |         .map(|x| Some(x + 1))
 +LL | |         .unwrap_or(None);
 +   | |________________________^
 +   |
 +help: use `and_then(<f>)` instead
 +   |
 +LL -         .map(|x| Some(x + 1))
 +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:52:13
++  --> $DIR/map_unwrap_or.rs:47:13
 +   |
 +LL |     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use `map_or(<a>, <f>)` instead
 +   |
 +LL -     let _ = Some("prefix").map(|p| format!("{}.", p)).unwrap_or(id);
 +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:56:13
++  --> $DIR/map_unwrap_or.rs:51:13
 +   |
 +LL |       let _ = opt.map(|x| {
 +   |  _____________^
 +LL | |         x + 1
 +LL | |     }
 +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:68:13
++  --> $DIR/map_unwrap_or.rs:55:13
 +   |
 +LL |       let _ = opt.map(|x| x + 1)
 +   |  _____________^
 +LL | |         .unwrap_or_else(||
 +LL | |             0
 +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:72:13
++  --> $DIR/map_unwrap_or.rs:67:13
 +   |
 +LL |       let _ = res.map(|x| {
 +   |  _____________^
 +LL | |         x + 1
 +LL | |     }
 +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:98:13
++  --> $DIR/map_unwrap_or.rs:71:13
 +   |
 +LL |       let _ = res.map(|x| x + 1)
 +   |  _____________^
 +LL | |         .unwrap_or_else(|_e| {
 +LL | |             0
 +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: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)`
 +
 +error: aborting due to 12 previous errors
 +
index 968f462f8a029d19f2d5dfae483dcc976d403496,0000000000000000000000000000000000000000..55cd15bd5c3858adb81df923ac8830385a5b5c48
mode 100644,000000..100644
--- /dev/null
@@@ -1,216 -1,0 +1,213 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.41"]
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(
 +    unreachable_patterns,
 +    dead_code,
 +    clippy::equatable_if_let,
 +    clippy::needless_borrowed_reference
 +)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = matches!(x, Some(0));
 +
 +    // Lint
 +    let _w = matches!(x, Some(_));
 +
 +    // Turn into is_none
 +    let _z = x.is_none();
 +
 +    // Lint
 +    let _zz = !matches!(x, Some(r) if r == 0);
 +
 +    // Lint
 +    let _zzz = matches!(x, Some(5));
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        // skip rustfmt to prevent removing block for first pattern
 +        #[rustfmt::skip]
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        let _ans = !matches!(x, E::B(_) | E::C);
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = matches!(z, Some(3));
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = matches!(&z, Some(3));
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = matches!(&z, AnEnum::X);
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
 +
 +    let x = ' ';
 +    // ignore if match block contains comment
 +    let _line_comments = match x {
 +        // numbers are bad!
 +        '1' | '2' | '3' => true,
 +        // spaces are very important to be true.
 +        ' ' => true,
 +        // as are dots
 +        '.' => true,
 +        _ => false,
 +    };
 +
 +    let _block_comments = match x {
 +        /* numbers are bad!
 +         */
 +        '1' | '2' | '3' => true,
 +        /* spaces are very important to be true.
 +         */
 +        ' ' => true,
 +        /* as are dots
 +         */
 +        '.' => true,
 +        _ => false,
 +    };
 +}
 +
++#[clippy::msrv = "1.41"]
 +fn msrv_1_41() {
-     #![clippy::msrv = "1.42"]
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
 +
++#[clippy::msrv = "1.42"]
 +fn msrv_1_42() {
 +    let _y = matches!(Some(5), Some(0));
 +}
index c6b479e27c5aca477a73e782291f7663412669bb,0000000000000000000000000000000000000000..5d645e108e511859e73013037be76ffcd786e59b
mode 100644,000000..100644
--- /dev/null
@@@ -1,260 -1,0 +1,257 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.41"]
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(
 +    unreachable_patterns,
 +    dead_code,
 +    clippy::equatable_if_let,
 +    clippy::needless_borrowed_reference
 +)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = match x {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +
 +    // Lint
 +    let _w = match x {
 +        Some(_) => true,
 +        _ => false,
 +    };
 +
 +    // Turn into is_none
 +    let _z = match x {
 +        Some(_) => false,
 +        None => true,
 +    };
 +
 +    // Lint
 +    let _zz = match x {
 +        Some(r) if r == 0 => false,
 +        _ => true,
 +    };
 +
 +    // Lint
 +    let _zzz = if let Some(5) = x { true } else { false };
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        // skip rustfmt to prevent removing block for first pattern
 +        #[rustfmt::skip]
 +        let _ans = match x {
 +            E::A(_) => {
 +                true
 +            }
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = match &z {
 +                AnEnum::X => true,
 +                _ => false,
 +            };
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
 +
 +    let x = ' ';
 +    // ignore if match block contains comment
 +    let _line_comments = match x {
 +        // numbers are bad!
 +        '1' | '2' | '3' => true,
 +        // spaces are very important to be true.
 +        ' ' => true,
 +        // as are dots
 +        '.' => true,
 +        _ => false,
 +    };
 +
 +    let _block_comments = match x {
 +        /* numbers are bad!
 +         */
 +        '1' | '2' | '3' => true,
 +        /* spaces are very important to be true.
 +         */
 +        ' ' => true,
 +        /* as are dots
 +         */
 +        '.' => true,
 +        _ => false,
 +    };
 +}
 +
++#[clippy::msrv = "1.41"]
 +fn msrv_1_41() {
-     #![clippy::msrv = "1.42"]
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
 +
++#[clippy::msrv = "1.42"]
 +fn msrv_1_42() {
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
index a4df8008ac239288436dc9919382b7f2304e58d6,0000000000000000000000000000000000000000..46f67ef4900f8d6d001ccd47e07748801013bfa8
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,147 @@@
-   --> $DIR/match_expr_like_matches_macro.rs:16:14
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:22:14
++  --> $DIR/match_expr_like_matches_macro.rs:15:14
 +   |
 +LL |       let _y = match x {
 +   |  ______________^
 +LL | |         Some(0) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(0))`
 +   |
 +   = 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:28:14
++  --> $DIR/match_expr_like_matches_macro.rs:21:14
 +   |
 +LL |       let _w = match x {
 +   |  ______________^
 +LL | |         Some(_) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(_))`
 +
 +error: redundant pattern matching, consider using `is_none()`
-   --> $DIR/match_expr_like_matches_macro.rs:34:15
++  --> $DIR/match_expr_like_matches_macro.rs:27:14
 +   |
 +LL |       let _z = match x {
 +   |  ______________^
 +LL | |         Some(_) => false,
 +LL | |         None => true,
 +LL | |     };
 +   | |_____^ help: try this: `x.is_none()`
 +   |
 +   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:40:16
++  --> $DIR/match_expr_like_matches_macro.rs:33:15
 +   |
 +LL |       let _zz = match x {
 +   |  _______________^
 +LL | |         Some(r) if r == 0 => false,
 +LL | |         _ => true,
 +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:64:20
++  --> $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:74:20
++  --> $DIR/match_expr_like_matches_macro.rs:63:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::A(_) => true,
 +LL | |             E::B(_) => true,
 +LL | |             _ => false,
 +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:73:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::A(_) => {
 +LL | |                 true
 +LL | |             }
 +LL | |             E::B(_) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:144:18
++  --> $DIR/match_expr_like_matches_macro.rs:83:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::B(_) => false,
 +LL | |             E::C => false,
 +LL | |             _ => true,
 +LL | |         };
 +   | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:153:18
++  --> $DIR/match_expr_like_matches_macro.rs:143:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +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:152:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&z, Some(3))`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:184:20
++  --> $DIR/match_expr_like_matches_macro.rs:169:21
 +   |
 +LL |               let _ = match &z {
 +   |  _____________________^
 +LL | |                 AnEnum::X => true,
 +LL | |                 _ => false,
 +LL | |             };
 +   | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
 +
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:196:20
++  --> $DIR/match_expr_like_matches_macro.rs:183:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +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:195:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
 +error: match expression looks like `matches!` macro
++  --> $DIR/match_expr_like_matches_macro.rs:253:14
 +   |
 +LL |       let _y = match Some(5) {
 +   |  ______________^
 +LL | |         Some(0) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(Some(5), Some(0))`
 +
 +error: aborting due to 14 previous errors
 +
index ae237395b95fc06d5db17bbc417a9d3c345c1974,0000000000000000000000000000000000000000..874d558433034d217e246dd8fcf38e17f842b4bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,92 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.39"]
 +#![allow(unused)]
 +#![warn(
 +    clippy::all,
 +    clippy::style,
 +    clippy::mem_replace_option_with_none,
 +    clippy::mem_replace_with_default
 +)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +use std::mem;
 +
 +fn replace_option_with_none() {
 +    let mut an_option = Some(1);
 +    let _ = an_option.take();
 +    let an_option = &mut Some(1);
 +    let _ = an_option.take();
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::take(&mut s);
 +
 +    let s = &mut String::from("foo");
 +    let _ = std::mem::take(s);
 +    let _ = std::mem::take(s);
 +
 +    let mut v = vec![123];
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +    let _ = std::mem::take(&mut v);
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    let _ = std::mem::take(&mut hash_map);
 +
 +    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
 +    let _ = std::mem::take(&mut btree_map);
 +
 +    let mut vd: VecDeque<i32> = VecDeque::new();
 +    let _ = std::mem::take(&mut vd);
 +
 +    let mut hash_set: HashSet<&str> = HashSet::new();
 +    let _ = std::mem::take(&mut hash_set);
 +
 +    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
 +    let _ = std::mem::take(&mut btree_set);
 +
 +    let mut list: LinkedList<i32> = LinkedList::new();
 +    let _ = std::mem::take(&mut list);
 +
 +    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
 +    let _ = std::mem::take(&mut binary_heap);
 +
 +    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
 +    let _ = std::mem::take(&mut tuple);
 +
 +    let mut refstr = "hello";
 +    let _ = std::mem::take(&mut refstr);
 +
 +    let mut slice: &[i32] = &[1, 2, 3];
 +    let _ = std::mem::take(&mut slice);
 +}
 +
 +// lint is disabled for primitives because in this case `take`
 +// has no clear benefit over `replace` and sometimes is harder to read
 +fn dont_lint_primitive() {
 +    let mut pbool = true;
 +    let _ = std::mem::replace(&mut pbool, false);
 +
 +    let mut pint = 5;
 +    let _ = std::mem::replace(&mut pint, 0);
 +}
 +
 +fn main() {
 +    replace_option_with_none();
 +    replace_with_default();
 +    dont_lint_primitive();
 +}
 +
++#[clippy::msrv = "1.39"]
 +fn msrv_1_39() {
-     #![clippy::msrv = "1.40"]
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::take(&mut s);
 +}
index 3202e99e0be9e99cd0957041eced7010b551fa6e,0000000000000000000000000000000000000000..f4f3bff514463e4a04a661ab372d36579e59ec0b
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,92 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.39"]
 +#![allow(unused)]
 +#![warn(
 +    clippy::all,
 +    clippy::style,
 +    clippy::mem_replace_option_with_none,
 +    clippy::mem_replace_with_default
 +)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +use std::mem;
 +
 +fn replace_option_with_none() {
 +    let mut an_option = Some(1);
 +    let _ = mem::replace(&mut an_option, None);
 +    let an_option = &mut Some(1);
 +    let _ = mem::replace(an_option, None);
 +}
 +
 +fn replace_with_default() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +
 +    let s = &mut String::from("foo");
 +    let _ = std::mem::replace(s, String::default());
 +    let _ = std::mem::replace(s, Default::default());
 +
 +    let mut v = vec![123];
 +    let _ = std::mem::replace(&mut v, Vec::default());
 +    let _ = std::mem::replace(&mut v, Default::default());
 +    let _ = std::mem::replace(&mut v, Vec::new());
 +    let _ = std::mem::replace(&mut v, vec![]);
 +
 +    let mut hash_map: HashMap<i32, i32> = HashMap::new();
 +    let _ = std::mem::replace(&mut hash_map, HashMap::new());
 +
 +    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
 +    let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
 +
 +    let mut vd: VecDeque<i32> = VecDeque::new();
 +    let _ = std::mem::replace(&mut vd, VecDeque::new());
 +
 +    let mut hash_set: HashSet<&str> = HashSet::new();
 +    let _ = std::mem::replace(&mut hash_set, HashSet::new());
 +
 +    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
 +    let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
 +
 +    let mut list: LinkedList<i32> = LinkedList::new();
 +    let _ = std::mem::replace(&mut list, LinkedList::new());
 +
 +    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
 +    let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
 +
 +    let mut tuple = (vec![1, 2], BinaryHeap::<i32>::new());
 +    let _ = std::mem::replace(&mut tuple, (vec![], BinaryHeap::new()));
 +
 +    let mut refstr = "hello";
 +    let _ = std::mem::replace(&mut refstr, "");
 +
 +    let mut slice: &[i32] = &[1, 2, 3];
 +    let _ = std::mem::replace(&mut slice, &[]);
 +}
 +
 +// lint is disabled for primitives because in this case `take`
 +// has no clear benefit over `replace` and sometimes is harder to read
 +fn dont_lint_primitive() {
 +    let mut pbool = true;
 +    let _ = std::mem::replace(&mut pbool, false);
 +
 +    let mut pint = 5;
 +    let _ = std::mem::replace(&mut pint, 0);
 +}
 +
 +fn main() {
 +    replace_option_with_none();
 +    replace_with_default();
 +    dont_lint_primitive();
 +}
 +
++#[clippy::msrv = "1.39"]
 +fn msrv_1_39() {
-     #![clippy::msrv = "1.40"]
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
 +    let mut s = String::from("foo");
 +    let _ = std::mem::replace(&mut s, String::default());
 +}
index dd8a50dab9002f1649d970a1af348d99993bbb0f,0000000000000000000000000000000000000000..caa127f76eeffa8a1d92b0b75437807eb2d8bac0
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,126 @@@
-   --> $DIR/mem_replace.rs:17:13
 +error: replacing an `Option` with `None`
-   --> $DIR/mem_replace.rs:19:13
++  --> $DIR/mem_replace.rs:16:13
 +   |
 +LL |     let _ = mem::replace(&mut an_option, None);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 +   |
 +   = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 +
 +error: replacing an `Option` with `None`
-   --> $DIR/mem_replace.rs:24: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:27:13
++  --> $DIR/mem_replace.rs:23:13
 +   |
 +LL |     let _ = std::mem::replace(&mut s, String::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
 +   |
 +   = 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:28: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:31: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:32: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:33: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:34: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:37: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:40: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:43: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:46: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:49: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:52: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:55: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:58: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:61: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:64: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:94: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:91:13
 +   |
 +LL |     let _ = std::mem::replace(&mut s, String::default());
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
 +
 +error: aborting due to 20 previous errors
 +
index cd148063bf065c304ff683fe19998885a56a3a70,0000000000000000000000000000000000000000..955e7eb727634134ddd9df2c9ef39c8b6692e9a4
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,62 @@@
-     #![clippy::msrv = "1.42.0"]
 +#![allow(clippy::redundant_clone)]
 +#![feature(custom_inner_attributes)]
 +
 +fn main() {}
 +
++#[clippy::msrv = "1.42.0"]
 +fn just_under_msrv() {
-     #![clippy::msrv = "1.43.0"]
 +    let log2_10 = 3.321928094887362;
 +}
 +
++#[clippy::msrv = "1.43.0"]
 +fn meets_msrv() {
-     #![clippy::msrv = "1.44.0"]
 +    let log2_10 = 3.321928094887362;
 +}
 +
++#[clippy::msrv = "1.44.0"]
 +fn just_above_msrv() {
-     #![clippy::msrv = "1.42"]
 +    let log2_10 = 3.321928094887362;
 +}
 +
++#[clippy::msrv = "1.42"]
 +fn no_patch_under() {
 +    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;
++        }
++    }
++}
index 68aa58748190be3552ef5ad7455f95b32dc7fce8,0000000000000000000000000000000000000000..7e2135584efde60aa144b334f15ed722860406b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,51 @@@
- error: aborting due to 3 previous errors
 +error: approximate value of `f{32, 64}::consts::LOG2_10` found
 +  --> $DIR/min_rust_version_attr.rs:13:19
 +   |
 +LL |     let log2_10 = 3.321928094887362;
 +   |                   ^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using the constant directly
 +   = note: `#[deny(clippy::approx_constant)]` on by default
 +
 +error: approximate value of `f{32, 64}::consts::LOG2_10` found
 +  --> $DIR/min_rust_version_attr.rs:18: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:28: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: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
 +
index 93370a0fa9c912ce45cf972743c38714c5bcaff7,0000000000000000000000000000000000000000..675b780315251ce20f64f17b62a4ff9c1075172c
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,50 @@@
- error: `msrv` cannot be an outer attribute
 +error: `invalid.version` is not a valid Rust version
 +  --> $DIR/min_rust_version_invalid_attr.rs:2:1
 +   |
 +LL | #![clippy::msrv = "invalid.version"]
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: `invalid.version` is not a valid Rust version
 +  --> $DIR/min_rust_version_invalid_attr.rs:6:1
 +   |
 +LL | #[clippy::msrv = "invalid.version"]
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `msrv` is defined multiple times
 +  --> $DIR/min_rust_version_invalid_attr.rs:11:5
 +   |
 +LL |     #![clippy::msrv = "=1.35.0"]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: first definition found here
 +  --> $DIR/min_rust_version_invalid_attr.rs:10:5
 +   |
 +LL |     #![clippy::msrv = "1.40"]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `msrv` is defined multiple times
 +  --> $DIR/min_rust_version_invalid_attr.rs:12:5
 +   |
 +LL |     #![clippy::msrv = "1.10.1"]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: first definition found here
 +  --> $DIR/min_rust_version_invalid_attr.rs:10:5
 +   |
 +LL |     #![clippy::msrv = "1.40"]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `msrv` is defined multiple times
 +  --> $DIR/min_rust_version_invalid_attr.rs:16:9
 +   |
 +LL |         #![clippy::msrv = "1.0.0"]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: first definition found here
 +  --> $DIR/min_rust_version_invalid_attr.rs:15:9
 +   |
 +LL |         #![clippy::msrv = "1"]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03e7dac7df94c4ceaaf7d29e69deb8dc80050ed3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1e38a83d019a6d33e7aa82c973beedd657e9dc9b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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
++
index b950248ef942024d0e003fc5be16c8ba0c249101,0000000000000000000000000000000000000000..75cace1816754e75eb17dca1d10e1063f528ac73
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,128 @@@
- #![feature(custom_inner_attributes)]
 +//! False-positive tests to ensure we don't suggest `const` for things where it would cause a
 +//! compilation error.
 +//! The .stderr output of this test should be empty. Otherwise it's a bug somewhere.
 +
 +// aux-build:helper.rs
 +// aux-build:../../auxiliary/proc_macro_with_span.rs
 +
 +#![warn(clippy::missing_const_for_fn)]
 +#![feature(start)]
-     #![clippy::msrv = "1.46.0"]
 +
 +extern crate helper;
 +extern crate proc_macro_with_span;
 +
 +use proc_macro_with_span::with_span;
 +
 +struct Game;
 +
 +// This should not be linted because it's already const
 +const fn already_const() -> i32 {
 +    32
 +}
 +
 +impl Game {
 +    // This should not be linted because it's already const
 +    pub const fn already_const() -> i32 {
 +        32
 +    }
 +}
 +
 +// Allowing on this function, because it would lint, which we don't want in this case.
 +#[allow(clippy::missing_const_for_fn)]
 +fn random() -> u32 {
 +    42
 +}
 +
 +// We should not suggest to make this function `const` because `random()` is non-const
 +fn random_caller() -> u32 {
 +    random()
 +}
 +
 +static Y: u32 = 0;
 +
 +// We should not suggest to make this function `const` because const functions are not allowed to
 +// refer to a static variable
 +fn get_y() -> u32 {
 +    Y
 +    //~^ ERROR E0013
 +}
 +
 +// Don't lint entrypoint functions
 +#[start]
 +fn init(num: isize, something: *const *const u8) -> isize {
 +    1
 +}
 +
 +trait Foo {
 +    // This should not be suggested to be made const
 +    // (rustc doesn't allow const trait methods)
 +    fn f() -> u32;
 +
 +    // This should not be suggested to be made const either
 +    fn g() -> u32 {
 +        33
 +    }
 +}
 +
 +// Don't lint in external macros (derive)
 +#[derive(PartialEq, Eq)]
 +struct Point(isize, isize);
 +
 +impl std::ops::Add for Point {
 +    type Output = Self;
 +
 +    // Don't lint in trait impls of derived methods
 +    fn add(self, other: Self) -> Self {
 +        Point(self.0 + other.0, self.1 + other.1)
 +    }
 +}
 +
 +mod with_drop {
 +    pub struct A;
 +    pub struct B;
 +    impl Drop for A {
 +        fn drop(&mut self) {}
 +    }
 +
 +    impl A {
 +        // This can not be const because the type implements `Drop`.
 +        pub fn b(self) -> B {
 +            B
 +        }
 +    }
 +
 +    impl B {
 +        // This can not be const because `a` implements `Drop`.
 +        pub fn a(self, a: A) -> B {
 +            B
 +        }
 +    }
 +}
 +
 +fn const_generic_params<T, const N: usize>(t: &[T; N]) -> &[T; N] {
 +    t
 +}
 +
 +fn const_generic_return<T, const N: usize>(t: &[T]) -> &[T; N] {
 +    let p = t.as_ptr() as *const [T; N];
 +
 +    unsafe { &*p }
 +}
 +
 +// Do not lint this because it calls a function whose constness is unstable.
 +fn unstably_const_fn() {
 +    helper::unstably_const_fn()
 +}
 +
++#[clippy::msrv = "1.46.0"]
 +mod const_fn_stabilized_after_msrv {
 +    // 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();
 +    }
 +}
 +
 +with_span! {
 +    span
 +    fn dont_check_in_proc_macro() {}
 +}
index b85e88784918d50401858472574b07902aadf6e0,0000000000000000000000000000000000000000..0246c8622ed3adf6b7f6c7086843b83b4049b960
mode 100644,000000..100644
--- /dev/null
@@@ -1,93 -1,0 +1,89 @@@
- #![feature(custom_inner_attributes)]
 +#![warn(clippy::missing_const_for_fn)]
 +#![allow(incomplete_features, clippy::let_and_return)]
-     #![clippy::msrv = "1.47.0"]
 +
 +use std::mem::transmute;
 +
 +struct Game {
 +    guess: i32,
 +}
 +
 +impl Game {
 +    // Could be const
 +    pub fn new() -> Self {
 +        Self { guess: 42 }
 +    }
 +
 +    fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 +        b
 +    }
 +}
 +
 +// Could be const
 +fn one() -> i32 {
 +    1
 +}
 +
 +// Could also be const
 +fn two() -> i32 {
 +    let abc = 2;
 +    abc
 +}
 +
 +// Could be const (since Rust 1.39)
 +fn string() -> String {
 +    String::new()
 +}
 +
 +// Could be const
 +unsafe fn four() -> i32 {
 +    4
 +}
 +
 +// Could also be const
 +fn generic<T>(t: T) -> T {
 +    t
 +}
 +
 +fn sub(x: u32) -> usize {
 +    unsafe { transmute(&x) }
 +}
 +
 +fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 +    t[0]
 +}
 +
 +mod with_drop {
 +    pub struct A;
 +    pub struct B;
 +    impl Drop for A {
 +        fn drop(&mut self) {}
 +    }
 +
 +    impl B {
 +        // This can be const, because `a` is passed by reference
 +        pub fn b(self, a: &A) -> B {
 +            B
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.47.0"]
 +mod const_fn_stabilized_before_msrv {
-     #![clippy::msrv = "1.45"]
 +    // 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.46"]
 +    45
 +}
 +
++#[clippy::msrv = "1.46"]
 +fn msrv_1_46() -> i32 {
 +    46
 +}
 +
 +// Should not be const
 +fn main() {}
index f8e221c82f1a5c4ba575433c3cdeb301577c60d3,0000000000000000000000000000000000000000..955e1ed26340899c898c4eedcd508b384ac2c2f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,93 @@@
-   --> $DIR/could_be_const.rs:13:5
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:17:5
++  --> $DIR/could_be_const.rs:12:5
 +   |
 +LL | /     pub fn new() -> Self {
 +LL | |         Self { guess: 42 }
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:23:1
++  --> $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
 +LL | |     }
 +   | |_____^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:28:1
++  --> $DIR/could_be_const.rs:22:1
 +   |
 +LL | / fn one() -> i32 {
 +LL | |     1
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:34:1
++  --> $DIR/could_be_const.rs:27:1
 +   |
 +LL | / fn two() -> i32 {
 +LL | |     let abc = 2;
 +LL | |     abc
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:39:1
++  --> $DIR/could_be_const.rs:33:1
 +   |
 +LL | / fn string() -> String {
 +LL | |     String::new()
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:44:1
++  --> $DIR/could_be_const.rs:38:1
 +   |
 +LL | / unsafe fn four() -> i32 {
 +LL | |     4
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:52:1
++  --> $DIR/could_be_const.rs:43:1
 +   |
 +LL | / fn generic<T>(t: T) -> T {
 +LL | |     t
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:65:9
++  --> $DIR/could_be_const.rs:51:1
 +   |
 +LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 +LL | |     t[0]
 +LL | | }
 +   | |_^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:75:5
++  --> $DIR/could_be_const.rs:64:9
 +   |
 +LL | /         pub fn b(self, a: &A) -> B {
 +LL | |             B
 +LL | |         }
 +   | |_________^
 +
 +error: this could be a `const fn`
-   --> $DIR/could_be_const.rs:86:1
++  --> $DIR/could_be_const.rs:73:5
 +   |
 +LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
 +LL | |         byte.is_ascii_digit();
 +LL | |     }
 +   | |_____^
 +
 +error: this could be a `const fn`
- LL | |     #![clippy::msrv = "1.46"]
- LL | |
++  --> $DIR/could_be_const.rs:84:1
 +   |
 +LL | / fn msrv_1_46() -> i32 {
 +LL | |     46
 +LL | | }
 +   | |_^
 +
 +error: aborting due to 11 previous errors
 +
index 85b6b639d5549c9b6636597617a8c486370a7863,0000000000000000000000000000000000000000..4cb7f6b687f1195c3bb429b941c8ca256f6534d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,512 -1,0 +1,493 @@@
- #![feature(custom_inner_attributes, lint_reasons)]
- #[warn(clippy::all, clippy::needless_borrow)]
- #[allow(unused_variables)]
- #[allow(
 +// run-rustfix
- #[allow(dead_code)]
++#![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;
 +    let _ = x(&a); // no warning
 +    let _ = x(&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &a
 +        },
 +        46 => &a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&a);
 +    let _ = x(&a);
 +    let _ = x(&mut b);
 +    let _ = x(ref_a);
 +    {
 +        let b = &mut b;
 +        x(b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(x);
 +    mut_ref(x);
 +    let y: &mut i32 = x;
 +    let y: &mut i32 = x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = x.0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 +
 +    struct S;
 +    impl From<S> for u32 {
 +        fn from(s: S) -> Self {
 +            (&s).into()
 +        }
 +    }
 +    impl From<&S> for u32 {
 +        fn from(s: &S) -> Self {
 +            0
 +        }
 +    }
 +
 +    let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
 +    let _ = std::path::Path::new(".").join(".");
 +    deref_target_is_x(X);
 +    multiple_constraints([[""]]);
 +    multiple_constraints_normalizes_to_same(X, X);
 +    let _ = Some("").unwrap_or("");
 +    let _ = std::fs::write("x", "".to_string());
 +
 +    only_sized(&""); // Don't lint. `Sized` is only bound
 +    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
 +    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
 +    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
 +    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
 +    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 +}
 +
 +#[allow(clippy::needless_borrowed_reference)]
 +fn x(y: &i32) -> i32 {
 +    *y
 +}
 +
 +fn mut_ref(y: &mut i32) {
 +    *y = 5;
 +}
 +
 +fn f<T: Copy>(y: &T) -> T {
 +    *y
 +}
 +
 +fn g(y: &[u8]) -> u8 {
 +    y[0]
 +}
 +
 +trait Trait {}
 +
 +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,
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: Fn() -> T,
 +    {
 +        fn calls_field(&self) -> T {
 +            (self.f)()
 +        }
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: FnMut() -> T,
 +    {
 +        fn calls_mut_field(&mut self) -> T {
 +            (self.f)()
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +struct X;
 +
 +impl std::ops::Deref for X {
 +    type Target = X;
 +    fn deref(&self) -> &Self::Target {
 +        self
 +    }
 +}
 +
 +fn deref_target_is_x<T>(_: T)
 +where
 +    T: std::ops::Deref<Target = X>,
 +{
 +}
 +
 +fn multiple_constraints<T, U, V, X, Y>(_: T)
 +where
 +    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +    X: IntoIterator<Item = Y>,
 +    Y: AsRef<std::ffi::OsStr>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
 +where
 +    T: std::ops::Deref<Target = U>,
 +    U: std::ops::Deref<Target = V>,
 +{
 +}
 +
 +fn only_sized<T>(_: T) {}
 +
 +fn ref_as_ref_path<T: 'static>(_: &'static T)
 +where
 +    &'static T: AsRef<std::path::Path>,
 +{
 +}
 +
 +trait RefsOnly {
 +    type Referent;
 +}
 +
 +impl<T> RefsOnly for &T {
 +    type Referent = T;
 +}
 +
 +fn refs_only<T, U>(_: T)
 +where
 +    T: RefsOnly<Referent = U>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
 +where
 +    T: IntoIterator<Item = U>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +{
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-     #![allow(dead_code)]
-     #![clippy::msrv = "1.52.0"]
 +mod copyable_iterator {
 +    #[derive(Clone, Copy)]
 +    struct Iter;
 +    impl Iterator for Iter {
 +        type Item = ();
 +        fn next(&mut self) -> Option<Self::Item> {
 +            None
 +        }
 +    }
 +    fn takes_iter(_: impl Iterator) {}
 +    fn dont_warn(mut x: Iter) {
 +        takes_iter(&mut x);
 +    }
 +    #[allow(unused_mut)]
 +    fn warn(mut x: &mut Iter) {
 +        takes_iter(x)
 +    }
 +}
 +
++#[clippy::msrv = "1.52.0"]
 +mod under_msrv {
-     #![allow(dead_code)]
-     #![clippy::msrv = "1.53.0"]
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
++#[clippy::msrv = "1.53.0"]
 +mod meets_msrv {
- #[allow(unused)]
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
- #[allow(dead_code)]
 +fn issue9383() {
 +    // Should not lint because unions need explicit deref when accessing field
 +    use std::mem::ManuallyDrop;
 +
 +    union Coral {
 +        crab: ManuallyDrop<Vec<i32>>,
 +    }
 +
 +    union Ocean {
 +        coral: ManuallyDrop<Coral>,
 +    }
 +
 +    let mut ocean = Ocean {
 +        coral: ManuallyDrop::new(Coral {
 +            crab: ManuallyDrop::new(vec![1, 2, 3]),
 +        }),
 +    };
 +
 +    unsafe {
 +        ManuallyDrop::drop(&mut (&mut ocean.coral).crab);
 +
 +        (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]);
 +        ManuallyDrop::drop(&mut (*ocean.coral).crab);
 +
 +        ManuallyDrop::drop(&mut ocean.coral);
 +    }
 +}
 +
- #[allow(dead_code)]
 +fn closure_test() {
 +    let env = "env".to_owned();
 +    let arg = "arg".to_owned();
 +    let f = |arg| {
 +        let loc = "loc".to_owned();
 +        let _ = std::fs::write("x", &env); // Don't lint. In environment
 +        let _ = std::fs::write("x", arg);
 +        let _ = std::fs::write("x", loc);
 +    };
 +    let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
 +    f(arg);
 +}
 +
- #[allow(dead_code)]
 +mod significant_drop {
 +    #[derive(Debug)]
 +    struct X;
 +
 +    #[derive(Debug)]
 +    struct Y;
 +
 +    impl Drop for Y {
 +        fn drop(&mut self) {}
 +    }
 +
 +    fn foo(x: X, y: Y) {
 +        debug(x);
 +        debug(&y); // Don't lint. Has significant drop
 +    }
 +
 +    fn debug(_: impl std::fmt::Debug) {}
 +}
 +
- #[allow(dead_code)]
 +mod used_exactly_once {
 +    fn foo(x: String) {
 +        use_x(x);
 +    }
 +    fn use_x(_: impl AsRef<str>) {}
 +}
 +
- #[allow(dead_code)]
 +mod used_more_than_once {
 +    fn foo(x: String) {
 +        use_x(&x);
 +        use_x_again(&x);
 +    }
 +    fn use_x(_: impl AsRef<str>) {}
 +    fn use_x_again(_: impl AsRef<str>) {}
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
- #[allow(dead_code)]
 +mod issue_9111 {
 +    struct A;
 +
 +    impl Extend<u8> for A {
 +        fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<'a> Extend<&'a u8> for A {
 +        fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
 +            unimplemented!()
 +        }
 +    }
 +
 +    fn main() {
 +        let mut a = A;
 +        a.extend(&[]); // vs a.extend([]);
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9710 {
 +    fn main() {
 +        let string = String::new();
 +        for _i in 0..10 {
 +            f(&string);
 +        }
 +    }
 +
 +    fn f<T: AsRef<str>>(_: T) {}
 +}
 +
- #[allow(dead_code)]
 +mod issue_9739 {
 +    fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
 +
 +    fn main() {
 +        foo(if std::env::var_os("HI").is_some() {
 +            &[0]
 +        } else {
 +            &[] as &[u32]
 +        });
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9739_method_variant {
 +    struct S;
 +
 +    impl S {
 +        fn foo<D: std::fmt::Display>(&self, _it: impl IntoIterator<Item = D>) {}
 +    }
 +
 +    fn main() {
 +        S.foo(if std::env::var_os("HI").is_some() {
 +            &[0]
 +        } else {
 +            &[] as &[u32]
 +        });
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9782 {
 +    fn foo<T: AsRef<[u8]>>(t: T) {
 +        println!("{}", std::mem::size_of::<T>());
 +        let _t: &[u8] = t.as_ref();
 +    }
 +
 +    fn main() {
 +        let a: [u8; 100] = [0u8; 100];
 +
 +        // 100
 +        foo::<[u8; 100]>(a);
 +        foo(a);
 +
 +        // 16
 +        foo::<&[u8]>(&a);
 +        foo(a.as_slice());
 +
 +        // 8
 +        foo::<&[u8; 100]>(&a);
 +        foo(a);
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9782_type_relative_variant {
 +    struct S;
 +
 +    impl S {
 +        fn foo<T: AsRef<[u8]>>(t: T) {
 +            println!("{}", std::mem::size_of::<T>());
 +            let _t: &[u8] = t.as_ref();
 +        }
 +    }
 +
 +    fn main() {
 +        let a: [u8; 100] = [0u8; 100];
 +
 +        S::foo::<&[u8; 100]>(&a);
 +    }
 +}
 +
 +mod issue_9782_method_variant {
 +    struct S;
 +
 +    impl S {
 +        fn foo<T: AsRef<[u8]>>(&self, t: T) {
 +            println!("{}", std::mem::size_of::<T>());
 +            let _t: &[u8] = t.as_ref();
 +        }
 +    }
 +
 +    fn main() {
 +        let a: [u8; 100] = [0u8; 100];
 +
 +        S.foo::<&[u8; 100]>(&a);
 +    }
 +}
index 7b97bcf3817ec0bc60c4b93acdfdabf1de063a70,0000000000000000000000000000000000000000..9a01190ed8dbd585127276cde688afa19fd07730
mode 100644,000000..100644
--- /dev/null
@@@ -1,512 -1,0 +1,493 @@@
- #![feature(custom_inner_attributes, lint_reasons)]
- #[warn(clippy::all, clippy::needless_borrow)]
- #[allow(unused_variables)]
- #[allow(
 +// run-rustfix
- #[allow(dead_code)]
++#![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;
 +    let _ = x(&a); // no warning
 +    let _ = x(&&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut &mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &&a
 +        },
 +        46 => &&a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break &ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&&&a);
 +    let _ = x(&mut &&a);
 +    let _ = x(&&&mut b);
 +    let _ = x(&&ref_a);
 +    {
 +        let b = &mut b;
 +        x(&b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(&mut x);
 +    mut_ref(&mut &mut x);
 +    let y: &mut i32 = &mut x;
 +    let y: &mut i32 = &mut &mut x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = (&x).0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (&*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 +
 +    struct S;
 +    impl From<S> for u32 {
 +        fn from(s: S) -> Self {
 +            (&s).into()
 +        }
 +    }
 +    impl From<&S> for u32 {
 +        fn from(s: &S) -> Self {
 +            0
 +        }
 +    }
 +
 +    let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    let _ = std::path::Path::new(".").join(&&".");
 +    deref_target_is_x(&X);
 +    multiple_constraints(&[[""]]);
 +    multiple_constraints_normalizes_to_same(&X, X);
 +    let _ = Some("").unwrap_or(&"");
 +    let _ = std::fs::write("x", &"".to_string());
 +
 +    only_sized(&""); // Don't lint. `Sized` is only bound
 +    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
 +    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
 +    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
 +    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
 +    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 +}
 +
 +#[allow(clippy::needless_borrowed_reference)]
 +fn x(y: &i32) -> i32 {
 +    *y
 +}
 +
 +fn mut_ref(y: &mut i32) {
 +    *y = 5;
 +}
 +
 +fn f<T: Copy>(y: &T) -> T {
 +    *y
 +}
 +
 +fn g(y: &[u8]) -> u8 {
 +    y[0]
 +}
 +
 +trait Trait {}
 +
 +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,
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: Fn() -> T,
 +    {
 +        fn calls_field(&self) -> T {
 +            (&self.f)()
 +        }
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: FnMut() -> T,
 +    {
 +        fn calls_mut_field(&mut self) -> T {
 +            (&mut self.f)()
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +struct X;
 +
 +impl std::ops::Deref for X {
 +    type Target = X;
 +    fn deref(&self) -> &Self::Target {
 +        self
 +    }
 +}
 +
 +fn deref_target_is_x<T>(_: T)
 +where
 +    T: std::ops::Deref<Target = X>,
 +{
 +}
 +
 +fn multiple_constraints<T, U, V, X, Y>(_: T)
 +where
 +    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +    X: IntoIterator<Item = Y>,
 +    Y: AsRef<std::ffi::OsStr>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
 +where
 +    T: std::ops::Deref<Target = U>,
 +    U: std::ops::Deref<Target = V>,
 +{
 +}
 +
 +fn only_sized<T>(_: T) {}
 +
 +fn ref_as_ref_path<T: 'static>(_: &'static T)
 +where
 +    &'static T: AsRef<std::path::Path>,
 +{
 +}
 +
 +trait RefsOnly {
 +    type Referent;
 +}
 +
 +impl<T> RefsOnly for &T {
 +    type Referent = T;
 +}
 +
 +fn refs_only<T, U>(_: T)
 +where
 +    T: RefsOnly<Referent = U>,
 +{
 +}
 +
 +fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
 +where
 +    T: IntoIterator<Item = U>,
 +    U: IntoIterator<Item = V>,
 +    V: AsRef<str>,
 +{
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
-     #![allow(dead_code)]
-     #![clippy::msrv = "1.52.0"]
 +mod copyable_iterator {
 +    #[derive(Clone, Copy)]
 +    struct Iter;
 +    impl Iterator for Iter {
 +        type Item = ();
 +        fn next(&mut self) -> Option<Self::Item> {
 +            None
 +        }
 +    }
 +    fn takes_iter(_: impl Iterator) {}
 +    fn dont_warn(mut x: Iter) {
 +        takes_iter(&mut x);
 +    }
 +    #[allow(unused_mut)]
 +    fn warn(mut x: &mut Iter) {
 +        takes_iter(&mut x)
 +    }
 +}
 +
++#[clippy::msrv = "1.52.0"]
 +mod under_msrv {
-     #![allow(dead_code)]
-     #![clippy::msrv = "1.53.0"]
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
++#[clippy::msrv = "1.53.0"]
 +mod meets_msrv {
- #[allow(unused)]
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
- #[allow(dead_code)]
 +fn issue9383() {
 +    // Should not lint because unions need explicit deref when accessing field
 +    use std::mem::ManuallyDrop;
 +
 +    union Coral {
 +        crab: ManuallyDrop<Vec<i32>>,
 +    }
 +
 +    union Ocean {
 +        coral: ManuallyDrop<Coral>,
 +    }
 +
 +    let mut ocean = Ocean {
 +        coral: ManuallyDrop::new(Coral {
 +            crab: ManuallyDrop::new(vec![1, 2, 3]),
 +        }),
 +    };
 +
 +    unsafe {
 +        ManuallyDrop::drop(&mut (&mut ocean.coral).crab);
 +
 +        (*ocean.coral).crab = ManuallyDrop::new(vec![4, 5, 6]);
 +        ManuallyDrop::drop(&mut (*ocean.coral).crab);
 +
 +        ManuallyDrop::drop(&mut ocean.coral);
 +    }
 +}
 +
- #[allow(dead_code)]
 +fn closure_test() {
 +    let env = "env".to_owned();
 +    let arg = "arg".to_owned();
 +    let f = |arg| {
 +        let loc = "loc".to_owned();
 +        let _ = std::fs::write("x", &env); // Don't lint. In environment
 +        let _ = std::fs::write("x", &arg);
 +        let _ = std::fs::write("x", &loc);
 +    };
 +    let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
 +    f(arg);
 +}
 +
- #[allow(dead_code)]
 +mod significant_drop {
 +    #[derive(Debug)]
 +    struct X;
 +
 +    #[derive(Debug)]
 +    struct Y;
 +
 +    impl Drop for Y {
 +        fn drop(&mut self) {}
 +    }
 +
 +    fn foo(x: X, y: Y) {
 +        debug(&x);
 +        debug(&y); // Don't lint. Has significant drop
 +    }
 +
 +    fn debug(_: impl std::fmt::Debug) {}
 +}
 +
- #[allow(dead_code)]
 +mod used_exactly_once {
 +    fn foo(x: String) {
 +        use_x(&x);
 +    }
 +    fn use_x(_: impl AsRef<str>) {}
 +}
 +
- #[allow(dead_code)]
 +mod used_more_than_once {
 +    fn foo(x: String) {
 +        use_x(&x);
 +        use_x_again(&x);
 +    }
 +    fn use_x(_: impl AsRef<str>) {}
 +    fn use_x_again(_: impl AsRef<str>) {}
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/9111#issuecomment-1277114280
- #[allow(dead_code)]
 +mod issue_9111 {
 +    struct A;
 +
 +    impl Extend<u8> for A {
 +        fn extend<T: IntoIterator<Item = u8>>(&mut self, _: T) {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<'a> Extend<&'a u8> for A {
 +        fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, _: T) {
 +            unimplemented!()
 +        }
 +    }
 +
 +    fn main() {
 +        let mut a = A;
 +        a.extend(&[]); // vs a.extend([]);
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9710 {
 +    fn main() {
 +        let string = String::new();
 +        for _i in 0..10 {
 +            f(&string);
 +        }
 +    }
 +
 +    fn f<T: AsRef<str>>(_: T) {}
 +}
 +
- #[allow(dead_code)]
 +mod issue_9739 {
 +    fn foo<D: std::fmt::Display>(_it: impl IntoIterator<Item = D>) {}
 +
 +    fn main() {
 +        foo(if std::env::var_os("HI").is_some() {
 +            &[0]
 +        } else {
 +            &[] as &[u32]
 +        });
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9739_method_variant {
 +    struct S;
 +
 +    impl S {
 +        fn foo<D: std::fmt::Display>(&self, _it: impl IntoIterator<Item = D>) {}
 +    }
 +
 +    fn main() {
 +        S.foo(if std::env::var_os("HI").is_some() {
 +            &[0]
 +        } else {
 +            &[] as &[u32]
 +        });
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9782 {
 +    fn foo<T: AsRef<[u8]>>(t: T) {
 +        println!("{}", std::mem::size_of::<T>());
 +        let _t: &[u8] = t.as_ref();
 +    }
 +
 +    fn main() {
 +        let a: [u8; 100] = [0u8; 100];
 +
 +        // 100
 +        foo::<[u8; 100]>(a);
 +        foo(a);
 +
 +        // 16
 +        foo::<&[u8]>(&a);
 +        foo(a.as_slice());
 +
 +        // 8
 +        foo::<&[u8; 100]>(&a);
 +        foo(&a);
 +    }
 +}
 +
- #[allow(dead_code)]
 +mod issue_9782_type_relative_variant {
 +    struct S;
 +
 +    impl S {
 +        fn foo<T: AsRef<[u8]>>(t: T) {
 +            println!("{}", std::mem::size_of::<T>());
 +            let _t: &[u8] = t.as_ref();
 +        }
 +    }
 +
 +    fn main() {
 +        let a: [u8; 100] = [0u8; 100];
 +
 +        S::foo::<&[u8; 100]>(&a);
 +    }
 +}
 +
 +mod issue_9782_method_variant {
 +    struct S;
 +
 +    impl S {
 +        fn foo<T: AsRef<[u8]>>(&self, t: T) {
 +            println!("{}", std::mem::size_of::<T>());
 +            let _t: &[u8] = t.as_ref();
 +        }
 +    }
 +
 +    fn main() {
 +        let a: [u8; 100] = [0u8; 100];
 +
 +        S.foo::<&[u8; 100]>(&a);
 +    }
 +}
index 485e6b84c868b11972932b16a35f6e664e7ae6f4,0000000000000000000000000000000000000000..d26c317124b8d18d3dc900d31c8badce92e20af1
mode 100644,000000..100644
--- /dev/null
@@@ -1,220 -1,0 +1,220 @@@
-   --> $DIR/needless_borrow.rs:192:13
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:15:15
 +   |
 +LL |     let _ = x(&&a); // warn
 +   |               ^^^ help: change this to: `&a`
 +   |
 +   = note: `-D clippy::needless-borrow` implied by `-D warnings`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:19:13
 +   |
 +LL |     mut_ref(&mut &mut b); // warn
 +   |             ^^^^^^^^^^^ help: change this to: `&mut b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:31:13
 +   |
 +LL |             &&a
 +   |             ^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:33:15
 +   |
 +LL |         46 => &&a,
 +   |               ^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:39:27
 +   |
 +LL |                     break &ref_a;
 +   |                           ^^^^^^ help: change this to: `ref_a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:46:15
 +   |
 +LL |     let _ = x(&&&a);
 +   |               ^^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:47:15
 +   |
 +LL |     let _ = x(&mut &&a);
 +   |               ^^^^^^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:48:15
 +   |
 +LL |     let _ = x(&&&mut b);
 +   |               ^^^^^^^^ help: change this to: `&mut b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:49:15
 +   |
 +LL |     let _ = x(&&ref_a);
 +   |               ^^^^^^^ help: change this to: `ref_a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:52:11
 +   |
 +LL |         x(&b);
 +   |           ^^ help: change this to: `b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:59:13
 +   |
 +LL |     mut_ref(&mut x);
 +   |             ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:60:13
 +   |
 +LL |     mut_ref(&mut &mut x);
 +   |             ^^^^^^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:61:23
 +   |
 +LL |     let y: &mut i32 = &mut x;
 +   |                       ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:62:23
 +   |
 +LL |     let y: &mut i32 = &mut &mut x;
 +   |                       ^^^^^^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:71:14
 +   |
 +LL |         0 => &mut x,
 +   |              ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:77:14
 +   |
 +LL |         0 => &mut x,
 +   |              ^^^^^^ help: change this to: `x`
 +
 +error: this expression borrows a value the compiler would automatically borrow
 +  --> $DIR/needless_borrow.rs:89:13
 +   |
 +LL |     let _ = (&x).0;
 +   |             ^^^^ help: change this to: `x`
 +
 +error: this expression borrows a value the compiler would automatically borrow
 +  --> $DIR/needless_borrow.rs:91:22
 +   |
 +LL |     let _ = unsafe { (&*x).0 };
 +   |                      ^^^^^ help: change this to: `(*x)`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:101:5
 +   |
 +LL |     (&&()).foo();
 +   |     ^^^^^^ help: change this to: `(&())`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:110:5
 +   |
 +LL |     (&&5).foo();
 +   |     ^^^^^ help: change this to: `(&5)`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:135:51
 +   |
 +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:136:44
 +   |
 +LL |     let _ = std::path::Path::new(".").join(&&".");
 +   |                                            ^^^^^ help: change this to: `"."`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:137:23
 +   |
 +LL |     deref_target_is_x(&X);
 +   |                       ^^ help: change this to: `X`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:138:26
 +   |
 +LL |     multiple_constraints(&[[""]]);
 +   |                          ^^^^^^^ help: change this to: `[[""]]`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:139:45
 +   |
 +LL |     multiple_constraints_normalizes_to_same(&X, X);
 +   |                                             ^^ help: change this to: `X`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:140:32
 +   |
 +LL |     let _ = Some("").unwrap_or(&"");
 +   |                                ^^^ help: change this to: `""`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:141:33
 +   |
 +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:201: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:286:20
++  --> $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:304:55
++  --> $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:344:37
++  --> $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:345: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:364:15
++  --> $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:374: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:474:13
++  --> $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:457:13
 +   |
 +LL |         foo(&a);
 +   |             ^^ help: change this to: `a`
 +
 +error: aborting due to 36 previous errors
 +
index ba9d15e59d0e45376374c23b07b87c661def3a17,0000000000000000000000000000000000000000..7eaca571992f46d971bea18739e3b7c50e565381
mode 100644,000000..100644
--- /dev/null
@@@ -1,140 -1,0 +1,139 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
 +#![warn(clippy::needless_question_mark)]
 +#![allow(
 +    clippy::needless_return,
 +    clippy::unnecessary_unwrap,
 +    clippy::upper_case_acronyms,
 +    dead_code,
 +    unused_must_use
 +)]
 +
 +struct TO {
 +    magic: Option<usize>,
 +}
 +
 +struct TR {
 +    magic: Result<usize, bool>,
 +}
 +
 +fn simple_option_bad1(to: TO) -> Option<usize> {
 +    // return as a statement
 +    return to.magic;
 +}
 +
 +// formatting will add a semi-colon, which would make
 +// this identical to the test case above
 +#[rustfmt::skip]
 +fn simple_option_bad2(to: TO) -> Option<usize> {
 +    // return as an expression
 +    return to.magic
 +}
 +
 +fn simple_option_bad3(to: TO) -> Option<usize> {
 +    // block value "return"
 +    to.magic
 +}
 +
 +fn simple_option_bad4(to: Option<TO>) -> Option<usize> {
 +    // single line closure
 +    to.and_then(|t| t.magic)
 +}
 +
 +// formatting this will remove the block brackets, making
 +// this test identical to the one above
 +#[rustfmt::skip]
 +fn simple_option_bad5(to: Option<TO>) -> Option<usize> {
 +    // closure with body
 +    to.and_then(|t| {
 +        t.magic
 +    })
 +}
 +
 +fn simple_result_bad1(tr: TR) -> Result<usize, bool> {
 +    return tr.magic;
 +}
 +
 +// formatting will add a semi-colon, which would make
 +// this identical to the test case above
 +#[rustfmt::skip]
 +fn simple_result_bad2(tr: TR) -> Result<usize, bool> {
 +    return tr.magic
 +}
 +
 +fn simple_result_bad3(tr: TR) -> Result<usize, bool> {
 +    tr.magic
 +}
 +
 +fn simple_result_bad4(tr: Result<TR, bool>) -> Result<usize, bool> {
 +    tr.and_then(|t| t.magic)
 +}
 +
 +// formatting this will remove the block brackets, making
 +// this test identical to the one above
 +#[rustfmt::skip]
 +fn simple_result_bad5(tr: Result<TR, bool>) -> Result<usize, bool> {
 +    tr.and_then(|t| {
 +        t.magic
 +    })
 +}
 +
 +fn also_bad(tr: Result<TR, bool>) -> Result<usize, bool> {
 +    if tr.is_ok() {
 +        let t = tr.unwrap();
 +        return t.magic;
 +    }
 +    Err(false)
 +}
 +
 +fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
 +where
 +    T: From<U>,
 +{
 +    Ok(x?)
 +}
 +
 +// not quite needless
 +fn deref_ref(s: Option<&String>) -> Option<&str> {
 +    Some(s?)
 +}
 +
 +fn main() {}
 +
 +// #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
 +// the suggestion fails to apply; do not lint
 +macro_rules! some_in_macro {
 +    ($expr:expr) => {
 +        || -> _ { Some($expr) }()
 +    };
 +}
 +
 +pub fn test1() {
 +    let x = Some(3);
 +    let _x = some_in_macro!(x?);
 +}
 +
 +// this one is ok because both the ? and the Some are both inside the macro def
 +macro_rules! some_and_qmark_in_macro {
 +    ($expr:expr) => {
 +        || -> Option<_> { Some($expr) }()
 +    };
 +}
 +
 +pub fn test2() {
 +    let x = Some(3);
 +    let _x = some_and_qmark_in_macro!(x?);
 +}
 +
 +async fn async_option_bad(to: TO) -> Option<usize> {
 +    let _ = Some(3);
 +    to.magic
 +}
 +
 +async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
 +    Some(s?)
 +}
 +
 +async fn async_result_bad(s: TR) -> Result<usize, bool> {
 +    s.magic
 +}
index 3a6523e8fe872a94b172c851d1c997f9a1ed9350,0000000000000000000000000000000000000000..960bc7b78983f96d17d454b568f9c17eafa72aa6
mode 100644,000000..100644
--- /dev/null
@@@ -1,140 -1,0 +1,139 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
 +#![warn(clippy::needless_question_mark)]
 +#![allow(
 +    clippy::needless_return,
 +    clippy::unnecessary_unwrap,
 +    clippy::upper_case_acronyms,
 +    dead_code,
 +    unused_must_use
 +)]
 +
 +struct TO {
 +    magic: Option<usize>,
 +}
 +
 +struct TR {
 +    magic: Result<usize, bool>,
 +}
 +
 +fn simple_option_bad1(to: TO) -> Option<usize> {
 +    // return as a statement
 +    return Some(to.magic?);
 +}
 +
 +// formatting will add a semi-colon, which would make
 +// this identical to the test case above
 +#[rustfmt::skip]
 +fn simple_option_bad2(to: TO) -> Option<usize> {
 +    // return as an expression
 +    return Some(to.magic?)
 +}
 +
 +fn simple_option_bad3(to: TO) -> Option<usize> {
 +    // block value "return"
 +    Some(to.magic?)
 +}
 +
 +fn simple_option_bad4(to: Option<TO>) -> Option<usize> {
 +    // single line closure
 +    to.and_then(|t| Some(t.magic?))
 +}
 +
 +// formatting this will remove the block brackets, making
 +// this test identical to the one above
 +#[rustfmt::skip]
 +fn simple_option_bad5(to: Option<TO>) -> Option<usize> {
 +    // closure with body
 +    to.and_then(|t| {
 +        Some(t.magic?)
 +    })
 +}
 +
 +fn simple_result_bad1(tr: TR) -> Result<usize, bool> {
 +    return Ok(tr.magic?);
 +}
 +
 +// formatting will add a semi-colon, which would make
 +// this identical to the test case above
 +#[rustfmt::skip]
 +fn simple_result_bad2(tr: TR) -> Result<usize, bool> {
 +    return Ok(tr.magic?)
 +}
 +
 +fn simple_result_bad3(tr: TR) -> Result<usize, bool> {
 +    Ok(tr.magic?)
 +}
 +
 +fn simple_result_bad4(tr: Result<TR, bool>) -> Result<usize, bool> {
 +    tr.and_then(|t| Ok(t.magic?))
 +}
 +
 +// formatting this will remove the block brackets, making
 +// this test identical to the one above
 +#[rustfmt::skip]
 +fn simple_result_bad5(tr: Result<TR, bool>) -> Result<usize, bool> {
 +    tr.and_then(|t| {
 +        Ok(t.magic?)
 +    })
 +}
 +
 +fn also_bad(tr: Result<TR, bool>) -> Result<usize, bool> {
 +    if tr.is_ok() {
 +        let t = tr.unwrap();
 +        return Ok(t.magic?);
 +    }
 +    Err(false)
 +}
 +
 +fn false_positive_test<U, T>(x: Result<(), U>) -> Result<(), T>
 +where
 +    T: From<U>,
 +{
 +    Ok(x?)
 +}
 +
 +// not quite needless
 +fn deref_ref(s: Option<&String>) -> Option<&str> {
 +    Some(s?)
 +}
 +
 +fn main() {}
 +
 +// #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
 +// the suggestion fails to apply; do not lint
 +macro_rules! some_in_macro {
 +    ($expr:expr) => {
 +        || -> _ { Some($expr) }()
 +    };
 +}
 +
 +pub fn test1() {
 +    let x = Some(3);
 +    let _x = some_in_macro!(x?);
 +}
 +
 +// this one is ok because both the ? and the Some are both inside the macro def
 +macro_rules! some_and_qmark_in_macro {
 +    ($expr:expr) => {
 +        || -> Option<_> { Some(Some($expr)?) }()
 +    };
 +}
 +
 +pub fn test2() {
 +    let x = Some(3);
 +    let _x = some_and_qmark_in_macro!(x?);
 +}
 +
 +async fn async_option_bad(to: TO) -> Option<usize> {
 +    let _ = Some(3);
 +    Some(to.magic?)
 +}
 +
 +async fn async_deref_ref(s: Option<&String>) -> Option<&str> {
 +    Some(s?)
 +}
 +
 +async fn async_result_bad(s: TR) -> Result<usize, bool> {
 +    Ok(s.magic?)
 +}
index f8308e24e7712e62adbf9b248757a3c9b0b32abb,0000000000000000000000000000000000000000..d1f89e326c67c3ab65b1dd7e6f31eb52e0093c01
mode 100644,000000..100644
--- /dev/null
@@@ -1,93 -1,0 +1,93 @@@
-   --> $DIR/needless_question_mark.rs:23:12
 +error: question mark operator is useless here
-   --> $DIR/needless_question_mark.rs:31:12
++  --> $DIR/needless_question_mark.rs:22:12
 +   |
 +LL |     return Some(to.magic?);
 +   |            ^^^^^^^^^^^^^^^ help: try removing question mark and `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:36:5
++  --> $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:41:21
++  --> $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:50:9
++  --> $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:55:12
++  --> $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:62: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:66:5
++  --> $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:70:21
++  --> $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:78:9
++  --> $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:85:16
++  --> $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:120:27
++  --> $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:131:5
++  --> $DIR/needless_question_mark.rs:119:27
 +   |
 +LL |         || -> Option<_> { Some(Some($expr)?) }()
 +   |                           ^^^^^^^^^^^^^^^^^^ help: try removing question mark and `Some()`: `Some($expr)`
 +...
 +LL |     let _x = some_and_qmark_in_macro!(x?);
 +   |              ---------------------------- in this macro invocation
 +   |
 +   = 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:139: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:138:5
 +   |
 +LL |     Ok(s.magic?)
 +   |     ^^^^^^^^^^^^ help: try removing question mark and `Ok()`: `s.magic`
 +
 +error: aborting due to 14 previous errors
 +
index d2163b14fcadca01e1c205a3ce89a94f7a865fde,0000000000000000000000000000000000000000..4386aaec49e2437751803ac8f9ce216c17e79f07
mode 100644,000000..100644
--- /dev/null
@@@ -1,272 -1,0 +1,275 @@@
-     
 +// run-rustfix
 +
 +#![feature(lint_reasons)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::if_same_then_else,
 +    clippy::single_match,
 +    clippy::needless_bool,
 +    clippy::equatable_if_let
 +)]
 +#![warn(clippy::needless_return)]
 +
 +use std::cell::RefCell;
 +
 +macro_rules! the_answer {
 +    () => {
 +        42
 +    };
 +}
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    true
 +}
 +
 +fn test_no_semicolon() -> bool {
 +    true
 +}
 +
 +fn test_if_block() -> bool {
 +    if true {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => {
 +            true
 +        },
 +    }
 +}
 +
 +fn test_closure() {
 +    let _ = || {
 +        true
 +    };
 +    let _ = || true;
 +}
 +
 +fn test_macro_call() -> i32 {
 +    the_answer!()
 +}
 +
 +fn test_void_fun() {
-         
 +}
 +
 +fn test_void_if_fun(b: bool) {
 +    if b {
-         
 +    } else {
-             
 +    }
 +}
 +
 +fn test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => (),
 +    }
 +}
 +
 +fn test_nested_match(x: u32) {
 +    match x {
 +        0 => (),
 +        1 => {
 +            let _ = 42;
-             
 +        },
 +        _ => (),
 +    }
 +}
 +
 +fn temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +fn borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        String::from("test")
 +    } else {
 +        String::new()
 +    }
 +}
 +
 +macro_rules! needed_return {
 +    ($e:expr) => {
 +        if $e > 3 {
 +            return;
 +        }
 +    };
 +}
 +
 +fn test_return_in_macro() {
 +    // This will return and the macro below won't be executed. Removing the `return` from the macro
 +    // will change semantics.
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +mod issue6501 {
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn foo(bar: Result<(), ()>) {
 +        bar.unwrap_or_else(|_| {})
 +    }
 +
 +    fn test_closure() {
 +        let _ = || {
-     
 +        };
 +        let _ = || {};
 +    }
 +
 +    struct Foo;
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn bar(res: Result<Foo, u8>) -> Foo {
 +        res.unwrap_or_else(|_| Foo)
 +    }
 +}
 +
 +async fn async_test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    true
 +}
 +
 +async fn async_test_no_semicolon() -> bool {
 +    true
 +}
 +
 +async fn async_test_if_block() -> bool {
 +    if true {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +async fn async_test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => {
 +            true
 +        },
 +    }
 +}
 +
 +async fn async_test_closure() {
 +    let _ = || {
 +        true
 +    };
 +    let _ = || true;
 +}
 +
 +async fn async_test_macro_call() -> i32 {
 +    the_answer!()
 +}
 +
 +async fn async_test_void_fun() {
-         
 +}
 +
 +async fn async_test_void_if_fun(b: bool) {
 +    if b {
-         
 +    } else {
 +    }
 +}
 +
 +async fn async_test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => (),
 +    }
 +}
 +
 +async fn async_temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +async fn async_borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        String::from("test")
 +    } else {
 +        String::new()
 +    }
 +}
 +
 +async fn async_test_return_in_macro() {
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +fn let_else() {
 +    let Some(1) = Some(1) else { return };
 +}
 +
 +fn needless_return_macro() -> String {
 +    let _ = "foo";
 +    let _ = "bar";
 +    format!("Hello {}", "world!")
 +}
 +
 +fn issue_9361() -> i32 {
 +    #[allow(clippy::integer_arithmetic)]
 +    return 1 + 2;
 +}
 +
 +fn issue8336(x: i32) -> bool {
 +    if x > 0 {
 +        println!("something");
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +fn issue8156(x: u8) -> u64 {
 +    match x {
 +        80 => {
 +            10
 +        },
 +        _ => {
 +            100
 +        },
 +    }
 +}
 +
 +// Ideally the compiler should throw `unused_braces` in this case
 +fn issue9192() -> i32 {
 +    {
 +        0
 +    }
 +}
 +
 +fn issue9503(x: usize) -> isize {
 +    unsafe {
 +        if x > 12 {
 +            *(x as *const isize)
 +        } else {
 +            !*(x as *const isize)
 +        }
 +    }
 +}
 +
++mod issue9416 {
++    pub fn with_newline() {
++        let _ = 42;
++    }
++
++    #[rustfmt::skip]
++    pub fn oneline() {
++        let _ = 42;
++    }
++}
++
 +fn main() {}
index 114414b5fac76b7d522ac6e246293e2ccb78533b,0000000000000000000000000000000000000000..666dc54b76b412b2267e083d3d130929cc07309e
mode 100644,000000..100644
--- /dev/null
@@@ -1,272 -1,0 +1,285 @@@
 +// run-rustfix
 +
 +#![feature(lint_reasons)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::if_same_then_else,
 +    clippy::single_match,
 +    clippy::needless_bool,
 +    clippy::equatable_if_let
 +)]
 +#![warn(clippy::needless_return)]
 +
 +use std::cell::RefCell;
 +
 +macro_rules! the_answer {
 +    () => {
 +        42
 +    };
 +}
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    return true;
 +}
 +
 +fn test_no_semicolon() -> bool {
 +    return true;
 +}
 +
 +fn test_if_block() -> bool {
 +    if true {
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => {
 +            return true;
 +        },
 +    }
 +}
 +
 +fn test_closure() {
 +    let _ = || {
 +        return true;
 +    };
 +    let _ = || return true;
 +}
 +
 +fn test_macro_call() -> i32 {
 +    return the_answer!();
 +}
 +
 +fn test_void_fun() {
 +    return;
 +}
 +
 +fn test_void_if_fun(b: bool) {
 +    if b {
 +        return;
 +    } else {
 +        return;
 +    }
 +}
 +
 +fn test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => return,
 +    }
 +}
 +
 +fn test_nested_match(x: u32) {
 +    match x {
 +        0 => (),
 +        1 => {
 +            let _ = 42;
 +            return;
 +        },
 +        _ => return,
 +    }
 +}
 +
 +fn temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +fn borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        return String::from("test");
 +    } else {
 +        return String::new();
 +    }
 +}
 +
 +macro_rules! needed_return {
 +    ($e:expr) => {
 +        if $e > 3 {
 +            return;
 +        }
 +    };
 +}
 +
 +fn test_return_in_macro() {
 +    // This will return and the macro below won't be executed. Removing the `return` from the macro
 +    // will change semantics.
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +mod issue6501 {
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn foo(bar: Result<(), ()>) {
 +        bar.unwrap_or_else(|_| return)
 +    }
 +
 +    fn test_closure() {
 +        let _ = || {
 +            return;
 +        };
 +        let _ = || return;
 +    }
 +
 +    struct Foo;
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn bar(res: Result<Foo, u8>) -> Foo {
 +        res.unwrap_or_else(|_| return Foo)
 +    }
 +}
 +
 +async fn async_test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    return true;
 +}
 +
 +async fn async_test_no_semicolon() -> bool {
 +    return true;
 +}
 +
 +async fn async_test_if_block() -> bool {
 +    if true {
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +async fn async_test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => {
 +            return true;
 +        },
 +    }
 +}
 +
 +async fn async_test_closure() {
 +    let _ = || {
 +        return true;
 +    };
 +    let _ = || return true;
 +}
 +
 +async fn async_test_macro_call() -> i32 {
 +    return the_answer!();
 +}
 +
 +async fn async_test_void_fun() {
 +    return;
 +}
 +
 +async fn async_test_void_if_fun(b: bool) {
 +    if b {
 +        return;
 +    } else {
 +        return;
 +    }
 +}
 +
 +async fn async_test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => return,
 +    }
 +}
 +
 +async fn async_temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +async fn async_borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        return String::from("test");
 +    } else {
 +        return String::new();
 +    }
 +}
 +
 +async fn async_test_return_in_macro() {
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +fn let_else() {
 +    let Some(1) = Some(1) else { return };
 +}
 +
 +fn needless_return_macro() -> String {
 +    let _ = "foo";
 +    let _ = "bar";
 +    return format!("Hello {}", "world!");
 +}
 +
 +fn issue_9361() -> i32 {
 +    #[allow(clippy::integer_arithmetic)]
 +    return 1 + 2;
 +}
 +
 +fn issue8336(x: i32) -> bool {
 +    if x > 0 {
 +        println!("something");
 +        return true;
 +    } else {
 +        return false;
 +    };
 +}
 +
 +fn issue8156(x: u8) -> u64 {
 +    match x {
 +        80 => {
 +            return 10;
 +        },
 +        _ => {
 +            return 100;
 +        },
 +    };
 +}
 +
 +// Ideally the compiler should throw `unused_braces` in this case
 +fn issue9192() -> i32 {
 +    {
 +        return 0;
 +    };
 +}
 +
 +fn issue9503(x: usize) -> isize {
 +    unsafe {
 +        if x > 12 {
 +            return *(x as *const isize);
 +        } else {
 +            return !*(x as *const isize);
 +        };
 +    };
 +}
 +
++mod issue9416 {
++    pub fn with_newline() {
++        let _ = 42;
++
++        return;
++    }
++
++    #[rustfmt::skip]
++    pub fn oneline() {
++        let _ = 42; return;
++    }
++}
++
 +fn main() {}
index 047fb6c2311a2153e6db1cc8c69df5867d485782,0000000000000000000000000000000000000000..a8b5d86cd5581f46a415a3c06f6e2d88cfa0ae60
mode 100644,000000..100644
--- /dev/null
@@@ -1,355 -1,0 +1,390 @@@
-   --> $DIR/needless_return.rs:62:5
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:26:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::needless-return` implied by `-D warnings`
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:30:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:35:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:37:9
 +   |
 +LL |         return false;
 +   |         ^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:43:17
 +   |
 +LL |         true => return false,
 +   |                 ^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:45:13
 +   |
 +LL |             return true;
 +   |             ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:52:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:54:16
 +   |
 +LL |     let _ = || return true;
 +   |                ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:58:5
 +   |
 +LL |     return the_answer!();
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
- LL |     return;
-    |     ^^^^^^
++  --> $DIR/needless_return.rs:61:21
 +   |
-   --> $DIR/needless_return.rs:67:9
++LL |   fn test_void_fun() {
++   |  _____________________^
++LL | |     return;
++   | |__________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
- LL |         return;
-    |         ^^^^^^
++  --> $DIR/needless_return.rs:66:11
 +   |
-   --> $DIR/needless_return.rs:69:9
++LL |       if b {
++   |  ___________^
++LL | |         return;
++   | |______________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
- LL |         return;
-    |         ^^^^^^
++  --> $DIR/needless_return.rs:68:13
 +   |
-   --> $DIR/needless_return.rs:85:13
++LL |       } else {
++   |  _____________^
++LL | |         return;
++   | |______________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:76:14
 +   |
 +LL |         _ => return,
 +   |              ^^^^^^
 +   |
 +   = help: replace `return` with a unit value
 +
 +error: unneeded `return` statement
- LL |             return;
-    |             ^^^^^^
++  --> $DIR/needless_return.rs:84:24
 +   |
-   --> $DIR/needless_return.rs:129:13
++LL |               let _ = 42;
++   |  ________________________^
++LL | |             return;
++   | |__________________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:87:14
 +   |
 +LL |         _ => return,
 +   |              ^^^^^^
 +   |
 +   = help: replace `return` with a unit value
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:100:9
 +   |
 +LL |         return String::from("test");
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:102:9
 +   |
 +LL |         return String::new();
 +   |         ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:124:32
 +   |
 +LL |         bar.unwrap_or_else(|_| return)
 +   |                                ^^^^^^
 +   |
 +   = help: replace `return` with an empty block
 +
 +error: unneeded `return` statement
- LL |             return;
-    |             ^^^^^^
++  --> $DIR/needless_return.rs:128:21
 +   |
-   --> $DIR/needless_return.rs:182:5
++LL |           let _ = || {
++   |  _____________________^
++LL | |             return;
++   | |__________________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:131:20
 +   |
 +LL |         let _ = || return;
 +   |                    ^^^^^^
 +   |
 +   = help: replace `return` with an empty block
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:137:32
 +   |
 +LL |         res.unwrap_or_else(|_| return Foo)
 +   |                                ^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:146:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:150:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:155:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:157:9
 +   |
 +LL |         return false;
 +   |         ^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:163:17
 +   |
 +LL |         true => return false,
 +   |                 ^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:165:13
 +   |
 +LL |             return true;
 +   |             ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:172:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:174:16
 +   |
 +LL |     let _ = || return true;
 +   |                ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:178:5
 +   |
 +LL |     return the_answer!();
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
- LL |     return;
-    |     ^^^^^^
++  --> $DIR/needless_return.rs:181:33
 +   |
-   --> $DIR/needless_return.rs:187:9
++LL |   async fn async_test_void_fun() {
++   |  _________________________________^
++LL | |     return;
++   | |__________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
- LL |         return;
-    |         ^^^^^^
++  --> $DIR/needless_return.rs:186:11
 +   |
-   --> $DIR/needless_return.rs:189:9
++LL |       if b {
++   |  ___________^
++LL | |         return;
++   | |______________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
- LL |         return;
-    |         ^^^^^^
++  --> $DIR/needless_return.rs:188:13
 +   |
- error: aborting due to 44 previous errors
++LL |       } else {
++   |  _____________^
++LL | |         return;
++   | |______________^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:196:14
 +   |
 +LL |         _ => return,
 +   |              ^^^^^^
 +   |
 +   = help: replace `return` with a unit value
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:209:9
 +   |
 +LL |         return String::from("test");
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:211:9
 +   |
 +LL |         return String::new();
 +   |         ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:227:5
 +   |
 +LL |     return format!("Hello {}", "world!");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:238:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:240:9
 +   |
 +LL |         return false;
 +   |         ^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:247:13
 +   |
 +LL |             return 10;
 +   |             ^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:250:13
 +   |
 +LL |             return 100;
 +   |             ^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:258:9
 +   |
 +LL |         return 0;
 +   |         ^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:265:13
 +   |
 +LL |             return *(x as *const isize);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:267:13
 +   |
 +LL |             return !*(x as *const isize);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: remove `return`
 +
++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
 +
index 61f5fc4e679edafbb609cfa70dd550ff3df2e39e,0000000000000000000000000000000000000000..5496031fefabf9f9fa8335328850685a5e13621e
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,46 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// edition:2018
 +
-     #![clippy::msrv = "1.51"]
 +#![warn(clippy::needless_splitn)]
 +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
 +    let str = "key=value=end";
 +    let _ = str.split('=').next();
 +    let _ = str.split('=').nth(0);
 +    let _ = str.splitn(2, '=').nth(1);
 +    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.split('=').next_tuple().unwrap();
 +    let _: Vec<&str> = str.splitn(3, '=').collect();
 +
 +    let _ = str.rsplit('=').next();
 +    let _ = str.rsplit('=').nth(0);
 +    let _ = str.rsplitn(2, '=').nth(1);
 +    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.rsplit('=').next_tuple().unwrap();
 +
 +    let _ = str.split('=').next();
 +    let _ = str.split('=').nth(3);
 +    let _ = str.splitn(5, '=').nth(4);
 +    let _ = str.splitn(5, '=').nth(5);
 +}
 +
 +fn _question_mark(s: &str) -> Option<()> {
 +    let _ = s.split('=').next()?;
 +    let _ = s.split('=').nth(0)?;
 +    let _ = s.rsplit('=').next()?;
 +    let _ = s.rsplit('=').nth(0)?;
 +
 +    Some(())
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn _test_msrv() {
 +    // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
 +    let _ = "key=value".split('=').nth(0).unwrap();
 +}
index 71d9a7077faa6f354e9475507b456d8e53e884e9,0000000000000000000000000000000000000000..35c2465bae13138f2da781f87ed30d800f84e35a
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,46 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// edition:2018
 +
-     #![clippy::msrv = "1.51"]
 +#![warn(clippy::needless_splitn)]
 +#![allow(clippy::iter_skip_next, clippy::iter_nth_zero, clippy::manual_split_once)]
 +
 +extern crate itertools;
 +
 +#[allow(unused_imports)]
 +use itertools::Itertools;
 +
 +fn main() {
 +    let str = "key=value=end";
 +    let _ = str.splitn(2, '=').next();
 +    let _ = str.splitn(2, '=').nth(0);
 +    let _ = str.splitn(2, '=').nth(1);
 +    let (_, _) = str.splitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.splitn(3, '=').next_tuple().unwrap();
 +    let _: Vec<&str> = str.splitn(3, '=').collect();
 +
 +    let _ = str.rsplitn(2, '=').next();
 +    let _ = str.rsplitn(2, '=').nth(0);
 +    let _ = str.rsplitn(2, '=').nth(1);
 +    let (_, _) = str.rsplitn(2, '=').next_tuple().unwrap();
 +    let (_, _) = str.rsplitn(3, '=').next_tuple().unwrap();
 +
 +    let _ = str.splitn(5, '=').next();
 +    let _ = str.splitn(5, '=').nth(3);
 +    let _ = str.splitn(5, '=').nth(4);
 +    let _ = str.splitn(5, '=').nth(5);
 +}
 +
 +fn _question_mark(s: &str) -> Option<()> {
 +    let _ = s.splitn(2, '=').next()?;
 +    let _ = s.splitn(2, '=').nth(0)?;
 +    let _ = s.rsplitn(2, '=').next()?;
 +    let _ = s.rsplitn(2, '=').nth(0)?;
 +
 +    Some(())
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn _test_msrv() {
 +    // `manual_split_once` MSRV shouldn't apply to `needless_splitn`
 +    let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 +}
index f112b29e7f2066ccb0751f049d479b45d5a79c58,0000000000000000000000000000000000000000..f607d8e1ab5f40dd97e380dc78c55c2bf98353f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,82 @@@
-   --> $DIR/needless_splitn.rs:15:13
 +error: unnecessary use of `splitn`
-   --> $DIR/needless_splitn.rs:16:13
++  --> $DIR/needless_splitn.rs:14:13
 +   |
 +LL |     let _ = str.splitn(2, '=').next();
 +   |             ^^^^^^^^^^^^^^^^^^ help: try this: `str.split('=')`
 +   |
 +   = note: `-D clippy::needless-splitn` implied by `-D warnings`
 +
 +error: unnecessary use of `splitn`
-   --> $DIR/needless_splitn.rs:19:18
++  --> $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:22:13
++  --> $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:23: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:26:18
++  --> $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:28:13
++  --> $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:29: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:35: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:36: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:37: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:38: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:46: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:45:13
 +   |
 +LL |     let _ = "key=value".splitn(2, '=').nth(0).unwrap();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `"key=value".split('=')`
 +
 +error: aborting due to 13 previous errors
 +
index bc376d0d7fb391eed0b6806779ed5bfec1063b47,0000000000000000000000000000000000000000..d124d133faa21ff8418703726c06ae7a941f9f35
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,56 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.39"]
 +#![allow(unused, clippy::redundant_clone)]
 +#![warn(clippy::option_as_ref_deref)]
 +
 +use std::ffi::{CString, OsString};
 +use std::ops::{Deref, DerefMut};
 +use std::path::PathBuf;
 +
 +fn main() {
 +    let mut opt = Some(String::from("123"));
 +
 +    let _ = opt.clone().as_deref().map(str::len);
 +
 +    #[rustfmt::skip]
 +    let _ = opt.clone().as_deref()
 +        .map(str::len);
 +
 +    let _ = opt.as_deref_mut();
 +
 +    let _ = opt.as_deref();
 +    let _ = opt.as_deref();
 +    let _ = opt.as_deref_mut();
 +    let _ = opt.as_deref_mut();
 +    let _ = Some(CString::new(vec![]).unwrap()).as_deref();
 +    let _ = Some(OsString::new()).as_deref();
 +    let _ = Some(PathBuf::new()).as_deref();
 +    let _ = Some(Vec::<()>::new()).as_deref();
 +    let _ = Some(Vec::<()>::new()).as_deref_mut();
 +
 +    let _ = opt.as_deref();
 +    let _ = opt.clone().as_deref_mut().map(|x| x.len());
 +
 +    let vc = vec![String::new()];
 +    let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
 +
 +    let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
 +
 +    let _ = opt.as_deref();
 +    let _ = opt.as_deref_mut();
 +
 +    // Issue #5927
 +    let _ = opt.as_deref();
 +}
 +
++#[clippy::msrv = "1.39"]
 +fn msrv_1_39() {
-     #![clippy::msrv = "1.40"]
 +    let opt = Some(String::from("123"));
 +    let _ = opt.as_ref().map(String::as_str);
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
 +    let opt = Some(String::from("123"));
 +    let _ = opt.as_deref();
 +}
index ba3a2eedc225cf42b27803ce102c4a4713344fb6,0000000000000000000000000000000000000000..86e354c6716bba5da71df2c5fa42f95d4fd42656
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,59 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.39"]
 +#![allow(unused, clippy::redundant_clone)]
 +#![warn(clippy::option_as_ref_deref)]
 +
 +use std::ffi::{CString, OsString};
 +use std::ops::{Deref, DerefMut};
 +use std::path::PathBuf;
 +
 +fn main() {
 +    let mut opt = Some(String::from("123"));
 +
 +    let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
 +
 +    #[rustfmt::skip]
 +    let _ = opt.clone()
 +        .as_ref().map(
 +            Deref::deref
 +        )
 +        .map(str::len);
 +
 +    let _ = opt.as_mut().map(DerefMut::deref_mut);
 +
 +    let _ = opt.as_ref().map(String::as_str);
 +    let _ = opt.as_ref().map(|x| x.as_str());
 +    let _ = opt.as_mut().map(String::as_mut_str);
 +    let _ = opt.as_mut().map(|x| x.as_mut_str());
 +    let _ = Some(CString::new(vec![]).unwrap()).as_ref().map(CString::as_c_str);
 +    let _ = Some(OsString::new()).as_ref().map(OsString::as_os_str);
 +    let _ = Some(PathBuf::new()).as_ref().map(PathBuf::as_path);
 +    let _ = Some(Vec::<()>::new()).as_ref().map(Vec::as_slice);
 +    let _ = Some(Vec::<()>::new()).as_mut().map(Vec::as_mut_slice);
 +
 +    let _ = opt.as_ref().map(|x| x.deref());
 +    let _ = opt.clone().as_mut().map(|x| x.deref_mut()).map(|x| x.len());
 +
 +    let vc = vec![String::new()];
 +    let _ = Some(1_usize).as_ref().map(|x| vc[*x].as_str()); // should not be linted
 +
 +    let _: Option<&str> = Some(&String::new()).as_ref().map(|x| x.as_str()); // should not be linted
 +
 +    let _ = opt.as_ref().map(|x| &**x);
 +    let _ = opt.as_mut().map(|x| &mut **x);
 +
 +    // Issue #5927
 +    let _ = opt.as_ref().map(std::ops::Deref::deref);
 +}
 +
++#[clippy::msrv = "1.39"]
 +fn msrv_1_39() {
-     #![clippy::msrv = "1.40"]
 +    let opt = Some(String::from("123"));
 +    let _ = opt.as_ref().map(String::as_str);
 +}
 +
++#[clippy::msrv = "1.40"]
 +fn msrv_1_40() {
 +    let opt = Some(String::from("123"));
 +    let _ = opt.as_ref().map(String::as_str);
 +}
index 7de8b3b6ba4355c55d3fb0a7712f7e43253e1596,0000000000000000000000000000000000000000..e471b56eea8170ef94c98de551e0c41e4d13736f
mode 100644,000000..100644
--- /dev/null
@@@ -1,116 -1,0 +1,116 @@@
-   --> $DIR/option_as_ref_deref.rs:14:13
 +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:13:13
 +   |
 +LL |     let _ = opt.clone().as_ref().map(Deref::deref).map(str::len);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.clone().as_deref()`
 +   |
 +   = 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:23:13
++  --> $DIR/option_as_ref_deref.rs:16:13
 +   |
 +LL |       let _ = opt.clone()
 +   |  _____________^
 +LL | |         .as_ref().map(
 +LL | |             Deref::deref
 +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:25: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:26: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:27: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:28: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:29: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:30: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:31: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:32: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:33: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:35: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:36: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:43: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:44: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:47: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:61: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:58:13
 +   |
 +LL |     let _ = opt.as_ref().map(String::as_str);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try using as_deref instead: `opt.as_deref()`
 +
 +error: aborting due to 18 previous errors
 +
index bea6be66a8e025ef2f4468b5bb41e556c4c4614c,0000000000000000000000000000000000000000..df36a9b842bf7f9f57066dc0c743393c397ce49a
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,64 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::ptr_as_ptr)]
-     #![clippy::msrv = "1.37"]
 +
 +extern crate macro_rules;
 +
 +macro_rules! cast_it {
 +    ($ptr: ident) => {
 +        $ptr.cast::<i32>()
 +    };
 +}
 +
 +fn main() {
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr.cast::<i32>();
 +    let _ = mut_ptr.cast::<i32>();
 +
 +    // Make sure the lint can handle the difference in their operator precedences.
 +    unsafe {
 +        let ptr_ptr: *const *const u32 = &ptr;
 +        let _ = (*ptr_ptr).cast::<i32>();
 +    }
 +
 +    // Changes in mutability. Do not lint this.
 +    let _ = ptr as *mut i32;
 +    let _ = mut_ptr as *const i32;
 +
 +    // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this.
 +    let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
 +    let _ = ptr_of_array as *const [u32];
 +    let _ = ptr_of_array as *const dyn std::fmt::Debug;
 +
 +    // Ensure the lint doesn't produce unnecessary turbofish for inferred types.
 +    let _: *const i32 = ptr.cast();
 +    let _: *mut i32 = mut_ptr.cast();
 +
 +    // Make sure the lint is triggered inside a macro
 +    let _ = cast_it!(ptr);
 +
 +    // Do not lint inside macros from external crates
 +    let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 +}
 +
++#[clippy::msrv = "1.37"]
 +fn _msrv_1_37() {
-     #![clippy::msrv = "1.38"]
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    // `pointer::cast` was stabilized in 1.38. Do not lint this
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +}
 +
++#[clippy::msrv = "1.38"]
 +fn _msrv_1_38() {
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr.cast::<i32>();
 +    let _ = mut_ptr.cast::<i32>();
 +}
index ca2616b0069a07003d5d99e6505f0c86922b9e4c,0000000000000000000000000000000000000000..302c66462d9b9e5ddd5f92f2d14ded1388cfe3ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,64 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::ptr_as_ptr)]
-     #![clippy::msrv = "1.37"]
 +
 +extern crate macro_rules;
 +
 +macro_rules! cast_it {
 +    ($ptr: ident) => {
 +        $ptr as *const i32
 +    };
 +}
 +
 +fn main() {
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +
 +    // Make sure the lint can handle the difference in their operator precedences.
 +    unsafe {
 +        let ptr_ptr: *const *const u32 = &ptr;
 +        let _ = *ptr_ptr as *const i32;
 +    }
 +
 +    // Changes in mutability. Do not lint this.
 +    let _ = ptr as *mut i32;
 +    let _ = mut_ptr as *const i32;
 +
 +    // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this.
 +    let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
 +    let _ = ptr_of_array as *const [u32];
 +    let _ = ptr_of_array as *const dyn std::fmt::Debug;
 +
 +    // Ensure the lint doesn't produce unnecessary turbofish for inferred types.
 +    let _: *const i32 = ptr as *const _;
 +    let _: *mut i32 = mut_ptr as _;
 +
 +    // Make sure the lint is triggered inside a macro
 +    let _ = cast_it!(ptr);
 +
 +    // Do not lint inside macros from external crates
 +    let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 +}
 +
++#[clippy::msrv = "1.37"]
 +fn _msrv_1_37() {
-     #![clippy::msrv = "1.38"]
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    // `pointer::cast` was stabilized in 1.38. Do not lint this
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +}
 +
++#[clippy::msrv = "1.38"]
 +fn _msrv_1_38() {
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +}
index c58c55cfd83a15c5ac671f19f37ff4f64be212a8,0000000000000000000000000000000000000000..a68e1cab6d357ecc6a2296287d643343bad2e42c
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,57 @@@
-   --> $DIR/ptr_as_ptr.rs:19:13
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:20:13
++  --> $DIR/ptr_as_ptr.rs:18:13
 +   |
 +LL |     let _ = ptr as *const i32;
 +   |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<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:25:17
++  --> $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:38:25
++  --> $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:39:23
++  --> $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:11:9
++  --> $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:63:13
++  --> $DIR/ptr_as_ptr.rs:10:9
 +   |
 +LL |         $ptr as *const i32
 +   |         ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
 +...
 +LL |     let _ = cast_it!(ptr);
 +   |             ------------- in this macro invocation
 +   |
 +   = 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:64: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:63:13
 +   |
 +LL |     let _ = mut_ptr as *mut i32;
 +   |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 +
 +error: aborting due to 8 previous errors
 +
index 824f00cb99e85159987586450fc593ac93d97792,0000000000000000000000000000000000000000..4923731fe45e8f8889f8e90ad5441e3ce81e0f4d
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,77 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.34"]
 +#![warn(clippy::manual_range_contains)]
 +#![allow(unused)]
 +#![allow(clippy::no_effect)]
 +#![allow(clippy::short_circuit_statement)]
 +#![allow(clippy::unnecessary_operation)]
 +
 +fn main() {
 +    let x = 9_i32;
 +
 +    // order shouldn't matter
 +    (8..12).contains(&x);
 +    (21..42).contains(&x);
 +    (1..100).contains(&x);
 +
 +    // also with inclusive ranges
 +    (9..=99).contains(&x);
 +    (1..=33).contains(&x);
 +    (1..=999).contains(&x);
 +
 +    // and the outside
 +    !(8..12).contains(&x);
 +    !(21..42).contains(&x);
 +    !(1..100).contains(&x);
 +
 +    // also with the outside of inclusive ranges
 +    !(9..=99).contains(&x);
 +    !(1..=33).contains(&x);
 +    !(1..=999).contains(&x);
 +
 +    // not a range.contains
 +    x > 8 && x < 12; // lower bound not inclusive
 +    x < 8 && x <= 12; // same direction
 +    x >= 12 && 12 >= x; // same bounds
 +    x < 8 && x > 12; // wrong direction
 +
 +    x <= 8 || x >= 12;
 +    x >= 8 || x >= 12;
 +    x < 12 || 12 < x;
 +    x >= 8 || x <= 12;
 +
 +    // Fix #6315
 +    let y = 3.;
 +    (0. ..1.).contains(&y);
 +    !(0. ..=1.).contains(&y);
 +
 +    // handle negatives #8721
 +    (-10..=10).contains(&x);
 +    x >= 10 && x <= -10;
 +    (-3. ..=3.).contains(&y);
 +    y >= 3. && y <= -3.;
 +
 +    // Fix #8745
 +    let z = 42;
 +    (0..=10).contains(&x) && (0..=10).contains(&z);
 +    !(0..10).contains(&x) || !(0..10).contains(&z);
 +    // Make sure operators in parens don't give a breaking suggestion
 +    ((x % 2 == 0) || (x < 0)) || (x >= 10);
 +}
 +
 +// Fix #6373
 +pub const fn in_range(a: i32) -> bool {
 +    3 <= a && a <= 20
 +}
 +
++#[clippy::msrv = "1.34"]
 +fn msrv_1_34() {
-     #![clippy::msrv = "1.35"]
 +    let x = 5;
 +    x >= 8 && x < 34;
 +}
 +
++#[clippy::msrv = "1.35"]
 +fn msrv_1_35() {
 +    let x = 5;
 +    (8..35).contains(&x);
 +}
index df925eeadfe5e0762f712412b29cf938de30ca08,0000000000000000000000000000000000000000..d623ccb5da636fe71874883d0f4b9ed5f6147f08
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,77 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.34"]
 +#![warn(clippy::manual_range_contains)]
 +#![allow(unused)]
 +#![allow(clippy::no_effect)]
 +#![allow(clippy::short_circuit_statement)]
 +#![allow(clippy::unnecessary_operation)]
 +
 +fn main() {
 +    let x = 9_i32;
 +
 +    // order shouldn't matter
 +    x >= 8 && x < 12;
 +    x < 42 && x >= 21;
 +    100 > x && 1 <= x;
 +
 +    // also with inclusive ranges
 +    x >= 9 && x <= 99;
 +    x <= 33 && x >= 1;
 +    999 >= x && 1 <= x;
 +
 +    // and the outside
 +    x < 8 || x >= 12;
 +    x >= 42 || x < 21;
 +    100 <= x || 1 > x;
 +
 +    // also with the outside of inclusive ranges
 +    x < 9 || x > 99;
 +    x > 33 || x < 1;
 +    999 < x || 1 > x;
 +
 +    // not a range.contains
 +    x > 8 && x < 12; // lower bound not inclusive
 +    x < 8 && x <= 12; // same direction
 +    x >= 12 && 12 >= x; // same bounds
 +    x < 8 && x > 12; // wrong direction
 +
 +    x <= 8 || x >= 12;
 +    x >= 8 || x >= 12;
 +    x < 12 || 12 < x;
 +    x >= 8 || x <= 12;
 +
 +    // Fix #6315
 +    let y = 3.;
 +    y >= 0. && y < 1.;
 +    y < 0. || y > 1.;
 +
 +    // handle negatives #8721
 +    x >= -10 && x <= 10;
 +    x >= 10 && x <= -10;
 +    y >= -3. && y <= 3.;
 +    y >= 3. && y <= -3.;
 +
 +    // Fix #8745
 +    let z = 42;
 +    (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
 +    (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
 +    // Make sure operators in parens don't give a breaking suggestion
 +    ((x % 2 == 0) || (x < 0)) || (x >= 10);
 +}
 +
 +// Fix #6373
 +pub const fn in_range(a: i32) -> bool {
 +    3 <= a && a <= 20
 +}
 +
++#[clippy::msrv = "1.34"]
 +fn msrv_1_34() {
-     #![clippy::msrv = "1.35"]
 +    let x = 5;
 +    x >= 8 && x < 34;
 +}
 +
++#[clippy::msrv = "1.35"]
 +fn msrv_1_35() {
 +    let x = 5;
 +    x >= 8 && x < 35;
 +}
index 9689e665b05c582ab061dc0734af45b461e0d059,0000000000000000000000000000000000000000..ea34023a46645f14322dbf063b50e9139cd9c430
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,130 @@@
-   --> $DIR/range_contains.rs:14:5
 +error: manual `Range::contains` implementation
-   --> $DIR/range_contains.rs:15:5
++  --> $DIR/range_contains.rs:13:5
 +   |
 +LL |     x >= 8 && x < 12;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
 +   |
 +   = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 +
 +error: manual `Range::contains` implementation
-   --> $DIR/range_contains.rs:16: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:19: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:20: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:21: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:24: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:25: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:26: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:29: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:30: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:31: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:46: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:47: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:50: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:52: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:57:30
++  --> $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:5
++  --> $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:58:29
++  --> $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:5
++  --> $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:79: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:76:5
 +   |
 +LL |     x >= 8 && x < 35;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)`
 +
 +error: aborting due to 21 previous errors
 +
index 7cd687c95a003f14c770fed009c8c4062359156b,0000000000000000000000000000000000000000..c0e49ff4caa7472f09bc38ce817b046f1cdb10a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,40 @@@
 +// run-rustfix
 +
 +#![feature(async_closure)]
 +#![warn(clippy::redundant_closure_call)]
 +#![allow(unused)]
 +
 +async fn something() -> u32 {
 +    21
 +}
 +
 +async fn something_else() -> u32 {
 +    2
 +}
 +
 +fn main() {
 +    let a = 42;
 +    let b = async {
 +        let x = something().await;
 +        let y = something_else().await;
 +        x * y
 +    };
 +    let c = {
 +        let x = 21;
 +        let y = 2;
 +        x * y
 +    };
 +    let d = async { something().await };
++
++    macro_rules! m {
++        () => {
++            0
++        };
++    }
++    macro_rules! m2 {
++        () => {
++            m!()
++        };
++    }
++    m2!();
 +}
index 37e4d2238641576fbff1184315d1264d2741f205,0000000000000000000000000000000000000000..9e6e54348a8c28f36f615a0444e86c59ef94b1f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,40 @@@
 +// run-rustfix
 +
 +#![feature(async_closure)]
 +#![warn(clippy::redundant_closure_call)]
 +#![allow(unused)]
 +
 +async fn something() -> u32 {
 +    21
 +}
 +
 +async fn something_else() -> u32 {
 +    2
 +}
 +
 +fn main() {
 +    let a = (|| 42)();
 +    let b = (async || {
 +        let x = something().await;
 +        let y = something_else().await;
 +        x * y
 +    })();
 +    let c = (|| {
 +        let x = 21;
 +        let y = 2;
 +        x * y
 +    })();
 +    let d = (async || something().await)();
++
++    macro_rules! m {
++        () => {
++            (|| 0)()
++        };
++    }
++    macro_rules! m2 {
++        () => {
++            (|| m!())()
++        };
++    }
++    m2!();
 +}
index 56a8e57c0c362097b74fb1b000b1438a1c62b097,0000000000000000000000000000000000000000..d71bcba2a8200414b0b74b98bb6c60a588f0e649
mode 100644,000000..100644
--- /dev/null
@@@ -1,56 -1,0 +1,78 @@@
- 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:16:13
 +   |
 +LL |     let a = (|| 42)();
 +   |             ^^^^^^^^^ help: try doing something like: `42`
 +   |
 +   = note: `-D clippy::redundant-closure-call` implied by `-D warnings`
 +
 +error: try not to call a closure in the expression where it is declared
 +  --> $DIR/redundant_closure_call_fixable.rs:17:13
 +   |
 +LL |       let b = (async || {
 +   |  _____________^
 +LL | |         let x = something().await;
 +LL | |         let y = something_else().await;
 +LL | |         x * y
 +LL | |     })();
 +   | |________^
 +   |
 +help: try doing something like
 +   |
 +LL ~     let b = async {
 +LL +         let x = something().await;
 +LL +         let y = something_else().await;
 +LL +         x * y
 +LL ~     };
 +   |
 +
 +error: try not to call a closure in the expression where it is declared
 +  --> $DIR/redundant_closure_call_fixable.rs:22:13
 +   |
 +LL |       let c = (|| {
 +   |  _____________^
 +LL | |         let x = 21;
 +LL | |         let y = 2;
 +LL | |         x * y
 +LL | |     })();
 +   | |________^
 +   |
 +help: try doing something like
 +   |
 +LL ~     let c = {
 +LL +         let x = 21;
 +LL +         let y = 2;
 +LL +         x * y
 +LL ~     };
 +   |
 +
 +error: try not to call a closure in the expression where it is declared
 +  --> $DIR/redundant_closure_call_fixable.rs:27:13
 +   |
 +LL |     let d = (async || something().await)();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `async { something().await }`
 +
++error: try not to call a closure in the expression where it is declared
++  --> $DIR/redundant_closure_call_fixable.rs:36:13
++   |
++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
 +
index 34ab552cb1d8d6aee22db40c07818a11eb42f8db,0000000000000000000000000000000000000000..ec7f8ae923a793b3241715b68653b80f98180be6
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,84 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.16"]
 +#![warn(clippy::redundant_field_names)]
 +#![allow(clippy::no_effect, dead_code, unused_variables)]
 +
 +#[macro_use]
 +extern crate derive_new;
 +
 +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
 +
 +mod foo {
 +    pub const BAR: u8 = 0;
 +}
 +
 +struct Person {
 +    gender: u8,
 +    age: u8,
 +    name: u8,
 +    buzz: u64,
 +    foo: u8,
 +}
 +
 +#[derive(new)]
 +pub struct S {
 +    v: String,
 +}
 +
 +fn main() {
 +    let gender: u8 = 42;
 +    let age = 0;
 +    let fizz: u64 = 0;
 +    let name: u8 = 0;
 +
 +    let me = Person {
 +        gender,
 +        age,
 +
 +        name,          //should be ok
 +        buzz: fizz,    //should be ok
 +        foo: foo::BAR, //should be ok
 +    };
 +
 +    // Range expressions
 +    let (start, end) = (0, 0);
 +
 +    let _ = start..;
 +    let _ = ..end;
 +    let _ = start..end;
 +
 +    let _ = ..=end;
 +    let _ = start..=end;
 +
 +    // Issue #2799
 +    let _: Vec<_> = (start..end).collect();
 +
 +    // hand-written Range family structs are linted
 +    let _ = RangeFrom { start };
 +    let _ = RangeTo { end };
 +    let _ = Range { start, end };
 +    let _ = RangeInclusive::new(start, end);
 +    let _ = RangeToInclusive { end };
 +}
 +
 +fn issue_3476() {
 +    fn foo<T>() {}
 +
 +    struct S {
 +        foo: fn(),
 +    }
 +
 +    S { foo: foo::<i32> };
 +}
 +
++#[clippy::msrv = "1.16"]
 +fn msrv_1_16() {
-     #![clippy::msrv = "1.17"]
 +    let start = 0;
 +    let _ = RangeFrom { start: start };
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn msrv_1_17() {
 +    let start = 0;
 +    let _ = RangeFrom { start };
 +}
index a051b1f96f0fda8dad22fe3fba5f867d0249263a,0000000000000000000000000000000000000000..73122016cf69e9eefec777cde172735926f503ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,84 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.16"]
 +#![warn(clippy::redundant_field_names)]
 +#![allow(clippy::no_effect, dead_code, unused_variables)]
 +
 +#[macro_use]
 +extern crate derive_new;
 +
 +use std::ops::{Range, RangeFrom, RangeInclusive, RangeTo, RangeToInclusive};
 +
 +mod foo {
 +    pub const BAR: u8 = 0;
 +}
 +
 +struct Person {
 +    gender: u8,
 +    age: u8,
 +    name: u8,
 +    buzz: u64,
 +    foo: u8,
 +}
 +
 +#[derive(new)]
 +pub struct S {
 +    v: String,
 +}
 +
 +fn main() {
 +    let gender: u8 = 42;
 +    let age = 0;
 +    let fizz: u64 = 0;
 +    let name: u8 = 0;
 +
 +    let me = Person {
 +        gender: gender,
 +        age: age,
 +
 +        name,          //should be ok
 +        buzz: fizz,    //should be ok
 +        foo: foo::BAR, //should be ok
 +    };
 +
 +    // Range expressions
 +    let (start, end) = (0, 0);
 +
 +    let _ = start..;
 +    let _ = ..end;
 +    let _ = start..end;
 +
 +    let _ = ..=end;
 +    let _ = start..=end;
 +
 +    // Issue #2799
 +    let _: Vec<_> = (start..end).collect();
 +
 +    // hand-written Range family structs are linted
 +    let _ = RangeFrom { start: start };
 +    let _ = RangeTo { end: end };
 +    let _ = Range { start: start, end: end };
 +    let _ = RangeInclusive::new(start, end);
 +    let _ = RangeToInclusive { end: end };
 +}
 +
 +fn issue_3476() {
 +    fn foo<T>() {}
 +
 +    struct S {
 +        foo: fn(),
 +    }
 +
 +    S { foo: foo::<i32> };
 +}
 +
++#[clippy::msrv = "1.16"]
 +fn msrv_1_16() {
-     #![clippy::msrv = "1.17"]
 +    let start = 0;
 +    let _ = RangeFrom { start: start };
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn msrv_1_17() {
 +    let start = 0;
 +    let _ = RangeFrom { start: start };
 +}
index 8b82e062b93a63325c94f5e4c79b5f9592f63f5c,0000000000000000000000000000000000000000..00a72c50cf7d0324cc3600fc069cc333d9158594
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
-   --> $DIR/redundant_field_names.rs:36:9
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:37:9
++  --> $DIR/redundant_field_names.rs:35:9
 +   |
 +LL |         gender: gender,
 +   |         ^^^^^^^^^^^^^^ help: replace it with: `gender`
 +   |
 +   = note: `-D clippy::redundant-field-names` implied by `-D warnings`
 +
 +error: redundant field names in struct initialization
-   --> $DIR/redundant_field_names.rs:58:25
++  --> $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:59:23
++  --> $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:60:21
++  --> $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:35
++  --> $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:62:32
++  --> $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:86:25
++  --> $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:83:25
 +   |
 +LL |     let _ = RangeFrom { start: start };
 +   |                         ^^^^^^^^^^^^ help: replace it with: `start`
 +
 +error: aborting due to 8 previous errors
 +
index 42110dbe81e84f8d35f678b7613be06f4b2f9a53,0000000000000000000000000000000000000000..4c5846fe837eaa8a68ed10ad585271923f0f6588
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,66 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.16"]
 +#![allow(unused)]
 +
 +#[derive(Debug)]
 +struct Foo;
 +
 +const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
 +
 +const VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +const VAR_FOUR: (&str, (&str, &str), &str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
 +
 +const VAR_SIX: &u8 = &5;
 +
 +const VAR_HEIGHT: &Foo = &Foo {};
 +
 +const VAR_SLICE: &[u8] = b"Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +const VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ONE: &str = "Test static #1"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
 +
 +static STATIC_VAR_THREE: &[&str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +static STATIC_VAR_SIX: &u8 = &5;
 +
 +static STATIC_VAR_HEIGHT: &Foo = &Foo {};
 +
 +static STATIC_VAR_SLICE: &[u8] = b"Test static #3"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TUPLE: &(u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ARRAY: &[u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +fn main() {
 +    let false_positive: &'static str = "test";
 +}
 +
 +trait Bar {
 +    const TRAIT_VAR: &'static str;
 +}
 +
 +impl Foo {
 +    const IMPL_VAR: &'static str = "var";
 +}
 +
 +impl Bar for Foo {
 +    const TRAIT_VAR: &'static str = "foo";
 +}
 +
++#[clippy::msrv = "1.16"]
 +fn msrv_1_16() {
-     #![clippy::msrv = "1.17"]
 +    static V: &'static u8 = &16;
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn msrv_1_17() {
 +    static V: &u8 = &17;
 +}
index bc5200bc8625b0a200e05035f2582f3dd2eab21d,0000000000000000000000000000000000000000..64a66be1a83c753ff8a786167759fd4f04ea52ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,66 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.16"]
 +#![allow(unused)]
 +
 +#[derive(Debug)]
 +struct Foo;
 +
 +const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TWO: &str = "Test constant #2"; // This line should not raise a warning.
 +
 +const VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +const VAR_FOUR: (&str, (&str, &'static str), &'static str) = ("on", ("th", "th"), "on"); // ERROR Consider removing 'static
 +
 +const VAR_SIX: &'static u8 = &5;
 +
 +const VAR_HEIGHT: &'static Foo = &Foo {};
 +
 +const VAR_SLICE: &'static [u8] = b"Test constant #1"; // ERROR Consider removing 'static.
 +
 +const VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +const VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ONE: &'static str = "Test static #1"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TWO: &str = "Test static #2"; // This line should not raise a warning.
 +
 +static STATIC_VAR_THREE: &[&'static str] = &["one", "two"]; // ERROR Consider removing 'static
 +
 +static STATIC_VAR_SIX: &'static u8 = &5;
 +
 +static STATIC_VAR_HEIGHT: &'static Foo = &Foo {};
 +
 +static STATIC_VAR_SLICE: &'static [u8] = b"Test static #3"; // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_TUPLE: &'static (u8, u8) = &(1, 2); // ERROR Consider removing 'static.
 +
 +static STATIC_VAR_ARRAY: &'static [u8; 1] = b"T"; // ERROR Consider removing 'static.
 +
 +fn main() {
 +    let false_positive: &'static str = "test";
 +}
 +
 +trait Bar {
 +    const TRAIT_VAR: &'static str;
 +}
 +
 +impl Foo {
 +    const IMPL_VAR: &'static str = "var";
 +}
 +
 +impl Bar for Foo {
 +    const TRAIT_VAR: &'static str = "foo";
 +}
 +
++#[clippy::msrv = "1.16"]
 +fn msrv_1_16() {
-     #![clippy::msrv = "1.17"]
 +    static V: &'static u8 = &16;
 +}
 +
++#[clippy::msrv = "1.17"]
 +fn msrv_1_17() {
 +    static V: &'static u8 = &17;
 +}
index 735113460d28caf9c67ea003d651fe7cbf9fbafc,0000000000000000000000000000000000000000..0938ebf783ff16888852e0be386dc22c86f1c254
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,106 @@@
-   --> $DIR/redundant_static_lifetimes.rs:9:17
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:13:21
++  --> $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`
 +   |
 +   = note: `-D clippy::redundant-static-lifetimes` implied by `-D warnings`
 +
 +error: constants have by default a `'static` lifetime
-   --> $DIR/redundant_static_lifetimes.rs:15:32
++  --> $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:47
++  --> $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:17:17
++  --> $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:19:20
++  --> $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:21:19
++  --> $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:23: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:25: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:27:25
++  --> $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:31:29
++  --> $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:33:25
++  --> $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:35:28
++  --> $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:37:27
++  --> $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:39: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:41: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:68:16
++  --> $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:65:16
 +   |
 +LL |     static V: &'static u8 = &17;
 +   |               -^^^^^^^--- help: consider removing `'static`: `&u8`
 +
 +error: aborting due to 17 previous errors
 +
index 9dd27d6dc01aaf8ae40245fc024c49b0e35cea14,0000000000000000000000000000000000000000..1c12cebfd971a372480593586f63c009d5940978
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,117 @@@
 +#![warn(clippy::result_large_err)]
 +#![allow(clippy::large_enum_variant)]
 +
 +pub fn small_err() -> Result<(), u128> {
 +    Ok(())
 +}
 +
 +pub fn large_err() -> Result<(), [u8; 512]> {
 +    Ok(())
 +}
 +
 +pub struct FullyDefinedLargeError {
 +    _foo: u128,
 +    _bar: [u8; 100],
 +    _foobar: [u8; 120],
 +}
 +
 +impl FullyDefinedLargeError {
 +    pub fn ret() -> Result<(), Self> {
 +        Ok(())
 +    }
 +}
 +
 +pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
 +    Ok(())
 +}
 +
 +type Fdlr<T> = std::result::Result<T, FullyDefinedLargeError>;
 +pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
 +    Ok(x)
 +}
 +
 +pub fn param_small_error<R>() -> Result<(), (R, u128)> {
 +    Ok(())
 +}
 +
 +pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
 +    Ok(())
 +}
 +
 +pub enum LargeErrorVariants<T> {
 +    _Small(u8),
 +    _Omg([u8; 512]),
 +    _Param(T),
 +}
 +
 +impl LargeErrorVariants<()> {
 +    pub fn large_enum_error() -> Result<(), Self> {
 +        Ok(())
 +    }
 +}
 +
 +enum MultipleLargeVariants {
 +    _Biggest([u8; 1024]),
 +    _AlsoBig([u8; 512]),
 +    _Ok(usize),
 +}
 +
 +impl MultipleLargeVariants {
 +    fn large_enum_error() -> Result<(), Self> {
 +        Ok(())
 +    }
 +}
 +
 +trait TraitForcesLargeError {
 +    fn large_error() -> Result<(), [u8; 512]> {
 +        Ok(())
 +    }
 +}
 +
 +struct TraitImpl;
 +
 +impl TraitForcesLargeError for TraitImpl {
 +    // Should not lint
 +    fn large_error() -> Result<(), [u8; 512]> {
 +        Ok(())
 +    }
 +}
 +
 +pub union FullyDefinedUnionError {
 +    _maybe: u8,
 +    _or_even: [[u8; 16]; 32],
 +}
 +
 +pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
 +    Ok(())
 +}
 +
 +pub union UnionError<T: Copy> {
 +    _maybe: T,
 +    _or_perhaps_even: (T, [u8; 512]),
 +}
 +
 +pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
 +    Ok(())
 +}
 +
 +pub struct ArrayError<T, U> {
 +    _large_array: [T; 32],
 +    _other_stuff: U,
 +}
 +
 +pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
 +    Ok(())
 +}
 +
 +pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
 +    Ok(())
 +}
 +
++// Issue #10005
++enum Empty {}
++fn _empty_error() -> Result<(), Empty> {
++    Ok(())
++}
++
 +fn main() {}
index 4b5303324bc6f17e7dde1e1dc477c1eac2b3c357,0000000000000000000000000000000000000000..1309c91b81c9830b52f9813d1bf0d75b572251e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,25 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +#![warn(clippy::seek_from_current)]
-     #![clippy::msrv = "1.50"]
 +
 +use std::fs::File;
 +use std::io::{self, Seek, SeekFrom, Write};
 +
++#[clippy::msrv = "1.50"]
 +fn _msrv_1_50() -> io::Result<()> {
-     #![clippy::msrv = "1.51"]
 +    let mut f = File::create("foo.txt")?;
 +    f.write_all(b"Hi!")?;
 +    f.seek(SeekFrom::Current(0))?;
 +    f.seek(SeekFrom::Current(1))?;
 +    Ok(())
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn _msrv_1_51() -> io::Result<()> {
 +    let mut f = File::create("foo.txt")?;
 +    f.write_all(b"Hi!")?;
 +    f.stream_position()?;
 +    f.seek(SeekFrom::Current(1))?;
 +    Ok(())
 +}
 +
 +fn main() {}
index f93639261a1808241f7e24dfc1957c4c71c1907e,0000000000000000000000000000000000000000..5d9b1424cf686fe5bd1ec150b86c7e8d6a7f0d3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,25 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +#![warn(clippy::seek_from_current)]
-     #![clippy::msrv = "1.50"]
 +
 +use std::fs::File;
 +use std::io::{self, Seek, SeekFrom, Write};
 +
++#[clippy::msrv = "1.50"]
 +fn _msrv_1_50() -> io::Result<()> {
-     #![clippy::msrv = "1.51"]
 +    let mut f = File::create("foo.txt")?;
 +    f.write_all(b"Hi!")?;
 +    f.seek(SeekFrom::Current(0))?;
 +    f.seek(SeekFrom::Current(1))?;
 +    Ok(())
 +}
 +
++#[clippy::msrv = "1.51"]
 +fn _msrv_1_51() -> io::Result<()> {
 +    let mut f = File::create("foo.txt")?;
 +    f.write_all(b"Hi!")?;
 +    f.seek(SeekFrom::Current(0))?;
 +    f.seek(SeekFrom::Current(1))?;
 +    Ok(())
 +}
 +
 +fn main() {}
index db1125b53cdf5c2182970a9c748633f0d9c5840d,0000000000000000000000000000000000000000..c079f361192928bcea5fe74a99da5e93cc521309
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
-   --> $DIR/seek_from_current.rs:21:5
 +error: using `SeekFrom::Current` to start from current position
++  --> $DIR/seek_from_current.rs:20:5
 +   |
 +LL |     f.seek(SeekFrom::Current(0))?;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()`
 +   |
 +   = note: `-D clippy::seek-from-current` implied by `-D warnings`
 +
 +error: aborting due to previous error
 +
index 464b6cdef63933acde79d463929e163049252bd8,0000000000000000000000000000000000000000..9d0d1124c460ed45b1b5894bd3878b18d15def72
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,134 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +#![allow(unused)]
-     #![clippy::msrv = "1.54"]
 +#![warn(clippy::seek_to_start_instead_of_rewind)]
 +
 +use std::fs::OpenOptions;
 +use std::io::{Read, Seek, SeekFrom, Write};
 +
 +struct StructWithSeekMethod {}
 +
 +impl StructWithSeekMethod {
 +    fn seek(&mut self, from: SeekFrom) {}
 +}
 +
 +trait MySeekTrait {
 +    fn seek(&mut self, from: SeekFrom) {}
 +}
 +
 +struct StructWithSeekTrait {}
 +impl MySeekTrait for StructWithSeekTrait {}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_method(t: &mut StructWithSeekMethod) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_trait_bound<T: MySeekTrait>(t: &mut T) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should trigger clippy warning
 +fn seek_to_start<T: Seek>(t: &mut T) {
 +    t.rewind();
 +}
 +
 +// This should trigger clippy warning
 +fn owned_seek_to_start<T: Seek>(mut t: T) {
 +    t.rewind();
 +}
 +
 +// This should NOT trigger clippy warning because
 +// it does not seek to start
 +fn seek_to_5<T: Seek>(t: &mut T) {
 +    t.seek(SeekFrom::Start(5));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// it does not seek to start
 +fn seek_to_end<T: Seek>(t: &mut T) {
 +    t.seek(SeekFrom::End(0));
 +}
 +
 +fn main() {
 +    let mut f = OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .create(true)
 +        .open("foo.txt")
 +        .unwrap();
 +
 +    let mut my_struct_trait = StructWithSeekTrait {};
 +    seek_to_start_false_trait_bound(&mut my_struct_trait);
 +
 +    let hello = "Hello!\n";
 +    write!(f, "{hello}").unwrap();
 +    seek_to_5(&mut f);
 +    seek_to_end(&mut f);
 +    seek_to_start(&mut f);
 +
 +    let mut buf = String::new();
 +    f.read_to_string(&mut buf).unwrap();
 +
 +    assert_eq!(&buf, hello);
 +}
 +
++#[clippy::msrv = "1.54"]
 +fn msrv_1_54() {
-     #![clippy::msrv = "1.55"]
 +    let mut f = OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .create(true)
 +        .open("foo.txt")
 +        .unwrap();
 +
 +    let hello = "Hello!\n";
 +    write!(f, "{hello}").unwrap();
 +
 +    f.seek(SeekFrom::Start(0));
 +
 +    let mut buf = String::new();
 +    f.read_to_string(&mut buf).unwrap();
 +
 +    assert_eq!(&buf, hello);
 +}
 +
++#[clippy::msrv = "1.55"]
 +fn msrv_1_55() {
 +    let mut f = OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .create(true)
 +        .open("foo.txt")
 +        .unwrap();
 +
 +    let hello = "Hello!\n";
 +    write!(f, "{hello}").unwrap();
 +
 +    f.rewind();
 +
 +    let mut buf = String::new();
 +    f.read_to_string(&mut buf).unwrap();
 +
 +    assert_eq!(&buf, hello);
 +}
index 68e09bd7c1f0fd4d4d210e90b58e50b04c1a1d40,0000000000000000000000000000000000000000..c5bc57cc3a74ce617a96b4dd401a58cca895647b
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,134 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +#![allow(unused)]
-     #![clippy::msrv = "1.54"]
 +#![warn(clippy::seek_to_start_instead_of_rewind)]
 +
 +use std::fs::OpenOptions;
 +use std::io::{Read, Seek, SeekFrom, Write};
 +
 +struct StructWithSeekMethod {}
 +
 +impl StructWithSeekMethod {
 +    fn seek(&mut self, from: SeekFrom) {}
 +}
 +
 +trait MySeekTrait {
 +    fn seek(&mut self, from: SeekFrom) {}
 +}
 +
 +struct StructWithSeekTrait {}
 +impl MySeekTrait for StructWithSeekTrait {}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_method(t: &mut StructWithSeekMethod) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_method_owned_false<T>(mut t: StructWithSeekMethod) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_trait(t: &mut StructWithSeekTrait) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_trait_owned<T>(mut t: StructWithSeekTrait) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// StructWithSeekMethod does not implement std::io::Seek;
 +fn seek_to_start_false_trait_bound<T: MySeekTrait>(t: &mut T) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should trigger clippy warning
 +fn seek_to_start<T: Seek>(t: &mut T) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should trigger clippy warning
 +fn owned_seek_to_start<T: Seek>(mut t: T) {
 +    t.seek(SeekFrom::Start(0));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// it does not seek to start
 +fn seek_to_5<T: Seek>(t: &mut T) {
 +    t.seek(SeekFrom::Start(5));
 +}
 +
 +// This should NOT trigger clippy warning because
 +// it does not seek to start
 +fn seek_to_end<T: Seek>(t: &mut T) {
 +    t.seek(SeekFrom::End(0));
 +}
 +
 +fn main() {
 +    let mut f = OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .create(true)
 +        .open("foo.txt")
 +        .unwrap();
 +
 +    let mut my_struct_trait = StructWithSeekTrait {};
 +    seek_to_start_false_trait_bound(&mut my_struct_trait);
 +
 +    let hello = "Hello!\n";
 +    write!(f, "{hello}").unwrap();
 +    seek_to_5(&mut f);
 +    seek_to_end(&mut f);
 +    seek_to_start(&mut f);
 +
 +    let mut buf = String::new();
 +    f.read_to_string(&mut buf).unwrap();
 +
 +    assert_eq!(&buf, hello);
 +}
 +
++#[clippy::msrv = "1.54"]
 +fn msrv_1_54() {
-     #![clippy::msrv = "1.55"]
 +    let mut f = OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .create(true)
 +        .open("foo.txt")
 +        .unwrap();
 +
 +    let hello = "Hello!\n";
 +    write!(f, "{hello}").unwrap();
 +
 +    f.seek(SeekFrom::Start(0));
 +
 +    let mut buf = String::new();
 +    f.read_to_string(&mut buf).unwrap();
 +
 +    assert_eq!(&buf, hello);
 +}
 +
++#[clippy::msrv = "1.55"]
 +fn msrv_1_55() {
 +    let mut f = OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .create(true)
 +        .open("foo.txt")
 +        .unwrap();
 +
 +    let hello = "Hello!\n";
 +    write!(f, "{hello}").unwrap();
 +
 +    f.seek(SeekFrom::Start(0));
 +
 +    let mut buf = String::new();
 +    f.read_to_string(&mut buf).unwrap();
 +
 +    assert_eq!(&buf, hello);
 +}
index de0eec5d909cd72695def70bc63abee7eecea714,0000000000000000000000000000000000000000..6cce025359fe2918bce42ea6bdbef601d8c74948
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
-   --> $DIR/seek_to_start_instead_of_rewind.rs:54:7
 +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:53:7
 +   |
 +LL |     t.seek(SeekFrom::Start(0));
 +   |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
 +   |
 +   = 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:131: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:128:7
 +   |
 +LL |     f.seek(SeekFrom::Start(0));
 +   |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
 +
 +error: aborting due to 3 previous errors
 +
index e5fe9133f975eebc1e6d6ff3821cecd36ba5480a,0000000000000000000000000000000000000000..074dae5fb2868bf1906f523e3c9aa0b222427eb3
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,77 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.38"]
 +#![warn(clippy::transmute_ptr_to_ref)]
 +#![allow(clippy::match_single_binding)]
 +
 +unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
 +    let _: &T = &*p;
 +    let _: &T = &*p;
 +
 +    let _: &mut T = &mut *m;
 +    let _: &mut T = &mut *m;
 +
 +    let _: &T = &*m;
 +    let _: &T = &*m;
 +
 +    let _: &mut T = &mut *(p as *mut T);
 +    let _ = &mut *(p as *mut T);
 +
 +    let _: &T = &*(o as *const T);
 +    let _: &T = &*(o as *const T);
 +
 +    let _: &mut T = &mut *(om as *mut T);
 +    let _: &mut T = &mut *(om as *mut T);
 +
 +    let _: &T = &*(om as *const T);
 +    let _: &T = &*(om as *const T);
 +}
 +
 +fn _issue1231() {
 +    struct Foo<'a, T> {
 +        bar: &'a T,
 +    }
 +
 +    let raw = 42 as *const i32;
 +    let _: &Foo<u8> = unsafe { &*raw.cast::<Foo<_>>() };
 +
 +    let _: &Foo<&u8> = unsafe { &*raw.cast::<Foo<&_>>() };
 +
 +    type Bar<'a> = &'a u8;
 +    let raw = 42 as *const i32;
 +    unsafe { &*(raw as *const u8) };
 +}
 +
 +unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
 +    match 0 {
 +        0 => &*x.cast::<&u32>(),
 +        1 => &*y.cast::<&u32>(),
 +        2 => &*x.cast::<&'b u32>(),
 +        _ => &*y.cast::<&'b u32>(),
 +    }
 +}
 +
++#[clippy::msrv = "1.38"]
 +unsafe fn _meets_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;
 +    let _: &u32 = &*a.cast::<u32>();
 +    match 0 {
 +        0 => &*x.cast::<&u32>(),
 +        _ => &*x.cast::<&'b u32>(),
 +    }
 +}
 +
++#[clippy::msrv = "1.37"]
 +unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
 +    let a = 0u32;
 +    let a = &a as *const u32;
 +    let _: &u32 = &*a;
 +    let _: &u32 = &*(a as *const u32);
 +    match 0 {
 +        0 => &*(x as *const () as *const &u32),
 +        _ => &*(x as *const () as *const &'b u32),
 +    }
 +}
 +
 +fn main() {}
index fe49cdc324fdd397e3db32cf85f46338753ae702,0000000000000000000000000000000000000000..2edc122cf47127b1b8e364f3ce4409e72d209855
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,77 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.38"]
 +#![warn(clippy::transmute_ptr_to_ref)]
 +#![allow(clippy::match_single_binding)]
 +
 +unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
 +    let _: &T = std::mem::transmute(p);
 +    let _: &T = &*p;
 +
 +    let _: &mut T = std::mem::transmute(m);
 +    let _: &mut T = &mut *m;
 +
 +    let _: &T = std::mem::transmute(m);
 +    let _: &T = &*m;
 +
 +    let _: &mut T = std::mem::transmute(p as *mut T);
 +    let _ = &mut *(p as *mut T);
 +
 +    let _: &T = std::mem::transmute(o);
 +    let _: &T = &*(o as *const T);
 +
 +    let _: &mut T = std::mem::transmute(om);
 +    let _: &mut T = &mut *(om as *mut T);
 +
 +    let _: &T = std::mem::transmute(om);
 +    let _: &T = &*(om as *const T);
 +}
 +
 +fn _issue1231() {
 +    struct Foo<'a, T> {
 +        bar: &'a T,
 +    }
 +
 +    let raw = 42 as *const i32;
 +    let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
 +
 +    let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
 +
 +    type Bar<'a> = &'a u8;
 +    let raw = 42 as *const i32;
 +    unsafe { std::mem::transmute::<_, Bar>(raw) };
 +}
 +
 +unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
 +    match 0 {
 +        0 => std::mem::transmute(x),
 +        1 => std::mem::transmute(y),
 +        2 => std::mem::transmute::<_, &&'b u32>(x),
 +        _ => std::mem::transmute::<_, &&'b u32>(y),
 +    }
 +}
 +
++#[clippy::msrv = "1.38"]
 +unsafe fn _meets_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);
 +    let _: &u32 = std::mem::transmute::<_, &u32>(a);
 +    match 0 {
 +        0 => std::mem::transmute(x),
 +        _ => std::mem::transmute::<_, &&'b u32>(x),
 +    }
 +}
 +
++#[clippy::msrv = "1.37"]
 +unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
 +    let a = 0u32;
 +    let a = &a as *const u32;
 +    let _: &u32 = std::mem::transmute(a);
 +    let _: &u32 = std::mem::transmute::<_, &u32>(a);
 +    match 0 {
 +        0 => std::mem::transmute(x),
 +        _ => std::mem::transmute::<_, &&'b u32>(x),
 +    }
 +}
 +
 +fn main() {}
index 10117ee9182ab97d97e9bb13e5cee991a399980f,0000000000000000000000000000000000000000..b3e6c09d2d7a1f2882996aa36818067edec35586
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,136 @@@
-   --> $DIR/transmute_ptr_to_ref.rs:8:17
 +error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
-   --> $DIR/transmute_ptr_to_ref.rs:11:21
++  --> $DIR/transmute_ptr_to_ref.rs:7:17
 +   |
 +LL |     let _: &T = std::mem::transmute(p);
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*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:14:17
++  --> $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:17:21
++  --> $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:20:17
++  --> $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:23:21
++  --> $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:26:17
++  --> $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:36:32
++  --> $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:38:33
++  --> $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:42:14
++  --> $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:47: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:48: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:49: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:50: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:58:19
++  --> $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:59: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:61:14
++  --> $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:62: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:70:19
++  --> $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:71: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:73:14
++  --> $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:74: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:73:14
 +   |
 +LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
 +
 +error: aborting due to 22 previous errors
 +
index cbc6768033ec822bb7b4bfe6447579585a85000d,0000000000000000000000000000000000000000..c05eb447b2ebd34e50ef3db1197116eab83e7241
mode 100644,000000..100644
--- /dev/null
@@@ -1,512 -1,0 +1,512 @@@
- #![warn(clippy::undocumented_unsafe_blocks)]
 +// aux-build:proc_macro_unsafe.rs
 +
++#![warn(clippy::undocumented_unsafe_blocks, clippy::unnecessary_safety_comment)]
 +#![allow(clippy::let_unit_value, clippy::missing_safety_doc)]
 +
 +extern crate proc_macro_unsafe;
 +
 +// Valid comments
 +
 +fn nested_local() {
 +    let _ = {
 +        let _ = {
 +            // SAFETY:
 +            let _ = unsafe {};
 +        };
 +    };
 +}
 +
 +fn deep_nest() {
 +    let _ = {
 +        let _ = {
 +            // SAFETY:
 +            let _ = unsafe {};
 +
 +            // Safety:
 +            unsafe {};
 +
 +            let _ = {
 +                let _ = {
 +                    let _ = {
 +                        let _ = {
 +                            let _ = {
 +                                // Safety:
 +                                let _ = unsafe {};
 +
 +                                // SAFETY:
 +                                unsafe {};
 +                            };
 +                        };
 +                    };
 +
 +                    // Safety:
 +                    unsafe {};
 +                };
 +            };
 +        };
 +
 +        // Safety:
 +        unsafe {};
 +    };
 +
 +    // SAFETY:
 +    unsafe {};
 +}
 +
 +fn local_tuple_expression() {
 +    // Safety:
 +    let _ = (42, unsafe {});
 +}
 +
 +fn line_comment() {
 +    // Safety:
 +    unsafe {}
 +}
 +
 +fn line_comment_newlines() {
 +    // SAFETY:
 +
 +    unsafe {}
 +}
 +
 +fn line_comment_empty() {
 +    // Safety:
 +    //
 +    //
 +    //
 +    unsafe {}
 +}
 +
 +fn line_comment_with_extras() {
 +    // This is a description
 +    // Safety:
 +    unsafe {}
 +}
 +
 +fn block_comment() {
 +    /* Safety: */
 +    unsafe {}
 +}
 +
 +fn block_comment_newlines() {
 +    /* SAFETY: */
 +
 +    unsafe {}
 +}
 +
 +fn block_comment_with_extras() {
 +    /* This is a description
 +     * SAFETY:
 +     */
 +    unsafe {}
 +}
 +
 +fn block_comment_terminator_same_line() {
 +    /* This is a description
 +     * Safety: */
 +    unsafe {}
 +}
 +
 +fn buried_safety() {
 +    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
 +    // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
 +    // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
 +    // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
 +    // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
 +    // laborum. Safety:
 +    // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
 +    // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
 +    // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
 +    // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
 +    unsafe {}
 +}
 +
 +fn safety_with_prepended_text() {
 +    // This is a test. safety:
 +    unsafe {}
 +}
 +
 +fn local_line_comment() {
 +    // Safety:
 +    let _ = unsafe {};
 +}
 +
 +fn local_block_comment() {
 +    /* SAFETY: */
 +    let _ = unsafe {};
 +}
 +
 +fn comment_array() {
 +    // Safety:
 +    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +}
 +
 +fn comment_tuple() {
 +    // sAFETY:
 +    let _ = (42, unsafe {}, "test", unsafe {});
 +}
 +
 +fn comment_unary() {
 +    // SAFETY:
 +    let _ = *unsafe { &42 };
 +}
 +
 +#[allow(clippy::match_single_binding)]
 +fn comment_match() {
 +    // SAFETY:
 +    let _ = match unsafe {} {
 +        _ => {},
 +    };
 +}
 +
 +fn comment_addr_of() {
 +    // Safety:
 +    let _ = &unsafe {};
 +}
 +
 +fn comment_repeat() {
 +    // Safety:
 +    let _ = [unsafe {}; 5];
 +}
 +
 +fn comment_macro_call() {
 +    macro_rules! t {
 +        ($b:expr) => {
 +            $b
 +        };
 +    }
 +
 +    t!(
 +        // SAFETY:
 +        unsafe {}
 +    );
 +}
 +
 +fn comment_macro_def() {
 +    macro_rules! t {
 +        () => {
 +            // Safety:
 +            unsafe {}
 +        };
 +    }
 +
 +    t!();
 +}
 +
 +fn non_ascii_comment() {
 +    // ॐ᧻໒ SaFeTy: ௵∰
 +    unsafe {};
 +}
 +
 +fn local_commented_block() {
 +    let _ =
 +        // safety:
 +        unsafe {};
 +}
 +
 +fn local_nest() {
 +    // safety:
 +    let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
 +}
 +
 +fn in_fn_call(x: *const u32) {
 +    fn f(x: u32) {}
 +
 +    // Safety: reason
 +    f(unsafe { *x });
 +}
 +
 +fn multi_in_fn_call(x: *const u32) {
 +    fn f(x: u32, y: u32) {}
 +
 +    // Safety: reason
 +    f(unsafe { *x }, unsafe { *x });
 +}
 +
 +fn in_multiline_fn_call(x: *const u32) {
 +    fn f(x: u32, y: u32) {}
 +
 +    f(
 +        // Safety: reason
 +        unsafe { *x },
 +        0,
 +    );
 +}
 +
 +fn in_macro_call(x: *const u32) {
 +    // Safety: reason
 +    println!("{}", unsafe { *x });
 +}
 +
 +fn in_multiline_macro_call(x: *const u32) {
 +    println!(
 +        "{}",
 +        // Safety: reason
 +        unsafe { *x },
 +    );
 +}
 +
 +fn from_proc_macro() {
 +    proc_macro_unsafe::unsafe_block!(token);
 +}
 +
 +fn in_closure(x: *const u32) {
 +    // Safety: reason
 +    let _ = || unsafe { *x };
 +}
 +
 +// Invalid comments
 +
 +#[rustfmt::skip]
 +fn inline_block_comment() {
 +    /* Safety: */ unsafe {}
 +}
 +
 +fn no_comment() {
 +    unsafe {}
 +}
 +
 +fn no_comment_array() {
 +    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +}
 +
 +fn no_comment_tuple() {
 +    let _ = (42, unsafe {}, "test", unsafe {});
 +}
 +
 +fn no_comment_unary() {
 +    let _ = *unsafe { &42 };
 +}
 +
 +#[allow(clippy::match_single_binding)]
 +fn no_comment_match() {
 +    let _ = match unsafe {} {
 +        _ => {},
 +    };
 +}
 +
 +fn no_comment_addr_of() {
 +    let _ = &unsafe {};
 +}
 +
 +fn no_comment_repeat() {
 +    let _ = [unsafe {}; 5];
 +}
 +
 +fn local_no_comment() {
 +    let _ = unsafe {};
 +}
 +
 +fn no_comment_macro_call() {
 +    macro_rules! t {
 +        ($b:expr) => {
 +            $b
 +        };
 +    }
 +
 +    t!(unsafe {});
 +}
 +
 +fn no_comment_macro_def() {
 +    macro_rules! t {
 +        () => {
 +            unsafe {}
 +        };
 +    }
 +
 +    t!();
 +}
 +
 +fn trailing_comment() {
 +    unsafe {} // SAFETY:
 +}
 +
 +fn internal_comment() {
 +    unsafe {
 +        // SAFETY:
 +    }
 +}
 +
 +fn interference() {
 +    // SAFETY
 +
 +    let _ = 42;
 +
 +    unsafe {};
 +}
 +
 +pub fn print_binary_tree() {
 +    println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
 +}
 +
 +mod unsafe_impl_smoke_test {
 +    unsafe trait A {}
 +
 +    // error: no safety comment
 +    unsafe impl A for () {}
 +
 +    // Safety: ok
 +    unsafe impl A for (i32) {}
 +
 +    mod sub_mod {
 +        // error:
 +        unsafe impl B for (u32) {}
 +        unsafe trait B {}
 +    }
 +
 +    #[rustfmt::skip]
 +    mod sub_mod2 {
 +        //
 +        // SAFETY: ok
 +        //
 +
 +        unsafe impl B for (u32) {}
 +        unsafe trait B {}
 +    }
 +}
 +
 +mod unsafe_impl_from_macro {
 +    unsafe trait T {}
 +
 +    // error
 +    macro_rules! no_safety_comment {
 +        ($t:ty) => {
 +            unsafe impl T for $t {}
 +        };
 +    }
 +
 +    // ok
 +    no_safety_comment!(());
 +
 +    // ok
 +    macro_rules! with_safety_comment {
 +        ($t:ty) => {
 +            // SAFETY:
 +            unsafe impl T for $t {}
 +        };
 +    }
 +
 +    // ok
 +    with_safety_comment!((i32));
 +}
 +
 +mod unsafe_impl_macro_and_not_macro {
 +    unsafe trait T {}
 +
 +    // error
 +    macro_rules! no_safety_comment {
 +        ($t:ty) => {
 +            unsafe impl T for $t {}
 +        };
 +    }
 +
 +    // ok
 +    no_safety_comment!(());
 +
 +    // error
 +    unsafe impl T for (i32) {}
 +
 +    // ok
 +    no_safety_comment!(u32);
 +
 +    // error
 +    unsafe impl T for (bool) {}
 +}
 +
 +#[rustfmt::skip]
 +mod unsafe_impl_valid_comment {
 +    unsafe trait SaFety {}
 +    // SaFety:
 +    unsafe impl SaFety for () {}
 +
 +    unsafe trait MultiLineComment {}
 +    // The following impl is safe
 +    // ...
 +    // Safety: reason
 +    unsafe impl MultiLineComment for () {}
 +
 +    unsafe trait NoAscii {}
 +    // 安全 SAFETY: 以下のコードは安全です
 +    unsafe impl NoAscii for () {}
 +
 +    unsafe trait InlineAndPrecedingComment {}
 +    // SAFETY:
 +    /* comment */ unsafe impl InlineAndPrecedingComment for () {}
 +
 +    unsafe trait BuriedSafety {}
 +    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
 +    // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
 +    // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
 +    // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
 +    // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
 +    // laborum. Safety:
 +    // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
 +    // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
 +    // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
 +    // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
 +    unsafe impl BuriedSafety for () {}
 +
 +    unsafe trait MultiLineBlockComment {}
 +    /* This is a description
 +     * Safety: */
 +    unsafe impl MultiLineBlockComment for () {}
 +}
 +
 +#[rustfmt::skip]
 +mod unsafe_impl_invalid_comment {
 +    unsafe trait NoComment {}
 +
 +    unsafe impl NoComment for () {}
 +
 +    unsafe trait InlineComment {}
 +
 +    /* SAFETY: */ unsafe impl InlineComment for () {}
 +
 +    unsafe trait TrailingComment {}
 +
 +    unsafe impl TrailingComment for () {} // SAFETY:
 +
 +    unsafe trait Interference {}
 +    // SAFETY:
 +    const BIG_NUMBER: i32 = 1000000;
 +    unsafe impl Interference for () {}
 +}
 +
 +unsafe trait ImplInFn {}
 +
 +fn impl_in_fn() {
 +    // error
 +    unsafe impl ImplInFn for () {}
 +
 +    // SAFETY: ok
 +    unsafe impl ImplInFn for (i32) {}
 +}
 +
 +unsafe trait CrateRoot {}
 +
 +// error
 +unsafe impl CrateRoot for () {}
 +
 +// SAFETY: ok
 +unsafe impl CrateRoot for (i32) {}
 +
 +fn issue_9142() {
 +    // SAFETY: ok
 +    let _ =
 +        // we need this comment to avoid rustfmt putting
 +        // it all on one line
 +        unsafe {};
 +
 +    // SAFETY: this is more than one level away, so it should warn
 +    let _ = {
 +        if unsafe { true } {
 +            todo!();
 +        } else {
 +            let bar = unsafe {};
 +            todo!();
 +            bar
 +        }
 +    };
 +}
 +
 +fn main() {}
index ba4de9806d17523a4675a4d9250b7fe94695dfc9,0000000000000000000000000000000000000000..d1c1bb5ffeac85eec808031e3f97ad96d82b8425
mode 100644,000000..100644
--- /dev/null
@@@ -1,291 -1,0 +1,322 @@@
- error: aborting due to 34 previous errors
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:262:19
 +   |
 +LL |     /* Safety: */ unsafe {}
 +   |                   ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:266:5
 +   |
 +LL |     unsafe {}
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:270:14
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |              ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:270:29
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |                             ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:270:48
 +   |
 +LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
 +   |                                                ^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:274:18
 +   |
 +LL |     let _ = (42, unsafe {}, "test", unsafe {});
 +   |                  ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:274:37
 +   |
 +LL |     let _ = (42, unsafe {}, "test", unsafe {});
 +   |                                     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:278:14
 +   |
 +LL |     let _ = *unsafe { &42 };
 +   |              ^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:283:19
 +   |
 +LL |     let _ = match unsafe {} {
 +   |                   ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:289:14
 +   |
 +LL |     let _ = &unsafe {};
 +   |              ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:293:14
 +   |
 +LL |     let _ = [unsafe {}; 5];
 +   |              ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:297:13
 +   |
 +LL |     let _ = unsafe {};
 +   |             ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:307:8
 +   |
 +LL |     t!(unsafe {});
 +   |        ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:313:13
 +   |
 +LL |             unsafe {}
 +   |             ^^^^^^^^^
 +...
 +LL |     t!();
 +   |     ---- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:321:5
 +   |
 +LL |     unsafe {} // SAFETY:
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:325:5
 +   |
 +LL |     unsafe {
 +   |     ^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:335:5
 +   |
 +LL |     unsafe {};
 +   |     ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:339:20
 +   |
 +LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:346:5
 +   |
 +LL |     unsafe impl A for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:353:9
 +   |
 +LL |         unsafe impl B for (u32) {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:374:13
 +   |
 +LL |             unsafe impl T for $t {}
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^
 +...
 +LL |     no_safety_comment!(());
 +   |     ---------------------- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:399:13
 +   |
 +LL |             unsafe impl T for $t {}
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^
 +...
 +LL |     no_safety_comment!(());
 +   |     ---------------------- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:407:5
 +   |
 +LL |     unsafe impl T for (i32) {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:399:13
 +   |
 +LL |             unsafe impl T for $t {}
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^
 +...
 +LL |     no_safety_comment!(u32);
 +   |     ----------------------- in this macro invocation
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +   = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:413:5
 +   |
 +LL |     unsafe impl T for (bool) {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:459:5
 +   |
 +LL |     unsafe impl NoComment for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:463:19
 +   |
 +LL |     /* SAFETY: */ unsafe impl InlineComment for () {}
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:467:5
 +   |
 +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
 +   |
 +LL |     unsafe impl Interference for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:479:5
 +   |
 +LL |     unsafe impl ImplInFn for () {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe impl missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:488:1
 +   |
 +LL | unsafe impl CrateRoot for () {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:498:9
 +   |
 +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
 +   |
 +LL |         if unsafe { true } {
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
 +error: unsafe block missing a safety comment
 +  --> $DIR/undocumented_unsafe_blocks.rs:505:23
 +   |
 +LL |             let bar = unsafe {};
 +   |                       ^^^^^^^^^
 +   |
 +   = help: consider adding a safety comment on the preceding line
 +
++error: aborting due to 36 previous errors
 +
index 106274479751d7439a50bc2411b9106317447f3b,0000000000000000000000000000000000000000..9d08e80cf9a55717b7ccb7b3fd137d4a3b9b5178
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,176 @@@
- #![feature(custom_inner_attributes)]
 +// aux-build:proc_macro_with_span.rs
 +// run-rustfix
-         "val='{
-     }'",
-         local_i32
 +#![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)]
 +
 +extern crate proc_macro_with_span;
 +use proc_macro_with_span::with_span;
 +
 +macro_rules! no_param_str {
 +    () => {
 +        "{}"
 +    };
 +}
 +
 +macro_rules! my_println {
 +   ($($args:tt),*) => {{
 +        println!($($args),*)
 +    }};
 +}
 +
 +macro_rules! my_println_args {
 +    ($($args:tt),*) => {{
 +        println!("foo: {}", format_args!($($args),*))
 +    }};
 +}
 +
 +fn tester(fn_arg: i32) {
 +    let local_i32 = 1;
 +    let local_f64 = 2.0;
 +    let local_opt: Option<i32> = Some(3);
 +    let width = 4;
 +    let prec = 5;
 +    let val = 6;
 +
 +    // make sure this file hasn't been corrupted with tabs converted to spaces
 +    // let _ = '      ';  // <- this is a single tab character
 +    let _: &[u8; 3] = b"              "; // <- <tab><space><tab>
 +
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32}'"); // 3 spaces
 +    println!("val='{local_i32}'"); // tab
 +    println!("val='{local_i32}'"); // space+tab
 +    println!("val='{local_i32}'"); // tab+space
 +    println!(
-     println!("Hello {} is {local_f64:.local_i32$}", "x");
-     println!("Hello {local_i32} is {local_f64:.*}", 5);
-     println!("Hello {local_i32} is {local_f64:.*}", 5);
++        "val='{local_i32}'"
 +    );
 +    println!("{local_i32}");
 +    println!("{fn_arg}");
 +    println!("{local_i32:?}");
 +    println!("{local_i32:#?}");
 +    println!("{local_i32:4}");
 +    println!("{local_i32:04}");
 +    println!("{local_i32:<3}");
 +    println!("{local_i32:#010x}");
 +    println!("{local_f64:.1}");
-     println!("{local_i32}, {}", local_opt.unwrap());
++    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}");
-         "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$}",
-         local_i32, width, prec,
++    println!("{}, {}", local_i32, local_opt.unwrap());
 +    println!("{val}");
 +    println!("{val}");
 +    println!("{} {1}", local_i32, 42);
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32}'");
 +    println!("val='{fn_arg}'");
 +    println!("{local_i32}");
 +    println!("{local_i32:?}");
 +    println!("{local_i32:#?}");
 +    println!("{local_i32:04}");
 +    println!("{local_i32:<3}");
 +    println!("{local_i32:#010x}");
 +    println!("{local_f64:.1}");
 +    println!("{local_i32} {local_i32}");
 +    println!("{local_f64} {local_i32} {local_i32} {local_f64}");
 +    println!("{local_i32} {local_f64}");
 +    println!("{local_f64} {local_i32}");
 +    println!("{local_f64} {local_i32} {local_f64} {local_i32}");
 +    println!("{1} {0}", "str", local_i32);
 +    println!("{local_i32}");
 +    println!("{local_i32:width$}");
 +    println!("{local_i32:width$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{val:val$}");
 +    println!("{val:val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{val:val$.val$}");
 +    println!("{width:width$}");
 +    println!("{local_i32:width$}");
 +    println!("{width:width$}");
 +    println!("{local_i32:width$}");
 +    println!("{prec:.prec$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{prec:.prec$}");
 +    println!("{local_i32:.prec$}");
 +    println!("{width:width$.prec$}");
 +    println!("{width:width$.prec$}");
 +    println!("{local_f64:width$.prec$}");
 +    println!("{local_f64:width$.prec$} {local_f64} {width} {prec}");
 +    println!(
-         "{}",
-         // comment with a comma , in it
-         val,
++        "{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}",
 +        local_i32,
 +        width,
 +        prec,
 +        1 + 2
 +    );
 +    println!("Width = {local_i32}, value with width = {local_f64:local_i32$}");
 +    println!("{local_i32:width$.prec$}");
 +    println!("{width:width$.prec$}");
 +    println!("{}", format!("{local_i32}"));
 +    my_println!("{}", local_i32);
 +    my_println_args!("{}", local_i32);
 +
 +    // these should NOT be modified by the lint
 +    println!(concat!("nope ", "{}"), local_i32);
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32 }'");
 +    println!("val='{local_i32 }'"); // with tab
 +    println!("val='{local_i32\n}'");
 +    println!("{}", usize::MAX);
 +    println!("{}", local_opt.unwrap());
 +    println!(
 +        "val='{local_i32
 +    }'"
 +    );
 +    println!(no_param_str!(), local_i32);
 +
 +    println!(
-     #![clippy::msrv = "1.57"]
++        "{val}",
 +    );
 +    println!("{val}");
 +
 +    println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64);
 +    println!("{}", with_span!(span val));
 +
 +    if local_i32 > 0 {
 +        panic!("p1 {local_i32}");
 +    }
 +    if local_i32 > 0 {
 +        panic!("p2 {local_i32}");
 +    }
 +    if local_i32 > 0 {
 +        panic!("p3 {local_i32}");
 +    }
 +    if local_i32 > 0 {
 +        panic!("p4 {local_i32}");
 +    }
 +}
 +
 +fn main() {
 +    tester(42);
 +}
 +
++#[clippy::msrv = "1.57"]
 +fn _under_msrv() {
-     #![clippy::msrv = "1.58"]
 +    let local_i32 = 1;
 +    println!("don't expand='{}'", local_i32);
 +}
 +
++#[clippy::msrv = "1.58"]
 +fn _meets_msrv() {
 +    let local_i32 = 1;
 +    println!("expand='{local_i32}'");
 +}
index 8e495ebd083a55e6eacae4ba91dbc7859c5d2ec6,0000000000000000000000000000000000000000..35b3677a8968ffe480a4fafcf62adc979c1e17b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,181 @@@
- #![feature(custom_inner_attributes)]
 +// aux-build:proc_macro_with_span.rs
 +// run-rustfix
-     #![clippy::msrv = "1.57"]
 +#![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)]
 +
 +extern crate proc_macro_with_span;
 +use proc_macro_with_span::with_span;
 +
 +macro_rules! no_param_str {
 +    () => {
 +        "{}"
 +    };
 +}
 +
 +macro_rules! my_println {
 +   ($($args:tt),*) => {{
 +        println!($($args),*)
 +    }};
 +}
 +
 +macro_rules! my_println_args {
 +    ($($args:tt),*) => {{
 +        println!("foo: {}", format_args!($($args),*))
 +    }};
 +}
 +
 +fn tester(fn_arg: i32) {
 +    let local_i32 = 1;
 +    let local_f64 = 2.0;
 +    let local_opt: Option<i32> = Some(3);
 +    let width = 4;
 +    let prec = 5;
 +    let val = 6;
 +
 +    // make sure this file hasn't been corrupted with tabs converted to spaces
 +    // let _ = '      ';  // <- this is a single tab character
 +    let _: &[u8; 3] = b"              "; // <- <tab><space><tab>
 +
 +    println!("val='{}'", local_i32);
 +    println!("val='{   }'", local_i32); // 3 spaces
 +    println!("val='{  }'", local_i32); // tab
 +    println!("val='{  }'", local_i32); // space+tab
 +    println!("val='{   }'", local_i32); // tab+space
 +    println!(
 +        "val='{
 +    }'",
 +        local_i32
 +    );
 +    println!("{}", local_i32);
 +    println!("{}", fn_arg);
 +    println!("{:?}", local_i32);
 +    println!("{:#?}", local_i32);
 +    println!("{:4}", local_i32);
 +    println!("{:04}", local_i32);
 +    println!("{:<3}", local_i32);
 +    println!("{:#010x}", local_i32);
 +    println!("{:.1}", local_f64);
 +    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!("{}", val);
 +    println!("{}", v = val);
 +    println!("{} {1}", local_i32, 42);
 +    println!("val='{\t }'", local_i32);
 +    println!("val='{\n }'", local_i32);
 +    println!("val='{local_i32}'", local_i32 = local_i32);
 +    println!("val='{local_i32}'", local_i32 = fn_arg);
 +    println!("{0}", local_i32);
 +    println!("{0:?}", local_i32);
 +    println!("{0:#?}", local_i32);
 +    println!("{0:04}", local_i32);
 +    println!("{0:<3}", local_i32);
 +    println!("{0:#010x}", local_i32);
 +    println!("{0:.1}", local_f64);
 +    println!("{0} {0}", local_i32);
 +    println!("{1} {} {0} {}", local_i32, local_f64);
 +    println!("{0} {1}", local_i32, local_f64);
 +    println!("{1} {0}", local_i32, local_f64);
 +    println!("{1} {0} {1} {0}", local_i32, local_f64);
 +    println!("{1} {0}", "str", local_i32);
 +    println!("{v}", v = local_i32);
 +    println!("{local_i32:0$}", width);
 +    println!("{local_i32:w$}", w = width);
 +    println!("{local_i32:.0$}", prec);
 +    println!("{local_i32:.p$}", p = prec);
 +    println!("{:0$}", v = val);
 +    println!("{0:0$}", v = val);
 +    println!("{:0$.0$}", v = val);
 +    println!("{0:0$.0$}", v = val);
 +    println!("{0:0$.v$}", v = val);
 +    println!("{0:v$.0$}", v = val);
 +    println!("{v:0$.0$}", v = val);
 +    println!("{v:v$.0$}", v = val);
 +    println!("{v:0$.v$}", v = val);
 +    println!("{v:v$.v$}", v = val);
 +    println!("{:0$}", width);
 +    println!("{:1$}", local_i32, width);
 +    println!("{:w$}", w = width);
 +    println!("{:w$}", local_i32, w = width);
 +    println!("{:.0$}", prec);
 +    println!("{:.1$}", local_i32, prec);
 +    println!("{:.p$}", p = prec);
 +    println!("{:.p$}", local_i32, p = prec);
 +    println!("{:0$.1$}", width, prec);
 +    println!("{:0$.w$}", width, w = prec);
 +    println!("{:1$.2$}", local_f64, width, prec);
 +    println!("{:1$.2$} {0} {1} {2}", 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,
 +    );
 +    println!(
 +        "{0:1$.2$} {0:2$.1$} {1:0$.2$} {1:2$.0$} {2:0$.1$} {2:1$.0$} {3}",
 +        local_i32,
 +        width,
 +        prec,
 +        1 + 2
 +    );
 +    println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
 +    println!("{:w$.p$}", local_i32, w = width, p = prec);
 +    println!("{:w$.p$}", w = width, p = prec);
 +    println!("{}", format!("{}", local_i32));
 +    my_println!("{}", local_i32);
 +    my_println_args!("{}", local_i32);
 +
 +    // these should NOT be modified by the lint
 +    println!(concat!("nope ", "{}"), local_i32);
 +    println!("val='{local_i32}'");
 +    println!("val='{local_i32 }'");
 +    println!("val='{local_i32 }'"); // with tab
 +    println!("val='{local_i32\n}'");
 +    println!("{}", usize::MAX);
 +    println!("{}", local_opt.unwrap());
 +    println!(
 +        "val='{local_i32
 +    }'"
 +    );
 +    println!(no_param_str!(), local_i32);
 +
 +    println!(
 +        "{}",
 +        // comment with a comma , in it
 +        val,
 +    );
 +    println!("{}", /* comment with a comma , in it */ val);
 +
 +    println!(with_span!("{0} {1}" "{1} {0}"), local_i32, local_f64);
 +    println!("{}", with_span!(span val));
 +
 +    if local_i32 > 0 {
 +        panic!("p1 {}", local_i32);
 +    }
 +    if local_i32 > 0 {
 +        panic!("p2 {0}", local_i32);
 +    }
 +    if local_i32 > 0 {
 +        panic!("p3 {local_i32}", local_i32 = local_i32);
 +    }
 +    if local_i32 > 0 {
 +        panic!("p4 {local_i32}");
 +    }
 +}
 +
 +fn main() {
 +    tester(42);
 +}
 +
++#[clippy::msrv = "1.57"]
 +fn _under_msrv() {
-     #![clippy::msrv = "1.58"]
 +    let local_i32 = 1;
 +    println!("don't expand='{}'", local_i32);
 +}
 +
++#[clippy::msrv = "1.58"]
 +fn _meets_msrv() {
 +    let local_i32 = 1;
 +    println!("expand='{}'", local_i32);
 +}
index 2ce3b7fa960c6159e3c3a237d43220aa195f1516,0000000000000000000000000000000000000000..a12abf8bef8af07bb4d53ab4f8227b26e6fd9a80
mode 100644,000000..100644
--- /dev/null
@@@ -1,879 -1,0 +1,860 @@@
-   --> $DIR/uninlined_format_args.rs:41:5
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:42:5
++  --> $DIR/uninlined_format_args.rs:40: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: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:43:5
++  --> $DIR/uninlined_format_args.rs:41:5
 +   |
 +LL |     println!("val='{   }'", local_i32); // 3 spaces
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{   }'", local_i32); // 3 spaces
 +LL +     println!("val='{local_i32}'"); // 3 spaces
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:44:5
++  --> $DIR/uninlined_format_args.rs:42:5
 +   |
 +LL |     println!("val='{    }'", local_i32); // tab
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{    }'", local_i32); // tab
 +LL +     println!("val='{local_i32}'"); // tab
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:45:5
++  --> $DIR/uninlined_format_args.rs:43:5
 +   |
 +LL |     println!("val='{     }'", local_i32); // space+tab
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{     }'", local_i32); // space+tab
 +LL +     println!("val='{local_i32}'"); // space+tab
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:51:5
++  --> $DIR/uninlined_format_args.rs:44:5
 +   |
 +LL |     println!("val='{     }'", local_i32); // tab+space
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{     }'", local_i32); // tab+space
 +LL +     println!("val='{local_i32}'"); // tab+space
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:52: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);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", local_i32);
 +LL +     println!("{local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:53:5
++  --> $DIR/uninlined_format_args.rs:51:5
 +   |
 +LL |     println!("{}", fn_arg);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", fn_arg);
 +LL +     println!("{fn_arg}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:54:5
++  --> $DIR/uninlined_format_args.rs:52:5
 +   |
 +LL |     println!("{:?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:?}", local_i32);
 +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:53:5
 +   |
 +LL |     println!("{:#?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:#?}", local_i32);
 +LL +     println!("{local_i32:#?}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:56:5
++  --> $DIR/uninlined_format_args.rs:54:5
 +   |
 +LL |     println!("{:4}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:4}", local_i32);
 +LL +     println!("{local_i32:4}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:57:5
++  --> $DIR/uninlined_format_args.rs:55:5
 +   |
 +LL |     println!("{:04}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:04}", local_i32);
 +LL +     println!("{local_i32:04}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:58:5
++  --> $DIR/uninlined_format_args.rs:56:5
 +   |
 +LL |     println!("{:<3}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:<3}", local_i32);
 +LL +     println!("{local_i32:<3}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:59:5
++  --> $DIR/uninlined_format_args.rs:57:5
 +   |
 +LL |     println!("{:#010x}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:#010x}", local_i32);
 +LL +     println!("{local_i32:#010x}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
- 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);
-    |
++  --> $DIR/uninlined_format_args.rs:58:5
 +   |
 +LL |     println!("{:.1}", local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.1}", local_f64);
 +LL +     println!("{local_f64:.1}");
 +   |
 +
- 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
-    |
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:62: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!("{} {}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{} {}", local_i32, local_f64);
 +LL +     println!("{local_i32} {local_f64}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
 +  --> $DIR/uninlined_format_args.rs:64:5
 +   |
-   --> $DIR/uninlined_format_args.rs:66:5
 +LL |     println!("{}", val);
 +   |     ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", val);
 +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:65:5
 +   |
 +LL |     println!("{}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", v = val);
 +LL +     println!("{val}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:69:5
++  --> $DIR/uninlined_format_args.rs:67:5
 +   |
 +LL |     println!("val='{/t }'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{/t }'", local_i32);
 +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:68:5
 +   |
 +LL |     println!("val='{/n }'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{/n }'", local_i32);
 +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:69:5
 +   |
 +LL |     println!("val='{local_i32}'", local_i32 = local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{local_i32}'", local_i32 = local_i32);
 +LL +     println!("val='{local_i32}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:72:5
++  --> $DIR/uninlined_format_args.rs:70:5
 +   |
 +LL |     println!("val='{local_i32}'", local_i32 = fn_arg);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("val='{local_i32}'", local_i32 = fn_arg);
 +LL +     println!("val='{fn_arg}'");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:73:5
++  --> $DIR/uninlined_format_args.rs:71:5
 +   |
 +LL |     println!("{0}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0}", local_i32);
 +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:72:5
 +   |
 +LL |     println!("{0:?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:?}", local_i32);
 +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:73:5
 +   |
 +LL |     println!("{0:#?}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:#?}", local_i32);
 +LL +     println!("{local_i32:#?}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:76:5
++  --> $DIR/uninlined_format_args.rs:74:5
 +   |
 +LL |     println!("{0:04}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:04}", local_i32);
 +LL +     println!("{local_i32:04}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:77:5
++  --> $DIR/uninlined_format_args.rs:75:5
 +   |
 +LL |     println!("{0:<3}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:<3}", local_i32);
 +LL +     println!("{local_i32:<3}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:78:5
++  --> $DIR/uninlined_format_args.rs:76:5
 +   |
 +LL |     println!("{0:#010x}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:#010x}", local_i32);
 +LL +     println!("{local_i32:#010x}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:79:5
++  --> $DIR/uninlined_format_args.rs:77:5
 +   |
 +LL |     println!("{0:.1}", local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:.1}", local_f64);
 +LL +     println!("{local_f64:.1}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:80:5
++  --> $DIR/uninlined_format_args.rs:78:5
 +   |
 +LL |     println!("{0} {0}", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0} {0}", local_i32);
 +LL +     println!("{local_i32} {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:81:5
++  --> $DIR/uninlined_format_args.rs:79:5
 +   |
 +LL |     println!("{1} {} {0} {}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{1} {} {0} {}", local_i32, local_f64);
 +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:82:5
++  --> $DIR/uninlined_format_args.rs:80:5
 +   |
 +LL |     println!("{0} {1}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0} {1}", local_i32, local_f64);
 +LL +     println!("{local_i32} {local_f64}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:83:5
++  --> $DIR/uninlined_format_args.rs:81:5
 +   |
 +LL |     println!("{1} {0}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{1} {0}", local_i32, local_f64);
 +LL +     println!("{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:82:5
 +   |
 +LL |     println!("{1} {0} {1} {0}", local_i32, local_f64);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{1} {0} {1} {0}", local_i32, local_f64);
 +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:86:5
++  --> $DIR/uninlined_format_args.rs:84:5
 +   |
 +LL |     println!("{v}", v = local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v}", v = local_i32);
 +LL +     println!("{local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:87:5
++  --> $DIR/uninlined_format_args.rs:85:5
 +   |
 +LL |     println!("{local_i32:0$}", width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:0$}", width);
 +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:86:5
 +   |
 +LL |     println!("{local_i32:w$}", w = width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:w$}", w = width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:89:5
++  --> $DIR/uninlined_format_args.rs:87:5
 +   |
 +LL |     println!("{local_i32:.0$}", prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:.0$}", prec);
 +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:88:5
 +   |
 +LL |     println!("{local_i32:.p$}", p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{local_i32:.p$}", p = prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:91:5
++  --> $DIR/uninlined_format_args.rs:89:5
 +   |
 +LL |     println!("{:0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$}", v = val);
 +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:90:5
 +   |
 +LL |     println!("{0:0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:0$}", v = val);
 +LL +     println!("{val:val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:93:5
++  --> $DIR/uninlined_format_args.rs:91:5
 +   |
 +LL |     println!("{:0$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$.0$}", v = val);
 +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:92:5
 +   |
 +LL |     println!("{0:0$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:0$.0$}", v = val);
 +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:93:5
 +   |
 +LL |     println!("{0:0$.v$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:0$.v$}", v = val);
 +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:94:5
 +   |
 +LL |     println!("{0:v$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{0:v$.0$}", v = val);
 +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:95:5
 +   |
 +LL |     println!("{v:0$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:0$.0$}", v = val);
 +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:96:5
 +   |
 +LL |     println!("{v:v$.0$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:v$.0$}", v = val);
 +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:97:5
 +   |
 +LL |     println!("{v:0$.v$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:0$.v$}", v = val);
 +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:98:5
 +   |
 +LL |     println!("{v:v$.v$}", v = val);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{v:v$.v$}", v = val);
 +LL +     println!("{val:val$.val$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:101:5
++  --> $DIR/uninlined_format_args.rs:99:5
 +   |
 +LL |     println!("{:0$}", width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$}", width);
 +LL +     println!("{width:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:102:5
++  --> $DIR/uninlined_format_args.rs:100:5
 +   |
 +LL |     println!("{:1$}", local_i32, width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:1$}", local_i32, width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:103:5
++  --> $DIR/uninlined_format_args.rs:101:5
 +   |
 +LL |     println!("{:w$}", w = width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$}", w = width);
 +LL +     println!("{width:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:104:5
++  --> $DIR/uninlined_format_args.rs:102:5
 +   |
 +LL |     println!("{:w$}", local_i32, w = width);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$}", local_i32, w = width);
 +LL +     println!("{local_i32:width$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:105:5
++  --> $DIR/uninlined_format_args.rs:103:5
 +   |
 +LL |     println!("{:.0$}", prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.0$}", prec);
 +LL +     println!("{prec:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:106:5
++  --> $DIR/uninlined_format_args.rs:104:5
 +   |
 +LL |     println!("{:.1$}", local_i32, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.1$}", local_i32, prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:107:5
++  --> $DIR/uninlined_format_args.rs:105:5
 +   |
 +LL |     println!("{:.p$}", p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.p$}", p = prec);
 +LL +     println!("{prec:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:108:5
++  --> $DIR/uninlined_format_args.rs:106:5
 +   |
 +LL |     println!("{:.p$}", local_i32, p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:.p$}", local_i32, p = prec);
 +LL +     println!("{local_i32:.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:109:5
++  --> $DIR/uninlined_format_args.rs:107:5
 +   |
 +LL |     println!("{:0$.1$}", width, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$.1$}", width, prec);
 +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:108:5
 +   |
 +LL |     println!("{:0$.w$}", width, w = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:0$.w$}", width, w = prec);
 +LL +     println!("{width:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:111:5
++  --> $DIR/uninlined_format_args.rs:109:5
 +   |
 +LL |     println!("{:1$.2$}", local_f64, width, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:1$.2$}", local_f64, width, prec);
 +LL +     println!("{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:110:5
 +   |
 +LL |     println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:1$.2$} {0} {1} {2}", local_f64, width, prec);
 +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:124: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);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("Width = {}, value with width = {:0$}", local_i32, local_f64);
 +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:125:5
++  --> $DIR/uninlined_format_args.rs:123:5
 +   |
 +LL |     println!("{:w$.p$}", local_i32, w = width, p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$.p$}", local_i32, w = width, p = prec);
 +LL +     println!("{local_i32:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:126:20
++  --> $DIR/uninlined_format_args.rs:124:5
 +   |
 +LL |     println!("{:w$.p$}", w = width, p = prec);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{:w$.p$}", w = width, p = prec);
 +LL +     println!("{width:width$.prec$}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:149:5
++  --> $DIR/uninlined_format_args.rs:125:20
 +   |
 +LL |     println!("{}", format!("{}", local_i32));
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", format!("{}", local_i32));
 +LL +     println!("{}", format!("{local_i32}"));
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:155:9
++  --> $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);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("{}", /* comment with a comma , in it */ val);
 +LL +     println!("{val}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:158:9
++  --> $DIR/uninlined_format_args.rs:154:9
 +   |
 +LL |         panic!("p1 {}", local_i32);
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -         panic!("p1 {}", local_i32);
 +LL +         panic!("p1 {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:161:9
++  --> $DIR/uninlined_format_args.rs:157:9
 +   |
 +LL |         panic!("p2 {0}", local_i32);
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -         panic!("p2 {0}", local_i32);
 +LL +         panic!("p2 {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
-   --> $DIR/uninlined_format_args.rs:181:5
++  --> $DIR/uninlined_format_args.rs:160:9
 +   |
 +LL |         panic!("p3 {local_i32}", local_i32 = local_i32);
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -         panic!("p3 {local_i32}", local_i32 = local_i32);
 +LL +         panic!("p3 {local_i32}");
 +   |
 +
 +error: variables can be used directly in the `format!` string
- error: aborting due to 73 previous errors
++  --> $DIR/uninlined_format_args.rs:180:5
 +   |
 +LL |     println!("expand='{}'", local_i32);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: change this to
 +   |
 +LL -     println!("expand='{}'", local_i32);
 +LL +     println!("expand='{local_i32}'");
 +   |
 +
++error: aborting due to 72 previous errors
 +
index ec8c6abfab91e725891f268967c59dabcb101e13,0000000000000000000000000000000000000000..2f7e2997e739d50ec990455b60cbf898820ad2bf
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,132 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
 +#![allow(
 +    unused_must_use,
 +    clippy::borrow_as_ptr,
 +    clippy::no_effect,
 +    clippy::nonstandard_macro_braces,
 +    clippy::unnecessary_operation
 +)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1_i32;
 +    1_f32;
 +    false;
 +    &1i32 as &i32;
 +
 +    -1_i32;
 +    - 1_i32;
 +    -1_f32;
 +    1_i32;
 +    1_f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // 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;
 +
 +mod fixable {
 +    #![allow(dead_code)]
 +
 +    fn main() {
 +        // casting integer literal to float is unnecessary
 +        100_f32;
 +        100_f64;
 +        100_f64;
 +        let _ = -100_f32;
 +        let _ = -100_f64;
 +        let _ = -100_f64;
 +        100_f32;
 +        100_f64;
 +        // Should not trigger
 +        #[rustfmt::skip]
 +        let v = vec!(1);
 +        &v as &[i32];
 +        0x10 as f32;
 +        0o10 as f32;
 +        0b10 as f32;
 +        0x11 as f64;
 +        0o11 as f64;
 +        0b11 as f64;
 +
 +        1_u32;
 +        0x10_i32;
 +        0b10_usize;
 +        0o73_u16;
 +        1_000_000_000_u32;
 +
 +        1.0_f64;
 +        0.5_f32;
 +
 +        1.0 as u16;
 +
 +        let _ = -1_i32;
 +        let _ = -1.0_f32;
 +
 +        let _ = 1 as I32Alias;
 +        let _ = &1 as &I32Alias;
++
++        let x = 1i32;
++        let _ = &{ x };
 +    }
 +
 +    type I32Alias = i32;
 +
 +    fn issue_9380() {
 +        let _: i32 = -1_i32;
 +        let _: f32 = -(1) as f32;
 +        let _: i64 = -1_i64;
 +        let _: i64 = -(1.0) as i64;
 +
 +        let _ = -(1 + 1) as i64;
 +    }
 +
 +    fn issue_9563() {
 +        let _: f64 = (-8.0_f64).exp();
 +        #[allow(clippy::precedence)]
 +        let _: f64 = -8.0_f64.exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
 +    }
 +
 +    fn issue_9562_non_literal() {
 +        fn foo() -> f32 {
 +            0.
 +        }
 +
 +        let _num = foo();
 +    }
 +
 +    fn issue_9603() {
 +        let _: f32 = -0x400 as f32;
 +    }
 +}
index 5213cdc269bd4c5a9aec63cb2c5ff31a87e4ccf9,0000000000000000000000000000000000000000..54dd46ba59f104a1172d14c9363b09b08ad79627
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,132 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
 +#![allow(
 +    unused_must_use,
 +    clippy::borrow_as_ptr,
 +    clippy::no_effect,
 +    clippy::nonstandard_macro_braces,
 +    clippy::unnecessary_operation
 +)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1i32 as i32;
 +    1f32 as f32;
 +    false as bool;
 +    &1i32 as &i32;
 +
 +    -1_i32 as i32;
 +    - 1_i32 as i32;
 +    -1f32 as f32;
 +    1_i32 as i32;
 +    1_f32 as f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // 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;
 +
 +mod fixable {
 +    #![allow(dead_code)]
 +
 +    fn main() {
 +        // casting integer literal to float is unnecessary
 +        100 as f32;
 +        100 as f64;
 +        100_i32 as f64;
 +        let _ = -100 as f32;
 +        let _ = -100 as f64;
 +        let _ = -100_i32 as f64;
 +        100. as f32;
 +        100. as f64;
 +        // Should not trigger
 +        #[rustfmt::skip]
 +        let v = vec!(1);
 +        &v as &[i32];
 +        0x10 as f32;
 +        0o10 as f32;
 +        0b10 as f32;
 +        0x11 as f64;
 +        0o11 as f64;
 +        0b11 as f64;
 +
 +        1 as u32;
 +        0x10 as i32;
 +        0b10 as usize;
 +        0o73 as u16;
 +        1_000_000_000 as u32;
 +
 +        1.0 as f64;
 +        0.5 as f32;
 +
 +        1.0 as u16;
 +
 +        let _ = -1 as i32;
 +        let _ = -1.0 as f32;
 +
 +        let _ = 1 as I32Alias;
 +        let _ = &1 as &I32Alias;
++
++        let x = 1i32;
++        let _ = &(x as i32);
 +    }
 +
 +    type I32Alias = i32;
 +
 +    fn issue_9380() {
 +        let _: i32 = -(1) as i32;
 +        let _: f32 = -(1) as f32;
 +        let _: i64 = -(1) as i64;
 +        let _: i64 = -(1.0) as i64;
 +
 +        let _ = -(1 + 1) as i64;
 +    }
 +
 +    fn issue_9563() {
 +        let _: f64 = (-8.0 as f64).exp();
 +        #[allow(clippy::precedence)]
 +        let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior
 +    }
 +
 +    fn issue_9562_non_literal() {
 +        fn foo() -> f32 {
 +            0.
 +        }
 +
 +        let _num = foo() as f32;
 +    }
 +
 +    fn issue_9603() {
 +        let _: f32 = -0x400 as f32;
 +    }
 +}
index e5c3dd5e53f876e2489d702319694058069fd6d4,0000000000000000000000000000000000000000..fcee4ee2a65cc9fa267c65354382292421c3ede8
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,190 @@@
-   --> $DIR/unnecessary_cast.rs:53:9
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:14:5
 +   |
 +LL |     1i32 as i32;
 +   |     ^^^^^^^^^^^ help: try: `1_i32`
 +   |
 +   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:15:5
 +   |
 +LL |     1f32 as f32;
 +   |     ^^^^^^^^^^^ help: try: `1_f32`
 +
 +error: casting to the same type is unnecessary (`bool` -> `bool`)
 +  --> $DIR/unnecessary_cast.rs:16:5
 +   |
 +LL |     false as bool;
 +   |     ^^^^^^^^^^^^^ help: try: `false`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:19:5
 +   |
 +LL |     -1_i32 as i32;
 +   |     ^^^^^^^^^^^^^ help: try: `-1_i32`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:20:5
 +   |
 +LL |     - 1_i32 as i32;
 +   |     ^^^^^^^^^^^^^^ help: try: `- 1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:21:5
 +   |
 +LL |     -1f32 as f32;
 +   |     ^^^^^^^^^^^^ help: try: `-1_f32`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:22:5
 +   |
 +LL |     1_i32 as i32;
 +   |     ^^^^^^^^^^^^ help: try: `1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:23:5
 +   |
 +LL |     1_f32 as f32;
 +   |     ^^^^^^^^^^^^ help: try: `1_f32`
 +
 +error: casting integer literal to `f32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:54: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:55: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:56:17
++  --> $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:57: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:58: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:59:9
++  --> $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:60: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:72: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:73: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:74: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:75: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:76: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:78: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:79: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:83:17
++  --> $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:84: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:93:22
++  --> $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:95: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:102: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:104:23
++  --> $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:112:20
++  --> $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`)
- error: aborting due to 30 previous errors
++  --> $DIR/unnecessary_cast.rs:126:20
 +   |
 +LL |         let _num = foo() as f32;
 +   |                    ^^^^^^^^^^^^ help: try: `foo()`
 +
++error: aborting due to 31 previous errors
 +
index ce4a82e021745d45e532710d2c93cd82be091988,0000000000000000000000000000000000000000..22e9bd8bdc510f69b48e09fa5b9c45685437eecb
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,162 @@@
-         // some lines
-         // some lines
-         // some lines
-         // some lines
-         // some lines
-         // some lines
-         or(Ok(ext_str.some_field));
 +// run-rustfix
 +// aux-build: proc_macro_with_span.rs
 +#![warn(clippy::unnecessary_lazy_evaluations)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::map_identity)]
 +
 +extern crate proc_macro_with_span;
 +use proc_macro_with_span::with_span;
 +
 +struct Deep(Option<usize>);
 +
 +#[derive(Copy, Clone)]
 +struct SomeStruct {
 +    some_field: usize,
 +}
 +
 +impl SomeStruct {
 +    fn return_some_field(&self) -> usize {
 +        self.some_field
 +    }
 +}
 +
 +fn some_call<T: Default>() -> T {
 +    T::default()
 +}
 +
 +struct Issue9427(i32);
 +
 +impl Drop for Issue9427 {
 +    fn drop(&mut self) {
 +        println!("{}", self.0);
 +    }
 +}
 +
++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];
 +    let ext_str = SomeStruct { some_field: 10 };
 +
 +    let mut opt = Some(42);
 +    let ext_opt = Some(42);
 +    let nested_opt = Some(Some(42));
 +    let nested_tuple_opt = Some(Some((42, 43)));
 +    let cond = true;
 +
 +    // Should lint - Option
 +    let _ = opt.unwrap_or(2);
 +    let _ = opt.unwrap_or(astronomers_pi);
 +    let _ = opt.unwrap_or(ext_str.some_field);
 +    let _ = opt.unwrap_or_else(|| ext_arr[0]);
 +    let _ = opt.and(ext_opt);
 +    let _ = opt.or(ext_opt);
 +    let _ = opt.or(None);
 +    let _ = opt.get_or_insert(2);
 +    let _ = opt.ok_or(2);
 +    let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
 +    let _ = cond.then_some(astronomers_pi);
 +
 +    // Cases when unwrap is not called on a simple variable
 +    let _ = Some(10).unwrap_or(2);
 +    let _ = Some(10).and(ext_opt);
 +    let _: Option<usize> = None.or(ext_opt);
 +    let _ = None.get_or_insert(2);
 +    let _: Result<usize, usize> = None.ok_or(2);
 +    let _: Option<usize> = None.or(None);
 +
 +    let mut deep = Deep(Some(42));
 +    let _ = deep.0.unwrap_or(2);
 +    let _ = deep.0.and(ext_opt);
 +    let _ = deep.0.or(None);
 +    let _ = deep.0.get_or_insert(2);
 +    let _ = deep.0.ok_or(2);
 +
 +    // Should not lint - Option
 +    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
 +    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
 +    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
 +    let _ = opt.or_else(some_call);
 +    let _ = opt.or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(some_call);
 +    let _ = deep.0.get_or_insert_with(|| some_call());
 +    let _ = deep.0.or_else(some_call);
 +    let _ = deep.0.or_else(|| some_call());
 +    let _ = opt.ok_or_else(|| ext_arr[0]);
 +
 +    // 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]));
 +    let _ = Some(10).and_then(|idx| Some(idx));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Option<usize> = None.or(Some(3));
 +    let _ = deep.0.or(Some(3));
 +    let _ = opt.or(Some(3));
 +
 +    // Should lint - Result
 +    let res: Result<usize, usize> = Err(5);
 +    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
 +
 +    let _ = res2.unwrap_or(2);
 +    let _ = res2.unwrap_or(astronomers_pi);
 +    let _ = res2.unwrap_or(ext_str.some_field);
 +
 +    // Should not lint - Result
 +    let _ = res.unwrap_or_else(|err| err);
 +    let _ = res.unwrap_or_else(|err| ext_arr[err]);
 +    let _ = res2.unwrap_or_else(|err| err.some_field);
 +    let _ = res2.unwrap_or_else(|err| err.return_some_field());
 +    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
 +
 +    // should not lint, bind_instead_of_map takes priority
 +    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Err(err));
 +
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or_else(|_| Err(2));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Result<usize, usize> = res.and(Err(2));
 +    let _: Result<usize, usize> = res.and(Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or(Ok(2));
 +    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));
 +
 +    // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
 +    let _: Result<usize, usize> = res.and_then(|x| Err(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
 +}
 +
 +#[allow(unused)]
 +fn issue9485() {
 +    // should not lint, is in proc macro
 +    with_span!(span Some(42).unwrap_or_else(|| 2););
 +}
index 59cdf66285463e3e69dcb750cdcb194ce0ca7066,0000000000000000000000000000000000000000..8726d84a23fcfd8d438134400a1e357d8c0503df
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,162 @@@
-         // some lines
-         // some lines
-         // some lines
-         // some lines
-         // some lines
-         // some lines
-         or_else(|_| Ok(ext_str.some_field));
 +// run-rustfix
 +// aux-build: proc_macro_with_span.rs
 +#![warn(clippy::unnecessary_lazy_evaluations)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::map_identity)]
 +
 +extern crate proc_macro_with_span;
 +use proc_macro_with_span::with_span;
 +
 +struct Deep(Option<usize>);
 +
 +#[derive(Copy, Clone)]
 +struct SomeStruct {
 +    some_field: usize,
 +}
 +
 +impl SomeStruct {
 +    fn return_some_field(&self) -> usize {
 +        self.some_field
 +    }
 +}
 +
 +fn some_call<T: Default>() -> T {
 +    T::default()
 +}
 +
 +struct Issue9427(i32);
 +
 +impl Drop for Issue9427 {
 +    fn drop(&mut self) {
 +        println!("{}", self.0);
 +    }
 +}
 +
++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];
 +    let ext_str = SomeStruct { some_field: 10 };
 +
 +    let mut opt = Some(42);
 +    let ext_opt = Some(42);
 +    let nested_opt = Some(Some(42));
 +    let nested_tuple_opt = Some(Some((42, 43)));
 +    let cond = true;
 +
 +    // Should lint - Option
 +    let _ = opt.unwrap_or_else(|| 2);
 +    let _ = opt.unwrap_or_else(|| astronomers_pi);
 +    let _ = opt.unwrap_or_else(|| ext_str.some_field);
 +    let _ = opt.unwrap_or_else(|| ext_arr[0]);
 +    let _ = opt.and_then(|_| ext_opt);
 +    let _ = opt.or_else(|| ext_opt);
 +    let _ = opt.or_else(|| None);
 +    let _ = opt.get_or_insert_with(|| 2);
 +    let _ = opt.ok_or_else(|| 2);
 +    let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
 +    let _ = cond.then(|| astronomers_pi);
 +
 +    // Cases when unwrap is not called on a simple variable
 +    let _ = Some(10).unwrap_or_else(|| 2);
 +    let _ = Some(10).and_then(|_| ext_opt);
 +    let _: Option<usize> = None.or_else(|| ext_opt);
 +    let _ = None.get_or_insert_with(|| 2);
 +    let _: Result<usize, usize> = None.ok_or_else(|| 2);
 +    let _: Option<usize> = None.or_else(|| None);
 +
 +    let mut deep = Deep(Some(42));
 +    let _ = deep.0.unwrap_or_else(|| 2);
 +    let _ = deep.0.and_then(|_| ext_opt);
 +    let _ = deep.0.or_else(|| None);
 +    let _ = deep.0.get_or_insert_with(|| 2);
 +    let _ = deep.0.ok_or_else(|| 2);
 +
 +    // Should not lint - Option
 +    let _ = opt.unwrap_or_else(|| ext_str.return_some_field());
 +    let _ = nested_opt.unwrap_or_else(|| Some(some_call()));
 +    let _ = nested_tuple_opt.unwrap_or_else(|| Some((some_call(), some_call())));
 +    let _ = opt.or_else(some_call);
 +    let _ = opt.or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(|| some_call());
 +    let _: Result<usize, usize> = opt.ok_or_else(some_call);
 +    let _ = deep.0.get_or_insert_with(|| some_call());
 +    let _ = deep.0.or_else(some_call);
 +    let _ = deep.0.or_else(|| some_call());
 +    let _ = opt.ok_or_else(|| ext_arr[0]);
 +
 +    // 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]));
 +    let _ = Some(10).and_then(|idx| Some(idx));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Option<usize> = None.or_else(|| Some(3));
 +    let _ = deep.0.or_else(|| Some(3));
 +    let _ = opt.or_else(|| Some(3));
 +
 +    // Should lint - Result
 +    let res: Result<usize, usize> = Err(5);
 +    let res2: Result<usize, SomeStruct> = Err(SomeStruct { some_field: 5 });
 +
 +    let _ = res2.unwrap_or_else(|_| 2);
 +    let _ = res2.unwrap_or_else(|_| astronomers_pi);
 +    let _ = res2.unwrap_or_else(|_| ext_str.some_field);
 +
 +    // Should not lint - Result
 +    let _ = res.unwrap_or_else(|err| err);
 +    let _ = res.unwrap_or_else(|err| ext_arr[err]);
 +    let _ = res2.unwrap_or_else(|err| err.some_field);
 +    let _ = res2.unwrap_or_else(|err| err.return_some_field());
 +    let _ = res2.unwrap_or_else(|_| ext_str.return_some_field());
 +
 +    // should not lint, bind_instead_of_map takes priority
 +    let _: Result<usize, usize> = res.and_then(|x| Ok(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Err(err));
 +
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(2));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(astronomers_pi));
 +    let _: Result<usize, usize> = res.and_then(|_| Ok(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or_else(|_| Err(2));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.or_else(|_| Err(ext_str.some_field));
 +
 +    // should lint, bind_instead_of_map doesn't apply
 +    let _: Result<usize, usize> = res.and_then(|_| Err(2));
 +    let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
 +    let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
 +
 +    let _: Result<usize, usize> = res.or_else(|_| Ok(2));
 +    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));
 +
 +    // neither bind_instead_of_map nor unnecessary_lazy_eval applies here
 +    let _: Result<usize, usize> = res.and_then(|x| Err(x));
 +    let _: Result<usize, usize> = res.or_else(|err| Ok(err));
 +}
 +
 +#[allow(unused)]
 +fn issue9485() {
 +    // should not lint, is in proc macro
 +    with_span!(span Some(42).unwrap_or_else(|| 2););
 +}
index 8a9ece4aa7e54548a4e841688cff2b2864713de6,0000000000000000000000000000000000000000..0339755442c5a7dfd1bdb5d4ac13822f0bcd2f80
mode 100644,000000..100644
--- /dev/null
@@@ -1,283 -1,0 +1,283 @@@
-   --> $DIR/unnecessary_lazy_eval.rs:48:13
 +error: unnecessary closure used to substitute value for `Option::None`
-   --> $DIR/unnecessary_lazy_eval.rs:49:13
++  --> $DIR/unnecessary_lazy_eval.rs:56:13
 +   |
 +LL |     let _ = opt.unwrap_or_else(|| 2);
 +   |             ^^^^--------------------
 +   |                 |
 +   |                 help: use `unwrap_or(..)` instead: `unwrap_or(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:50:13
++  --> $DIR/unnecessary_lazy_eval.rs:57:13
 +   |
 +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:52:13
++  --> $DIR/unnecessary_lazy_eval.rs:58:13
 +   |
 +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:53:13
++  --> $DIR/unnecessary_lazy_eval.rs:60:13
 +   |
 +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:54:13
++  --> $DIR/unnecessary_lazy_eval.rs:61:13
 +   |
 +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:55:13
++  --> $DIR/unnecessary_lazy_eval.rs:62:13
 +   |
 +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:56:13
++  --> $DIR/unnecessary_lazy_eval.rs:63:13
 +   |
 +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:57:13
++  --> $DIR/unnecessary_lazy_eval.rs:64:13
 +   |
 +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:58:13
++  --> $DIR/unnecessary_lazy_eval.rs:65:13
 +   |
 +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:61:13
++  --> $DIR/unnecessary_lazy_eval.rs:66:13
 +   |
 +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:62:13
++  --> $DIR/unnecessary_lazy_eval.rs:69:13
 +   |
 +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:63:28
++  --> $DIR/unnecessary_lazy_eval.rs:70:13
 +   |
 +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:64:13
++  --> $DIR/unnecessary_lazy_eval.rs:71:28
 +   |
 +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:65:35
++  --> $DIR/unnecessary_lazy_eval.rs:72:13
 +   |
 +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:66:28
++  --> $DIR/unnecessary_lazy_eval.rs:73:35
 +   |
 +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:69:13
++  --> $DIR/unnecessary_lazy_eval.rs:74:28
 +   |
 +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:70:13
++  --> $DIR/unnecessary_lazy_eval.rs:77:13
 +   |
 +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:71:13
++  --> $DIR/unnecessary_lazy_eval.rs:78:13
 +   |
 +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:72:13
++  --> $DIR/unnecessary_lazy_eval.rs:79:13
 +   |
 +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:73:13
++  --> $DIR/unnecessary_lazy_eval.rs:80:13
 +   |
 +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:96:28
++  --> $DIR/unnecessary_lazy_eval.rs:81:13
 +   |
 +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:97:13
++  --> $DIR/unnecessary_lazy_eval.rs:105:28
 +   |
 +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:98:13
++  --> $DIR/unnecessary_lazy_eval.rs:106:13
 +   |
 +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:104:13
++  --> $DIR/unnecessary_lazy_eval.rs:107:13
 +   |
 +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:105:13
++  --> $DIR/unnecessary_lazy_eval.rs:113:13
 +   |
 +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:106:13
++  --> $DIR/unnecessary_lazy_eval.rs:114:13
 +   |
 +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:128:35
++  --> $DIR/unnecessary_lazy_eval.rs:115:13
 +   |
 +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:129:35
++  --> $DIR/unnecessary_lazy_eval.rs:137:35
 +   |
 +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:130:35
++  --> $DIR/unnecessary_lazy_eval.rs:138:35
 +   |
 +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:132:35
++  --> $DIR/unnecessary_lazy_eval.rs:139:35
 +   |
 +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:133:35
++  --> $DIR/unnecessary_lazy_eval.rs:141:35
 +   |
 +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:134:35
++  --> $DIR/unnecessary_lazy_eval.rs:142:35
 +   |
 +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:135:35
++  --> $DIR/unnecessary_lazy_eval.rs:143:35
 +   |
 +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`
- LL | |         // some lines
- LL | |         // some lines
- LL | |         // some lines
++  --> $DIR/unnecessary_lazy_eval.rs:144:35
 +   |
 +LL |       let _: Result<usize, usize> = res.
 +   |  ___________________________________^
- 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 | |     // 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))`
 +
 +error: aborting due to 34 previous errors
 +
index bf0ec8deb3458bccdc65a4151a8237241f6a0626,0000000000000000000000000000000000000000..d37163570abe07ee61fba6db3942e538e6382601
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,88 @@@
 +// run-rustfix
 +
 +#![feature(box_syntax)]
 +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
 +#![warn(clippy::unnecessary_operation)]
 +
 +struct Tuple(i32);
 +struct Struct {
 +    field: i32,
 +}
 +enum Enum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +struct DropStruct {
 +    field: i32,
 +}
 +impl Drop for DropStruct {
 +    fn drop(&mut self) {}
 +}
 +struct DropTuple(i32);
 +impl Drop for DropTuple {
 +    fn drop(&mut self) {}
 +}
 +enum DropEnum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +impl Drop for DropEnum {
 +    fn drop(&mut self) {}
 +}
 +struct FooString {
 +    s: String,
 +}
 +
 +fn get_number() -> i32 {
 +    0
 +}
 +
 +fn get_usize() -> usize {
 +    0
 +}
 +fn get_struct() -> Struct {
 +    Struct { field: 0 }
 +}
 +fn get_drop_struct() -> DropStruct {
 +    DropStruct { field: 0 }
 +}
 +
 +fn main() {
 +    get_number();
 +    get_number();
 +    get_struct();
 +    get_number();
 +    get_number();
 +    5;get_number();
 +    get_number();
 +    get_number();
 +    5;6;get_number();
 +    get_number();
 +    get_number();
 +    get_number();
 +    5;get_number();
 +    42;get_number();
 +    assert!([42, 55].len() > get_usize());
 +    42;get_number();
 +    get_number();
 +    assert!([42; 55].len() > get_usize());
 +    get_number();
 +    String::from("blah");
 +
 +    // Do not warn
 +    DropTuple(get_number());
 +    DropStruct { field: get_number() };
 +    DropStruct { field: get_number() };
 +    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());
 +}
index 08cb9ab522ee0d7f22ea647908102b06b73b36a7,0000000000000000000000000000000000000000..a14fd4bca0efde72bd30a1acec726665cdb6f9d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,83 -1,0 +1,92 @@@
 +// run-rustfix
 +
 +#![feature(box_syntax)]
 +#![allow(clippy::deref_addrof, dead_code, unused, clippy::no_effect)]
 +#![warn(clippy::unnecessary_operation)]
 +
 +struct Tuple(i32);
 +struct Struct {
 +    field: i32,
 +}
 +enum Enum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +struct DropStruct {
 +    field: i32,
 +}
 +impl Drop for DropStruct {
 +    fn drop(&mut self) {}
 +}
 +struct DropTuple(i32);
 +impl Drop for DropTuple {
 +    fn drop(&mut self) {}
 +}
 +enum DropEnum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +impl Drop for DropEnum {
 +    fn drop(&mut self) {}
 +}
 +struct FooString {
 +    s: String,
 +}
 +
 +fn get_number() -> i32 {
 +    0
 +}
 +
 +fn get_usize() -> usize {
 +    0
 +}
 +fn get_struct() -> Struct {
 +    Struct { field: 0 }
 +}
 +fn get_drop_struct() -> DropStruct {
 +    DropStruct { field: 0 }
 +}
 +
 +fn main() {
 +    Tuple(get_number());
 +    Struct { field: get_number() };
 +    Struct { ..get_struct() };
 +    Enum::Tuple(get_number());
 +    Enum::Struct { field: get_number() };
 +    5 + get_number();
 +    *&get_number();
 +    &get_number();
 +    (5, 6, get_number());
 +    box get_number();
 +    get_number()..;
 +    ..get_number();
 +    5..get_number();
 +    [42, get_number()];
 +    [42, 55][get_usize()];
 +    (42, get_number()).1;
 +    [get_number(); 55];
 +    [42; 55][get_usize()];
 +    {
 +        get_number()
 +    };
 +    FooString {
 +        s: String::from("blah"),
 +    };
 +
 +    // Do not warn
 +    DropTuple(get_number());
 +    DropStruct { field: get_number() };
 +    DropStruct { field: get_number() };
 +    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());
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7fefea7051d698ce92aab8e5c9dc6b48242e2564
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7b2af67d64c7b9a7f1037dda39f4b25fc68a57ae
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,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
++
index fe09aad06bc84d792c10d99a04e8fdd37f05bdbd,0000000000000000000000000000000000000000..ddeda795f81793e8d70d7109a16c958a4821f7f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,428 -1,0 +1,456 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
 +#![allow(clippy::needless_borrow, clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
-     #![clippy::msrv = "1.35"]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str));
 +    require_c_str(c_str);
 +
 +    require_os_str(os_str);
 +    require_os_str(&Cow::from(os_str));
 +    require_os_str(os_str);
 +
 +    require_path(path);
 +    require_path(&Cow::from(path));
 +    require_path(path);
 +
 +    require_str(s);
 +    require_str(&Cow::from(s));
 +    require_str(s);
 +    require_str(x_ref.as_ref());
 +
 +    require_slice(slice);
 +    require_slice(&Cow::from(slice));
 +    require_slice(array.as_ref());
 +    require_slice(array_ref.as_ref());
 +    require_slice(slice);
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()));
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str);
 +    require_deref_os_str(os_str);
 +    require_deref_path(path);
 +    require_deref_str(s);
 +    require_deref_slice(slice);
 +
 +    require_impl_deref_c_str(c_str);
 +    require_impl_deref_os_str(os_str);
 +    require_impl_deref_path(path);
 +    require_impl_deref_str(s);
 +    require_impl_deref_slice(slice);
 +
 +    require_deref_str_slice(s, slice);
 +    require_deref_slice_str(slice, s);
 +
 +    require_as_ref_c_str(c_str);
 +    require_as_ref_os_str(os_str);
 +    require_as_ref_path(path);
 +    require_as_ref_str(s);
 +    require_as_ref_str(&x);
 +    require_as_ref_slice(array);
 +    require_as_ref_slice(array_ref);
 +    require_as_ref_slice(slice);
 +
 +    require_impl_as_ref_c_str(c_str);
 +    require_impl_as_ref_os_str(os_str);
 +    require_impl_as_ref_path(path);
 +    require_impl_as_ref_str(s);
 +    require_impl_as_ref_str(&x);
 +    require_impl_as_ref_slice(array);
 +    require_impl_as_ref_slice(array_ref);
 +    require_impl_as_ref_slice(slice);
 +
 +    require_as_ref_str_slice(s, array);
 +    require_as_ref_str_slice(s, array_ref);
 +    require_as_ref_str_slice(s, slice);
 +    require_as_ref_slice_str(array, s);
 +    require_as_ref_slice_str(array_ref, s);
 +    require_as_ref_slice_str(slice, s);
 +
 +    let _ = x.join(x_ref);
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap());
 +    require_os_str(&OsString::from("x"));
 +    require_path(&std::path::PathBuf::from("x"));
 +    require_str(&String::from("x"));
 +    require_slice(&[String::from("x")]);
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types {
 +        let path = match get_file_path(t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
++#[clippy::msrv = "1.35"]
 +fn _msrv_1_35() {
-     #![clippy::msrv = "1.36"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].iter().cloned();
 +}
 +
++#[clippy::msrv = "1.36"]
 +fn _msrv_1_36() {
 +    let _ = &["x"][..].iter().copied();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_9317 {
 +    #![allow(dead_code)]
 +
 +    struct Bytes {}
 +
 +    impl ToString for Bytes {
 +        fn to_string(&self) -> String {
 +            "123".to_string()
 +        }
 +    }
 +
 +    impl AsRef<[u8]> for Bytes {
 +        fn as_ref(&self) -> &[u8] {
 +            &[1, 2, 3]
 +        }
 +    }
 +
 +    fn consume<C: AsRef<[u8]>>(c: C) {
 +        let _ = c;
 +    }
 +
 +    pub fn main() {
 +        let b = Bytes {};
 +        // Should not lint.
 +        consume(b.to_string());
 +    }
 +}
 +
 +mod issue_9351 {
 +    #![allow(dead_code)]
 +
 +    use std::ops::Deref;
 +    use std::path::{Path, PathBuf};
 +
 +    fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {}
 +
 +    fn id<T: AsRef<str>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn predicates_are_satisfied(_x: impl std::fmt::Write) {}
 +
 +    // Should lint
 +    fn single_return() -> impl AsRef<str> {
 +        id("abc")
 +    }
 +
 +    // Should not lint
 +    fn multiple_returns(b: bool) -> impl AsRef<str> {
 +        if b {
 +            return String::new();
 +        }
 +
 +        id("abc".to_string())
 +    }
 +
 +    struct S1(String);
 +
 +    // Should not lint
 +    fn fields1() -> S1 {
 +        S1(id("abc".to_string()))
 +    }
 +
 +    struct S2 {
 +        s: String,
 +    }
 +
 +    // Should not lint
 +    fn fields2() {
 +        let mut s = S2 { s: "abc".into() };
 +        s.s = id("abc".to_string());
 +    }
 +
 +    pub fn main() {
 +        let path = std::path::Path::new("x");
 +        let path_buf = path.to_owned();
 +
 +        // Should not lint.
 +        let _x: PathBuf = require_deref_path(path.to_owned());
 +        generic_arg_used_elsewhere(path.to_owned(), path_buf);
 +        predicates_are_satisfied(id("abc".to_string()));
 +    }
 +}
 +
 +mod issue_9504 {
 +    #![allow(dead_code)]
 +
 +    async fn foo<S: AsRef<str>>(_: S) {}
 +    async fn bar() {
 +        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())
++    }
++}
index 3de6d0903c0f1d0e083fa5de7fc89cfc331fe1a1,0000000000000000000000000000000000000000..95d2576733cd75ea8e42bf0ea127fb8983f7e451
mode 100644,000000..100644
--- /dev/null
@@@ -1,428 -1,0 +1,456 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +
 +#![allow(clippy::needless_borrow, clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
-     #![clippy::msrv = "1.35"]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str).into_owned());
 +    require_c_str(&c_str.to_owned());
 +
 +    require_os_str(&os_str.to_os_string());
 +    require_os_str(&Cow::from(os_str).into_owned());
 +    require_os_str(&os_str.to_owned());
 +
 +    require_path(&path.to_path_buf());
 +    require_path(&Cow::from(path).into_owned());
 +    require_path(&path.to_owned());
 +
 +    require_str(&s.to_string());
 +    require_str(&Cow::from(s).into_owned());
 +    require_str(&s.to_owned());
 +    require_str(&x_ref.to_string());
 +
 +    require_slice(&slice.to_vec());
 +    require_slice(&Cow::from(slice).into_owned());
 +    require_slice(&array.to_owned());
 +    require_slice(&array_ref.to_owned());
 +    require_slice(&slice.to_owned());
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str.to_owned());
 +    require_deref_os_str(os_str.to_owned());
 +    require_deref_path(path.to_owned());
 +    require_deref_str(s.to_owned());
 +    require_deref_slice(slice.to_owned());
 +
 +    require_impl_deref_c_str(c_str.to_owned());
 +    require_impl_deref_os_str(os_str.to_owned());
 +    require_impl_deref_path(path.to_owned());
 +    require_impl_deref_str(s.to_owned());
 +    require_impl_deref_slice(slice.to_owned());
 +
 +    require_deref_str_slice(s.to_owned(), slice.to_owned());
 +    require_deref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    require_as_ref_c_str(c_str.to_owned());
 +    require_as_ref_os_str(os_str.to_owned());
 +    require_as_ref_path(path.to_owned());
 +    require_as_ref_str(s.to_owned());
 +    require_as_ref_str(x.to_owned());
 +    require_as_ref_slice(array.to_owned());
 +    require_as_ref_slice(array_ref.to_owned());
 +    require_as_ref_slice(slice.to_owned());
 +
 +    require_impl_as_ref_c_str(c_str.to_owned());
 +    require_impl_as_ref_os_str(os_str.to_owned());
 +    require_impl_as_ref_path(path.to_owned());
 +    require_impl_as_ref_str(s.to_owned());
 +    require_impl_as_ref_str(x.to_owned());
 +    require_impl_as_ref_slice(array.to_owned());
 +    require_impl_as_ref_slice(array_ref.to_owned());
 +    require_impl_as_ref_slice(slice.to_owned());
 +
 +    require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +    require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    let _ = x.join(&x_ref.to_string());
 +
 +    let _ = slice.to_vec().into_iter();
 +    let _ = slice.to_owned().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
 +
 +    let _ = IntoIterator::into_iter(slice.to_vec());
 +    let _ = IntoIterator::into_iter(slice.to_owned());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +    require_os_str(&OsString::from("x").to_os_string());
 +    require_path(&std::path::PathBuf::from("x").to_path_buf());
 +    require_str(&String::from("x").to_string());
 +    require_slice(&[String::from("x")].to_owned());
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types.to_vec() {
 +        let path = match get_file_path(&t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
++#[clippy::msrv = "1.35"]
 +fn _msrv_1_35() {
-     #![clippy::msrv = "1.36"]
 +    // `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() {
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y.to_string()))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_9317 {
 +    #![allow(dead_code)]
 +
 +    struct Bytes {}
 +
 +    impl ToString for Bytes {
 +        fn to_string(&self) -> String {
 +            "123".to_string()
 +        }
 +    }
 +
 +    impl AsRef<[u8]> for Bytes {
 +        fn as_ref(&self) -> &[u8] {
 +            &[1, 2, 3]
 +        }
 +    }
 +
 +    fn consume<C: AsRef<[u8]>>(c: C) {
 +        let _ = c;
 +    }
 +
 +    pub fn main() {
 +        let b = Bytes {};
 +        // Should not lint.
 +        consume(b.to_string());
 +    }
 +}
 +
 +mod issue_9351 {
 +    #![allow(dead_code)]
 +
 +    use std::ops::Deref;
 +    use std::path::{Path, PathBuf};
 +
 +    fn require_deref_path<T: Deref<Target = std::path::Path>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn generic_arg_used_elsewhere<T: AsRef<Path>>(_x: T, _y: T) {}
 +
 +    fn id<T: AsRef<str>>(x: T) -> T {
 +        x
 +    }
 +
 +    fn predicates_are_satisfied(_x: impl std::fmt::Write) {}
 +
 +    // Should lint
 +    fn single_return() -> impl AsRef<str> {
 +        id("abc".to_string())
 +    }
 +
 +    // Should not lint
 +    fn multiple_returns(b: bool) -> impl AsRef<str> {
 +        if b {
 +            return String::new();
 +        }
 +
 +        id("abc".to_string())
 +    }
 +
 +    struct S1(String);
 +
 +    // Should not lint
 +    fn fields1() -> S1 {
 +        S1(id("abc".to_string()))
 +    }
 +
 +    struct S2 {
 +        s: String,
 +    }
 +
 +    // Should not lint
 +    fn fields2() {
 +        let mut s = S2 { s: "abc".into() };
 +        s.s = id("abc".to_string());
 +    }
 +
 +    pub fn main() {
 +        let path = std::path::Path::new("x");
 +        let path_buf = path.to_owned();
 +
 +        // Should not lint.
 +        let _x: PathBuf = require_deref_path(path.to_owned());
 +        generic_arg_used_elsewhere(path.to_owned(), path_buf);
 +        predicates_are_satisfied(id("abc".to_string()));
 +    }
 +}
 +
 +mod issue_9504 {
 +    #![allow(dead_code)]
 +
 +    async fn foo<S: AsRef<str>>(_: S) {}
 +    async fn bar() {
 +        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())
++    }
++}
index 02bf45a33fbe1913ed1adaaee6fe7bddb4f7259b,0000000000000000000000000000000000000000..4918fe35598602f8c3b19eb45c1b6f16c2053d48
mode 100644,000000..100644
--- /dev/null
@@@ -1,519 -1,0 +1,519 @@@
-   --> $DIR/unnecessary_to_owned.rs:151:64
 +error: redundant clone
-   --> $DIR/unnecessary_to_owned.rs:151:20
++  --> $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:152:40
++  --> $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:21
++  --> $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:153:48
++  --> $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:19
++  --> $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:154:35
++  --> $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:18
++  --> $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:155:39
++  --> $DIR/unnecessary_to_owned.rs:153:18
 +   |
 +LL |     require_str(&String::from("x").to_string());
 +   |                  ^^^^^^^^^^^^^^^^^
 +
 +error: redundant clone
-   --> $DIR/unnecessary_to_owned.rs:155:20
++  --> $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:60:36
++  --> $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:61:19
++  --> $DIR/unnecessary_to_owned.rs:59:36
 +   |
 +LL |     require_c_str(&Cow::from(c_str).into_owned());
 +   |                                    ^^^^^^^^^^^^^ help: remove this
 +   |
 +   = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
 +
 +error: unnecessary use of `to_owned`
-   --> $DIR/unnecessary_to_owned.rs:63:20
++  --> $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:64:38
++  --> $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:65:20
++  --> $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:67:18
++  --> $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:68:34
++  --> $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:69:18
++  --> $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:71:17
++  --> $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:72:30
++  --> $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:73:17
++  --> $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:74: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:76:19
++  --> $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:77:36
++  --> $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:78:19
++  --> $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:79: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:80: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:83:42
++  --> $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:86:25
++  --> $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:87:26
++  --> $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:88:24
++  --> $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:89:23
++  --> $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:90:25
++  --> $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:92:30
++  --> $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:93:31
++  --> $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:94:29
++  --> $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:95:28
++  --> $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:96:30
++  --> $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:98:29
++  --> $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:43
++  --> $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:99:29
++  --> $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:47
++  --> $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:101:26
++  --> $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:102:27
++  --> $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:103:25
++  --> $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:104:24
++  --> $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:105: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:106:26
++  --> $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:107: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:108: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:110:31
++  --> $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:111:32
++  --> $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:112:30
++  --> $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:113:29
++  --> $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:114: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:115:31
++  --> $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:116: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:117: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:119:30
++  --> $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:44
++  --> $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:120:30
++  --> $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:44
++  --> $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:121:30
++  --> $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:44
++  --> $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:122:30
++  --> $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:48
++  --> $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:123:30
++  --> $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:52
++  --> $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:124:30
++  --> $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:48
++  --> $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:126:20
++  --> $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:128:13
++  --> $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:129: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:130: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:131: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:133: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:134: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:135: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:136: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:198:14
++  --> $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:221:14
++  --> $DIR/unnecessary_to_owned.rs:197:14
 +   |
 +LL |     for t in file_types.to_vec() {
 +   |              ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: use
 +   |
 +LL |     for t in file_types {
 +   |              ~~~~~~~~~~
 +help: remove this `&`
 +   |
 +LL -         let path = match get_file_path(&t) {
 +LL +         let path = match get_file_path(t) {
 +   |
 +
 +error: unnecessary use of `to_vec`
-   --> $DIR/unnecessary_to_owned.rs:226: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:273:24
++  --> $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:381:12
++  --> $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:380:12
 +   |
 +LL |         id("abc".to_string())
 +   |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
 +
 +error: aborting due to 79 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c160e31afd33b82200b72515da2c6eb9e1ffa09e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,149 @@@
++// aux-build:doc_unsafe_macros.rs
++
++#![allow(clippy::let_unit_value)]
++#![warn(clippy::unnecessary_safety_doc)]
++
++#[macro_use]
++extern crate doc_unsafe_macros;
++
++/// This is has no safety section, and does not need one either
++pub fn destroy_the_planet() {
++    unimplemented!();
++}
++
++/// This one does not need a `Safety` section
++///
++/// # Safety
++///
++/// This function shouldn't be called unless the horsemen are ready
++pub fn apocalypse(universe: &mut ()) {
++    unimplemented!();
++}
++
++/// This is a private function, skip to match behavior with `missing_safety_doc`.
++///
++/// # Safety
++///
++/// Boo!
++fn you_dont_see_me() {
++    unimplemented!();
++}
++
++mod private_mod {
++    /// This is public but unexported function, skip to match behavior with `missing_safety_doc`.
++    ///
++    /// # Safety
++    ///
++    /// Very safe!
++    pub fn only_crate_wide_accessible() {
++        unimplemented!();
++    }
++
++    /// # Safety
++    ///
++    /// Unnecessary safety!
++    pub fn republished() {
++        unimplemented!();
++    }
++}
++
++pub use private_mod::republished;
++
++pub trait SafeTraitSafeMethods {
++    fn woefully_underdocumented(self);
++
++    /// # Safety
++    ///
++    /// Unnecessary!
++    fn documented(self);
++}
++
++pub trait SafeTrait {
++    fn method();
++}
++
++/// # Safety
++///
++/// Unnecessary!
++pub trait DocumentedSafeTrait {
++    fn method2();
++}
++
++pub struct Struct;
++
++impl SafeTraitSafeMethods for Struct {
++    fn woefully_underdocumented(self) {
++        // all is well
++    }
++
++    fn documented(self) {
++        // all is still well
++    }
++}
++
++impl SafeTrait for Struct {
++    fn method() {}
++}
++
++impl DocumentedSafeTrait for Struct {
++    fn method2() {}
++}
++
++impl Struct {
++    /// # Safety
++    ///
++    /// Unnecessary!
++    pub fn documented() -> Self {
++        unimplemented!();
++    }
++
++    pub fn undocumented(&self) {
++        unimplemented!();
++    }
++
++    /// Private, fine again to stay consistent with `missing_safety_doc`.
++    ///
++    /// # Safety
++    ///
++    /// Unnecessary!
++    fn private(&self) {
++        unimplemented!();
++    }
++}
++
++macro_rules! very_safe {
++    () => {
++        pub fn whee() {
++            unimplemented!()
++        }
++
++        /// # Safety
++        ///
++        /// Driving is very safe already!
++        pub fn drive() {
++            whee()
++        }
++    };
++}
++
++very_safe!();
++
++// we don't lint code from external macros
++undocd_safe!();
++
++fn main() {}
++
++// do not lint if any parent has `#[doc(hidden)]` attribute
++// see #7347
++#[doc(hidden)]
++pub mod __macro {
++    pub struct T;
++    impl T {
++        pub unsafe fn f() {}
++    }
++}
++
++/// # Implementation safety
++pub trait DocumentedSafeTraitWithImplementationHeader {
++    fn method();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..72898c93fa11395664aca11e162e7b3757a72c85
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/unnecessary_unsafety_doc.rs:19:1
++   |
++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/unnecessary_unsafety_doc.rs:45:5
++   |
++LL |     pub fn republished() {
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/unnecessary_unsafety_doc.rs:58:5
++   |
++LL |     fn documented(self);
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: docs for safe trait have unnecessary `# Safety` section
++  --> $DIR/unnecessary_unsafety_doc.rs:68:1
++   |
++LL | pub trait DocumentedSafeTrait {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/unnecessary_unsafety_doc.rs:96:5
++   |
++LL |     pub fn documented() -> Self {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/unnecessary_unsafety_doc.rs:123:9
++   |
++LL |         pub fn drive() {
++   |         ^^^^^^^^^^^^^^
++...
++LL | very_safe!();
++   | ------------ in this macro invocation
++   |
++   = 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/unnecessary_unsafety_doc.rs:147:1
++   |
++LL | pub trait DocumentedSafeTraitWithImplementationHeader {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 9786c7b12128ba811fb0298222d3e48b821eed9f,0000000000000000000000000000000000000000..0a8e7b34dfa4fcdd622b742c45b94a1b3d7ae939
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,45 @@@
- #![feature(box_patterns, custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.52"]
++#![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)]
 +
 +fn main() {
 +    // Should be ignored by this lint, as nesting requires more characters.
 +    if let &0 | &2 = &0 {}
 +
 +    if let box (0 | 2) = Box::new(0) {}
 +    if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
 +    const C0: Option<u8> = Some(1);
 +    if let Some(1 | 2) | C0 = None {}
 +    if let &mut (0 | 2) = &mut 0 {}
 +    if let x @ (0 | 2) = 0 {}
 +    if let (0, 1 | 2 | 3) = (0, 0) {}
 +    if let (1 | 2 | 3, 0) = (0, 0) {}
 +    if let (x, ..) | (x, 1 | 2) = (0, 1) {}
 +    if let [0 | 1] = [0] {}
 +    if let [x, 0 | 1] = [0, 1] {}
 +    if let [x, 0 | 1 | 2] = [0, 1] {}
 +    if let [x, ..] | [x, 1 | 2] = [0, 1] {}
 +    struct TS(u8, u8);
 +    if let TS(0 | 1, x) = TS(0, 0) {}
 +    if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
 +    if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
 +    struct S {
 +        x: u8,
 +        y: u8,
 +    }
 +    if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
 +    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.53"]
 +    if let [1] | [52] = [0] {}
 +}
 +
++#[clippy::msrv = "1.53"]
 +fn msrv_1_53() {
 +    if let [1 | 53] = [0] {}
 +}
index f57322396d4ac242c09efab9a7c9bdde7e8779cb,0000000000000000000000000000000000000000..2c454adfe89db0066ebd5df473c9c0c898005524
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,45 @@@
- #![feature(box_patterns, custom_inner_attributes)]
 +// run-rustfix
 +
-     #![clippy::msrv = "1.52"]
++#![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)]
 +
 +fn main() {
 +    // Should be ignored by this lint, as nesting requires more characters.
 +    if let &0 | &2 = &0 {}
 +
 +    if let box 0 | box 2 = Box::new(0) {}
 +    if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
 +    const C0: Option<u8> = Some(1);
 +    if let Some(1) | C0 | Some(2) = None {}
 +    if let &mut 0 | &mut 2 = &mut 0 {}
 +    if let x @ 0 | x @ 2 = 0 {}
 +    if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
 +    if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
 +    if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
 +    if let [0] | [1] = [0] {}
 +    if let [x, 0] | [x, 1] = [0, 1] {}
 +    if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
 +    if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
 +    struct TS(u8, u8);
 +    if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 +    if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
 +    if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
 +    struct S {
 +        x: u8,
 +        y: u8,
 +    }
 +    if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 +    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.53"]
 +    if let [1] | [52] = [0] {}
 +}
 +
++#[clippy::msrv = "1.53"]
 +fn msrv_1_53() {
 +    if let [1] | [53] = [0] {}
 +}
index fbc12fff0b0e7dccc8d4c89aab51a408b499ea83,0000000000000000000000000000000000000000..a1f193db555ad12f26fc5729490ec59d454b5758
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,190 @@@
-   --> $DIR/unnested_or_patterns.rs:46:12
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:12:12
 +   |
 +LL |     if let box 0 | box 2 = Box::new(0) {}
 +   |            ^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::unnested-or-patterns` implied by `-D warnings`
 +help: nest the patterns
 +   |
 +LL |     if let box (0 | 2) = Box::new(0) {}
 +   |            ~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:13:12
 +   |
 +LL |     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
 +   |            ~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:15:12
 +   |
 +LL |     if let Some(1) | C0 | Some(2) = None {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let Some(1 | 2) | C0 = None {}
 +   |            ~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:16:12
 +   |
 +LL |     if let &mut 0 | &mut 2 = &mut 0 {}
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let &mut (0 | 2) = &mut 0 {}
 +   |            ~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:17:12
 +   |
 +LL |     if let x @ 0 | x @ 2 = 0 {}
 +   |            ^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let x @ (0 | 2) = 0 {}
 +   |            ~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:18:12
 +   |
 +LL |     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let (0, 1 | 2 | 3) = (0, 0) {}
 +   |            ~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:19:12
 +   |
 +LL |     if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let (1 | 2 | 3, 0) = (0, 0) {}
 +   |            ~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:20:12
 +   |
 +LL |     if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let (x, ..) | (x, 1 | 2) = (0, 1) {}
 +   |            ~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:21:12
 +   |
 +LL |     if let [0] | [1] = [0] {}
 +   |            ^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [0 | 1] = [0] {}
 +   |            ~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:22:12
 +   |
 +LL |     if let [x, 0] | [x, 1] = [0, 1] {}
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [x, 0 | 1] = [0, 1] {}
 +   |            ~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:23:12
 +   |
 +LL |     if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [x, 0 | 1 | 2] = [0, 1] {}
 +   |            ~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:24:12
 +   |
 +LL |     if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [x, ..] | [x, 1 | 2] = [0, 1] {}
 +   |            ~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:26:12
 +   |
 +LL |     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let TS(0 | 1, x) = TS(0, 0) {}
 +   |            ~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:27:12
 +   |
 +LL |     if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
 +   |            ~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:28:12
 +   |
 +LL |     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
 +   |            ~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
 +  --> $DIR/unnested_or_patterns.rs:33:12
 +   |
 +LL |     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
 +   |            ~~~~~~~~~~~~~~~~~
 +
 +error: unnested or-patterns
++  --> $DIR/unnested_or_patterns.rs:44:12
 +   |
 +LL |     if let [1] | [53] = [0] {}
 +   |            ^^^^^^^^^^
 +   |
 +help: nest the patterns
 +   |
 +LL |     if let [1 | 53] = [0] {}
 +   |            ~~~~~~~~
 +
 +error: aborting due to 17 previous errors
 +
index 38fe6c34cfec177e90a1f644cc215a200e476ceb,0000000000000000000000000000000000000000..f6f734c05ed59e95c06401d13f7faa393c4a9051
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,17 @@@
 +// run-rustfix
 +#![warn(clippy::unused_rounding)]
 +
 +fn main() {
 +    let _ = 1f32;
 +    let _ = 1.0f64;
 +    let _ = 1.00f32;
 +    let _ = 2e-54f64.floor();
 +
 +    // issue9866
 +    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();
 +}
index a5cac64d023ae1db417a8ac95f61436cee6e5cb8,0000000000000000000000000000000000000000..a0267d8144aabb45c462280e45bb29f46ed81d03
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,17 @@@
 +// run-rustfix
 +#![warn(clippy::unused_rounding)]
 +
 +fn main() {
 +    let _ = 1f32.ceil();
 +    let _ = 1.0f64.floor();
 +    let _ = 1.00f32.round();
 +    let _ = 2e-54f64.floor();
 +
 +    // issue9866
 +    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();
 +}
index 1eeb5d1de883212f1dcc1aa25a3a4c21defbca5d,0000000000000000000000000000000000000000..b867996fe5763c826e375a948796bf45e9bd0b0b
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,34 @@@
- error: aborting due to 4 previous errors
 +error: used the `ceil` method with a whole number float
 +  --> $DIR/unused_rounding.rs:5:13
 +   |
 +LL |     let _ = 1f32.ceil();
 +   |             ^^^^^^^^^^^ help: remove the `ceil` method call: `1f32`
 +   |
 +   = note: `-D clippy::unused-rounding` implied by `-D warnings`
 +
 +error: used the `floor` method with a whole number float
 +  --> $DIR/unused_rounding.rs:6:13
 +   |
 +LL |     let _ = 1.0f64.floor();
 +   |             ^^^^^^^^^^^^^^ help: remove the `floor` method call: `1.0f64`
 +
 +error: used the `round` method with a whole number float
 +  --> $DIR/unused_rounding.rs:7:13
 +   |
 +LL |     let _ = 1.00f32.round();
 +   |             ^^^^^^^^^^^^^^^ help: remove the `round` method call: `1.00f32`
 +
 +error: used the `round` method with a whole number float
 +  --> $DIR/unused_rounding.rs:13:13
 +   |
 +LL |     let _ = 3.0_f32.round();
 +   |             ^^^^^^^^^^^^^^^ help: remove the `round` method call: `3.0_f32`
 +
++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
 +
index 3b54fe9d5ff3d66e8d7074b7884a9367537e340d,0000000000000000000000000000000000000000..0a6166571ebe38c3f5c4bab85661b346f9751937
mode 100644,000000..100644
--- /dev/null
@@@ -1,652 -1,0 +1,649 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
-     #![clippy::msrv = "1.36"]
 +#![warn(clippy::use_self)]
 +#![allow(dead_code, unreachable_code)]
 +#![allow(
 +    clippy::should_implement_trait,
 +    clippy::upper_case_acronyms,
 +    clippy::from_over_into,
 +    clippy::self_named_constructors
 +)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            Self(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo;
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo;
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Self {
 +                    Self { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Self::B(42);
 +            let _ = Self::C { field: true };
 +            let _ = Self::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A;
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            Self::fun_1();
 +            Self::A;
 +
 +            Self {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct;
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            Self::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S;
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> Self {
 +            Self {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[Self::A..Self::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Self {
 +            Self { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo;
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo;
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B;
 +        pub struct C;
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            Self::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod self_is_ty_param {
 +    trait Trait {
 +        type Type;
 +        type Hi;
 +
 +        fn test();
 +    }
 +
 +    impl<I> Trait for I
 +    where
 +        I: Iterator,
 +        I::Item: Trait, // changing this to Self would require <Self as Iterator>
 +    {
 +        type Type = I;
 +        type Hi = I::Item;
 +
 +        fn test() {
 +            let _: I::Item;
 +            let _: I; // this could lint, but is questionable
 +        }
 +    }
 +}
 +
 +mod use_self_in_pat {
 +    enum Foo {
 +        Bar,
 +        Baz,
 +    }
 +
 +    impl Foo {
 +        fn do_stuff(self) {
 +            match self {
 +                Self::Bar => unimplemented!(),
 +                Self::Baz => unimplemented!(),
 +            }
 +            match Some(1) {
 +                Some(_) => unimplemented!(),
 +                None => unimplemented!(),
 +            }
 +            if let Self::Bar = self {
 +                unimplemented!()
 +            }
 +        }
 +    }
 +}
 +
 +mod issue8845 {
 +    pub enum Something {
 +        Num(u8),
 +        TupleNums(u8, u8),
 +        StructNums { one: u8, two: u8 },
 +    }
 +
 +    struct Foo(u8);
 +
 +    struct Bar {
 +        x: u8,
 +        y: usize,
 +    }
 +
 +    impl Something {
 +        fn get_value(&self) -> u8 {
 +            match self {
 +                Self::Num(n) => *n,
 +                Self::TupleNums(n, _m) => *n,
 +                Self::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            match self {
 +                Self::Num(n) => *n,
 +                Self::TupleNums(n, _m) => *n,
 +                Self::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn imported_values(&self) -> u8 {
 +            use Something::*;
 +            match self {
 +                Num(n) => *n,
 +                TupleNums(n, _m) => *n,
 +                StructNums { one, two: _ } => *one,
 +            }
 +        }
 +    }
 +
 +    impl Foo {
 +        fn get_value(&self) -> u8 {
 +            let Self(x) = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let Self(x) = self;
 +            *x
 +        }
 +    }
 +
 +    impl Bar {
 +        fn get_value(&self) -> u8 {
 +            let Self { x, .. } = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let Self { x, .. } = self;
 +            *x
 +        }
 +    }
 +}
 +
 +mod issue6902 {
 +    use serde::Serialize;
 +
 +    #[derive(Serialize)]
 +    pub enum Foo {
 +        Bar = 1,
 +    }
 +}
 +
++#[clippy::msrv = "1.36"]
 +fn msrv_1_36() {
-     #![clippy::msrv = "1.37"]
 +    enum E {
 +        A,
 +    }
 +
 +    impl E {
 +        fn foo(self) {
 +            match self {
 +                E::A => {},
 +            }
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.37"]
 +fn msrv_1_37() {
 +    enum E {
 +        A,
 +    }
 +
 +    impl E {
 +        fn foo(self) {
 +            match self {
 +                Self::A => {},
 +            }
 +        }
 +    }
 +}
index bf87633cd2d8f1b675552f2af3b5fae37ad5f191,0000000000000000000000000000000000000000..39c2b431f7fb95323766b0ed75ce269494881637
mode 100644,000000..100644
--- /dev/null
@@@ -1,652 -1,0 +1,649 @@@
- #![feature(custom_inner_attributes)]
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
-     #![clippy::msrv = "1.36"]
 +#![warn(clippy::use_self)]
 +#![allow(dead_code, unreachable_code)]
 +#![allow(
 +    clippy::should_implement_trait,
 +    clippy::upper_case_acronyms,
 +    clippy::from_over_into,
 +    clippy::self_named_constructors
 +)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +        fn test() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            TS(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo;
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo;
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Bar {
 +                    Bar { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Enum::B(42);
 +            let _ = Enum::C { field: true };
 +            let _ = Enum::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A;
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            nested::A::fun_1();
 +            nested::A::A;
 +
 +            nested::A {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct;
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            TestStruct::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S;
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> S {
 +            S {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[S::A..S::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Foo<T> {
 +            Foo::<T> { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo;
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo;
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B;
 +        pub struct C;
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            A::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            S2::new()
 +        }
 +    }
 +}
 +
 +mod self_is_ty_param {
 +    trait Trait {
 +        type Type;
 +        type Hi;
 +
 +        fn test();
 +    }
 +
 +    impl<I> Trait for I
 +    where
 +        I: Iterator,
 +        I::Item: Trait, // changing this to Self would require <Self as Iterator>
 +    {
 +        type Type = I;
 +        type Hi = I::Item;
 +
 +        fn test() {
 +            let _: I::Item;
 +            let _: I; // this could lint, but is questionable
 +        }
 +    }
 +}
 +
 +mod use_self_in_pat {
 +    enum Foo {
 +        Bar,
 +        Baz,
 +    }
 +
 +    impl Foo {
 +        fn do_stuff(self) {
 +            match self {
 +                Foo::Bar => unimplemented!(),
 +                Foo::Baz => unimplemented!(),
 +            }
 +            match Some(1) {
 +                Some(_) => unimplemented!(),
 +                None => unimplemented!(),
 +            }
 +            if let Foo::Bar = self {
 +                unimplemented!()
 +            }
 +        }
 +    }
 +}
 +
 +mod issue8845 {
 +    pub enum Something {
 +        Num(u8),
 +        TupleNums(u8, u8),
 +        StructNums { one: u8, two: u8 },
 +    }
 +
 +    struct Foo(u8);
 +
 +    struct Bar {
 +        x: u8,
 +        y: usize,
 +    }
 +
 +    impl Something {
 +        fn get_value(&self) -> u8 {
 +            match self {
 +                Something::Num(n) => *n,
 +                Something::TupleNums(n, _m) => *n,
 +                Something::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            match self {
 +                crate::issue8845::Something::Num(n) => *n,
 +                crate::issue8845::Something::TupleNums(n, _m) => *n,
 +                crate::issue8845::Something::StructNums { one, two: _ } => *one,
 +            }
 +        }
 +
 +        fn imported_values(&self) -> u8 {
 +            use Something::*;
 +            match self {
 +                Num(n) => *n,
 +                TupleNums(n, _m) => *n,
 +                StructNums { one, two: _ } => *one,
 +            }
 +        }
 +    }
 +
 +    impl Foo {
 +        fn get_value(&self) -> u8 {
 +            let Foo(x) = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let crate::issue8845::Foo(x) = self;
 +            *x
 +        }
 +    }
 +
 +    impl Bar {
 +        fn get_value(&self) -> u8 {
 +            let Bar { x, .. } = self;
 +            *x
 +        }
 +
 +        fn use_crate(&self) -> u8 {
 +            let crate::issue8845::Bar { x, .. } = self;
 +            *x
 +        }
 +    }
 +}
 +
 +mod issue6902 {
 +    use serde::Serialize;
 +
 +    #[derive(Serialize)]
 +    pub enum Foo {
 +        Bar = 1,
 +    }
 +}
 +
++#[clippy::msrv = "1.36"]
 +fn msrv_1_36() {
-     #![clippy::msrv = "1.37"]
 +    enum E {
 +        A,
 +    }
 +
 +    impl E {
 +        fn foo(self) {
 +            match self {
 +                E::A => {},
 +            }
 +        }
 +    }
 +}
 +
++#[clippy::msrv = "1.37"]
 +fn msrv_1_37() {
 +    enum E {
 +        A,
 +    }
 +
 +    impl E {
 +        fn foo(self) {
 +            match self {
 +                E::A => {},
 +            }
 +        }
 +    }
 +}
index 16fb0609242cbfea2724b8b548ef41f720898739,0000000000000000000000000000000000000000..48364c40c3b26007ca5b0d6049d9fa8168df407d
mode 100644,000000..100644
--- /dev/null
@@@ -1,256 -1,0 +1,256 @@@
-   --> $DIR/use_self.rs:23:21
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:24:13
++  --> $DIR/use_self.rs:22:21
 +   |
 +LL |         fn new() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +   = note: `-D clippy::use-self` implied by `-D warnings`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:26:22
++  --> $DIR/use_self.rs:23:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:27:13
++  --> $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:32:25
++  --> $DIR/use_self.rs:26:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:33:13
++  --> $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:98:24
++  --> $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:55
++  --> $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:113:13
++  --> $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:148:29
++  --> $DIR/use_self.rs:112:13
 +   |
 +LL |             TS(0)
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:149:21
++  --> $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:160: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:161:13
++  --> $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:178:21
++  --> $DIR/use_self.rs:160:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:179: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:180: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:222:13
++  --> $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:223: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:225: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:244: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:258:25
++  --> $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:259:13
++  --> $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:263:16
++  --> $DIR/use_self.rs:258:13
 +   |
 +LL |             S {}
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:263:22
++  --> $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:286:29
++  --> $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:287:13
++  --> $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:459: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:496: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:533:17
++  --> $DIR/use_self.rs:495:13
 +   |
 +LL |             S2::new()
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
-   --> $DIR/use_self.rs:534: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:540:20
++  --> $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:564:17
++  --> $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:565: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:566: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:572: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:573: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:574: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:590: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:595: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:602: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:607: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:648: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:645:17
 +   |
 +LL |                 E::A => {},
 +   |                 ^ help: use the applicable keyword: `Self`
 +
 +error: aborting due to 42 previous errors
 +
index 80c30393832c107b93b6c45b9033b3dbb9fd0799,0000000000000000000000000000000000000000..acb476ee69628efc987a0785bae0ddb36ea85b46
mode 100644,000000..100644
--- /dev/null
@@@ -1,12 -1,0 +1,29 @@@
- [assign]
 +[relabel]
 +allow-unauthenticated = [
 +    "A-*", "C-*", "E-*", "I-*", "L-*", "P-*", "S-*", "T-*",
 +    "good-first-issue"
 +]
 +
 +# 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",
++]