]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'f4850f7292efa33759b4f7f9b7621268979e9914' into clippyup
authorPhilipp Krones <hello@philkrones.com>
Mon, 21 Nov 2022 19:34:47 +0000 (20:34 +0100)
committerPhilipp Krones <hello@philkrones.com>
Mon, 21 Nov 2022 19:51:52 +0000 (20:51 +0100)
297 files changed:
1  2 
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CODE_OF_CONDUCT.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/Cargo.toml
src/tools/clippy/README.md
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/lint.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
src/tools/clippy/clippy_lints/src/declared_lints.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/disallowed_macros.rs
src/tools/clippy/clippy_lints/src/disallowed_methods.rs
src/tools/clippy/clippy_lints/src/disallowed_types.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/enum_variants.rs
src/tools/clippy/clippy_lints/src/equatable_if_let.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/excessive_bools.rs
src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
src/tools/clippy/clippy_lints/src/functions/mod.rs
src/tools/clippy/clippy_lints/src/functions/must_use.rs
src/tools/clippy/clippy_lints/src/functions/result.rs
src/tools/clippy/clippy_lints/src/implicit_hasher.rs
src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
src/tools/clippy/clippy_lints/src/indexing_slicing.rs
src/tools/clippy/clippy_lints/src/instant_subtraction.rs
src/tools/clippy/clippy_lints/src/int_plus_one.rs
src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/len_zero.rs
src/tools/clippy/clippy_lints/src/let_underscore.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/lifetimes.rs
src/tools/clippy/clippy_lints/src/loops/mod.rs
src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
src/tools/clippy/clippy_lints/src/loops/never_loop.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/map_unit_fn.rs
src/tools/clippy/clippy_lints/src/matches/infallible_destructuring_match.rs
src/tools/clippy/clippy_lints/src/matches/manual_filter.rs
src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
src/tools/clippy/clippy_lints/src/matches/single_match.rs
src/tools/clippy/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
src/tools/clippy/clippy_lints/src/methods/chars_last_cmp.rs
src/tools/clippy/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs
src/tools/clippy/clippy_lints/src/methods/chars_next_cmp.rs
src/tools/clippy/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs
src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
src/tools/clippy/clippy_lints/src/methods/expect_used.rs
src/tools/clippy/clippy_lints/src/methods/filter_map.rs
src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
src/tools/clippy/clippy_lints/src/methods/iter_nth_zero.rs
src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
src/tools/clippy/clippy_lints/src/methods/manual_saturating_arithmetic.rs
src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
src/tools/clippy/clippy_lints/src/methods/map_clone.rs
src/tools/clippy/clippy_lints/src/methods/map_collect_result_unit.rs
src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs
src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs
src/tools/clippy/clippy_lints/src/methods/string_extend_chars.rs
src/tools/clippy/clippy_lints/src/methods/suspicious_map.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_join.rs
src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs
src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs
src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
src/tools/clippy/clippy_lints/src/mut_key.rs
src/tools/clippy/clippy_lints/src/mut_mut.rs
src/tools/clippy/clippy_lints/src/needless_borrowed_ref.rs
src/tools/clippy/clippy_lints/src/needless_continue.rs
src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
src/tools/clippy/clippy_lints/src/octal_escapes.rs
src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
src/tools/clippy/clippy_lints/src/operators/op_ref.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
src/tools/clippy/clippy_lints/src/renamed_lints.rs
src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
src/tools/clippy/clippy_lints/src/swap.rs
src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
src/tools/clippy/clippy_lints/src/transmute/utils.rs
src/tools/clippy/clippy_lints/src/types/box_collection.rs
src/tools/clippy/clippy_lints/src/types/mod.rs
src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
src/tools/clippy/clippy_lints/src/types/vec_box.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs
src/tools/clippy/clippy_lints/src/unused_peekable.rs
src/tools/clippy/clippy_lints/src/unused_rounding.rs
src/tools/clippy/clippy_lints/src/unused_unit.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/interning_defined_symbol.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/diagnostics.rs
src/tools/clippy/clippy_utils/src/hir_utils.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/usage.rs
src/tools/clippy/declare_clippy_lint/Cargo.toml
src/tools/clippy/declare_clippy_lint/src/lib.rs
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/src/driver.rs
src/tools/clippy/src/main.rs
src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr
src/tools/clippy/tests/ui-internal/custom_ice_message.rs
src/tools/clippy/tests/ui-internal/custom_ice_message.stderr
src/tools/clippy/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
src/tools/clippy/tests/ui-toml/arithmetic_side_effects_allowed/arithmetic_side_effects_allowed.rs
src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
src/tools/clippy/tests/ui-toml/expect_used/expect_used.rs
src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr
src/tools/clippy/tests/ui-toml/mut_key/clippy.toml
src/tools/clippy/tests/ui-toml/mut_key/mut_key.rs
src/tools/clippy/tests/ui-toml/print_macro/clippy.toml
src/tools/clippy/tests/ui-toml/print_macro/print_macro.rs
src/tools/clippy/tests/ui-toml/print_macro/print_macro.stderr
src/tools/clippy/tests/ui-toml/toml_disallowed_methods/clippy.toml
src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs
src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
src/tools/clippy/tests/ui-toml/vec_box_sized/test.rs
src/tools/clippy/tests/ui-toml/vec_box_sized/test.stderr
src/tools/clippy/tests/ui/arithmetic_side_effects.rs
src/tools/clippy/tests/ui/arithmetic_side_effects.stderr
src/tools/clippy/tests/ui/auxiliary/doc_unsafe_macros.rs
src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.rs
src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
src/tools/clippy/tests/ui/bool_to_int_with_if.fixed
src/tools/clippy/tests/ui/bool_to_int_with_if.rs
src/tools/clippy/tests/ui/bool_to_int_with_if.stderr
src/tools/clippy/tests/ui/cognitive_complexity.rs
src/tools/clippy/tests/ui/cognitive_complexity.stderr
src/tools/clippy/tests/ui/crashes/ice-2774.stderr
src/tools/clippy/tests/ui/crashes/ice-9746.rs
src/tools/clippy/tests/ui/crashes/needless_lifetimes_impl_trait.stderr
src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs
src/tools/clippy/tests/ui/doc_errors.stderr
src/tools/clippy/tests/ui/doc_unnecessary_unsafe.rs
src/tools/clippy/tests/ui/doc_unnecessary_unsafe.stderr
src/tools/clippy/tests/ui/doc_unsafe.stderr
src/tools/clippy/tests/ui/eq_op.rs
src/tools/clippy/tests/ui/eq_op.stderr
src/tools/clippy/tests/ui/equatable_if_let.fixed
src/tools/clippy/tests/ui/equatable_if_let.rs
src/tools/clippy/tests/ui/equatable_if_let.stderr
src/tools/clippy/tests/ui/expect.stderr
src/tools/clippy/tests/ui/explicit_auto_deref.fixed
src/tools/clippy/tests/ui/explicit_auto_deref.rs
src/tools/clippy/tests/ui/fn_params_excessive_bools.rs
src/tools/clippy/tests/ui/fn_params_excessive_bools.stderr
src/tools/clippy/tests/ui/from_raw_with_void_ptr.rs
src/tools/clippy/tests/ui/from_raw_with_void_ptr.stderr
src/tools/clippy/tests/ui/get_unwrap.stderr
src/tools/clippy/tests/ui/infallible_destructuring_match.fixed
src/tools/clippy/tests/ui/infallible_destructuring_match.rs
src/tools/clippy/tests/ui/infallible_destructuring_match.stderr
src/tools/clippy/tests/ui/issue_4266.stderr
src/tools/clippy/tests/ui/let_underscore_future.rs
src/tools/clippy/tests/ui/let_underscore_future.stderr
src/tools/clippy/tests/ui/let_underscore_lock.rs
src/tools/clippy/tests/ui/let_underscore_lock.stderr
src/tools/clippy/tests/ui/let_underscore_must_use.stderr
src/tools/clippy/tests/ui/manual_flatten.rs
src/tools/clippy/tests/ui/manual_instant_elapsed.fixed
src/tools/clippy/tests/ui/manual_instant_elapsed.rs
src/tools/clippy/tests/ui/manual_instant_elapsed.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_let_else_match.rs
src/tools/clippy/tests/ui/manual_let_else_match.stderr
src/tools/clippy/tests/ui/manual_ok_or.fixed
src/tools/clippy/tests/ui/manual_ok_or.rs
src/tools/clippy/tests/ui/manual_ok_or.stderr
src/tools/clippy/tests/ui/map_flatten_fixable.fixed
src/tools/clippy/tests/ui/map_flatten_fixable.rs
src/tools/clippy/tests/ui/map_flatten_fixable.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/missing_panics_doc.stderr
src/tools/clippy/tests/ui/mut_from_ref.rs
src/tools/clippy/tests/ui/mut_mut.rs
src/tools/clippy/tests/ui/mut_range_bound.rs
src/tools/clippy/tests/ui/mut_range_bound.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_borrowed_ref.fixed
src/tools/clippy/tests/ui/needless_borrowed_ref.rs
src/tools/clippy/tests/ui/needless_borrowed_ref.stderr
src/tools/clippy/tests/ui/needless_collect.fixed
src/tools/clippy/tests/ui/needless_collect.rs
src/tools/clippy/tests/ui/needless_collect.stderr
src/tools/clippy/tests/ui/needless_collect_indirect.rs
src/tools/clippy/tests/ui/needless_collect_indirect.stderr
src/tools/clippy/tests/ui/needless_lifetimes.rs
src/tools/clippy/tests/ui/needless_lifetimes.stderr
src/tools/clippy/tests/ui/never_loop.rs
src/tools/clippy/tests/ui/never_loop.stderr
src/tools/clippy/tests/ui/new_ret_no_self.rs
src/tools/clippy/tests/ui/new_ret_no_self.stderr
src/tools/clippy/tests/ui/option_if_let_else.fixed
src/tools/clippy/tests/ui/option_if_let_else.rs
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/question_mark.stderr
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/result_large_err.rs
src/tools/clippy/tests/ui/result_large_err.stderr
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/single_component_path_imports.stderr
src/tools/clippy/tests/ui/single_component_path_imports_nested_first.stderr
src/tools/clippy/tests/ui/string_extend.fixed
src/tools/clippy/tests/ui/string_extend.rs
src/tools/clippy/tests/ui/string_extend.stderr
src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.rs
src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr
src/tools/clippy/tests/ui/swap.fixed
src/tools/clippy/tests/ui/swap.rs
src/tools/clippy/tests/ui/transmute.rs
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
src/tools/clippy/tests/ui/unchecked_duration_subtraction.fixed
src/tools/clippy/tests/ui/unchecked_duration_subtraction.rs
src/tools/clippy/tests/ui/unchecked_duration_subtraction.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/unnecessary_join.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/unused_unit.fixed
src/tools/clippy/tests/ui/unused_unit.rs
src/tools/clippy/tests/ui/unused_unit.stderr
src/tools/clippy/tests/ui/unwrap.stderr
src/tools/clippy/tests/ui/unwrap_expect_used.stderr
src/tools/clippy/tests/ui/unwrap_or.rs
src/tools/clippy/tests/ui/use_self_trait.fixed
src/tools/clippy/tests/ui/use_self_trait.rs
src/tools/clippy/tests/ui/use_self_trait.stderr
src/tools/clippy/tests/ui/useless_attribute.fixed
src/tools/clippy/tests/ui/useless_attribute.rs
src/tools/clippy/tests/ui/useless_attribute.stderr
src/tools/clippy/tests/versioncheck.rs

index 2d7bda27e4fcccf4773d21d4e016bb728f30044d,0000000000000000000000000000000000000000..6f1f73c1fd2fe385e06badeb46983a8860864c44
mode 100644,000000..100644
--- /dev/null
@@@ -1,4367 -1,0 +1,4520 @@@
- ## Unreleased / In Rust Nightly
 +# 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.
 +
- [3c7e7dbc...master](https://github.com/rust-lang/rust-clippy/compare/3c7e7dbc...master)
++## Unreleased / Beta / In Rust Nightly
 +
- Current stable, released 2022-09-22
++[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
 +[`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_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 dec13e44a17f88a7ff734f3b6b4492d9e86cfd21,0000000000000000000000000000000000000000..e3708bc485399fd42b32c6a1c24491771afa1a04
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,3 @@@
- A version of this document [can be found online](https://www.rust-lang.org/conduct.html).
- ## Conduct
- **Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org)
- * We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience,
-   gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age,
-   religion, nationality, or other similar characteristic.
- * On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and
-   welcoming environment for all.
- * Please be kind and courteous. There's no need to be mean or rude.
- * Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and
-   numerous costs. There is seldom a right answer.
- * Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and
-   see how it works.
- * We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We
-   interpret the term "harassment" as including the definition in the <a href="http://citizencodeofconduct.org/">Citizen
-   Code of Conduct</a>; if you have any lack of clarity about what might be included in that concept, please read their
-   definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
- * Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or
-   made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation
-   team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a
-   safe place for you and we've got your back.
- * Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
- ## Moderation
- These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation,
- please contact the [Rust moderation team][mod_team].
- 1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks,
-    are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
- 2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
- 3. Moderators will first respond to such remarks with a warning.
- 4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
- 5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
- 6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended
-    party a genuine apology.
- 7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a
-    different moderator, **in private**. Complaints about bans in-channel are not allowed.
- 8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate
-    situation, they should expect less leeway than others.
- In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically
- unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly
- if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can
- drive people away from the community entirely.
- And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was
- they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good
- there was something you could've communicated better — remember that it's your responsibility to make your fellow
- Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about
- cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their
- trust.
- The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust,
- #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo);
- GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org
- (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the
- maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider
- explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion.
- *Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the
- [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
- [mod_team]: https://www.rust-lang.org/team.html#Moderation-team
 +# The Rust Code of Conduct
 +
++The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).
index 85f94a74ad91d30629d055e8d3f7b357a3537662,0000000000000000000000000000000000000000..3158080d2b3099dc66a99e826eeed90128d89fa6
mode 100644,000000..100644
--- /dev/null
@@@ -1,253 -1,0 +1,286 @@@
 +# Contributing to Clippy
 +
 +Hello fellow Rustacean! Great to see your interest in compiler internals and lints!
 +
 +**First**: if you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyway. You won't be
 +yelled at for giving it your best effort. The worst that can happen is that you'll be politely asked to change
 +something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that.
 +
 +Clippy welcomes contributions from everyone. There are many ways to contribute to Clippy and the following document
 +explains how you can contribute and how to get started.  If you have any questions about contributing or need help with
 +anything, feel free to ask questions on issues or visit the `#clippy` on [Zulip].
 +
 +All contributors are expected to follow the [Rust Code of Conduct].
 +
 +- [Contributing to Clippy](#contributing-to-clippy)
 +  - [The Clippy book](#the-clippy-book)
 +  - [High level approach](#high-level-approach)
 +  - [Finding something to fix/improve](#finding-something-to-fiximprove)
 +  - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
 +    - [IntelliJ Rust](#intellij-rust)
 +    - [Rust Analyzer](#rust-analyzer)
 +  - [How Clippy works](#how-clippy-works)
 +  - [Issue and PR triage](#issue-and-pr-triage)
 +  - [Bors and Homu](#bors-and-homu)
 +  - [Contributions](#contributions)
++  - [License](#license)
 +
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
 +[Rust Code of Conduct]: https://www.rust-lang.org/policies/code-of-conduct
 +
 +## The Clippy book
 +
 +If you're new to Clippy and don't know where to start, the [Clippy book] includes
 +a [developer guide] and is a good place to start your journey.
 +
 +[Clippy book]: https://doc.rust-lang.org/nightly/clippy/index.html
 +[developer guide]: https://doc.rust-lang.org/nightly/clippy/development/index.html
 +
 +## High level approach
 +
 +1. Find something to fix/improve
 +2. Change code (likely some file in `clippy_lints/src/`)
 +3. Follow the instructions in the [Basics docs](book/src/development/basics.md)
 +   to get set up
 +4. Run `cargo test` in the root directory and wiggle code until it passes
 +5. Open a PR (also can be done after 2. if you run into problems)
 +
 +## Finding something to fix/improve
 +
 +All issues on Clippy are mentored, if you want help simply ask someone from the
 +Clippy team directly by mentioning them in the issue or over on [Zulip]. All
 +currently active team members can be found
 +[here](https://github.com/rust-lang/highfive/blob/master/highfive/configs/rust-lang/rust-clippy.json#L3)
 +
 +Some issues are easier than others. The [`good-first-issue`] label can be used to find the easy
 +issues. You can use `@rustbot claim` to assign the issue to yourself.
 +
 +There are also some abandoned PRs, marked with [`S-inactive-closed`].
 +Pretty often these PRs are nearly completed and just need some extra steps
 +(formatting, addressing review comments, ...) to be merged. If you want to
 +complete such a PR, please leave a comment in the PR and open a new one based
 +on it.
 +
 +Issues marked [`T-AST`] involve simple matching of the syntax tree structure,
 +and are generally easier than [`T-middle`] issues, which involve types
 +and resolved paths.
 +
 +[`T-AST`] issues will generally need you to match against a predefined syntax structure.
 +To figure out how this syntax structure is encoded in the AST, it is recommended to run
 +`rustc -Z unpretty=ast-tree` on an example of the structure and compare with the [nodes in the AST docs].
 +Usually the lint will end up to be a nested series of matches and ifs, [like so][deep-nesting].
 +But we can make it nest-less by using [let chains], [like this][nest-less].
 +
 +[`E-medium`] issues are generally pretty easy too, though it's recommended you work on an [`good-first-issue`]
 +first. Sometimes they are only somewhat involved code wise, but not difficult per-se.
 +Note that [`E-medium`] issues may require some knowledge of Clippy internals or some
 +debugging to find the actual problem behind the issue.
 +
 +[`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
 +lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
 +an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
 +
 +[`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
 +[`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
 +[`T-AST`]: https://github.com/rust-lang/rust-clippy/labels/T-AST
 +[`T-middle`]: https://github.com/rust-lang/rust-clippy/labels/T-middle
 +[`E-medium`]: https://github.com/rust-lang/rust-clippy/labels/E-medium
 +[`ty`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty
 +[nodes in the AST docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/
 +[deep-nesting]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/mem_forget.rs#L31-L45
 +[let chains]: https://github.com/rust-lang/rust/pull/94927
 +[nest-less]: https://github.com/rust-lang/rust-clippy/blob/5e4f0922911536f80d9591180fa604229ac13939/clippy_lints/src/bit_mask.rs#L133-L159
 +
 +## Getting code-completion for rustc internals to work
 +
 +### IntelliJ Rust
 +Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
 +using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
 +available via a `rustup` component at the time of writing.
 +To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
 +`git clone https://github.com/rust-lang/rust/`.
 +Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
 +which `IntelliJ Rust` will be able to understand.
 +Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 +you just cloned.
 +The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
 +Clippy's `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
 +Just make sure to remove the dependencies again before finally making a pull request!
 +
 +[rustc_repo]: https://github.com/rust-lang/rust/
 +[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
 +
 +### Rust Analyzer
 +For [`rust-analyzer`][ra_homepage] to work correctly make sure that in the `rust-analyzer` configuration you set
 +
 +```json
 +{ "rust-analyzer.rustc.source": "discover" }
 +```
 +
 +You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
 +a lot more type hints.
 +
 +To have `rust-analyzer` also work in the `clippy_dev` and `lintcheck` crates, add the following configuration
 +
 +```json
 +{
 +    "rust-analyzer.linkedProjects": [
 +        "./Cargo.toml",
 +        "clippy_dev/Cargo.toml",
 +        "lintcheck/Cargo.toml",
 +    ]
 +}
 +```
 +
 +[ra_homepage]: https://rust-analyzer.github.io/
 +
 +## How Clippy works
 +
 +[`clippy_lints/src/lib.rs`][lint_crate_entry] imports all the different lint modules and registers in the [`LintStore`].
 +For example, the [`else_if_without_else`][else_if_without_else] lint is registered like this:
 +
 +```rust
 +// ./clippy_lints/src/lib.rs
 +
 +// ...
 +pub mod else_if_without_else;
 +// ...
 +
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // ...
 +    store.register_early_pass(|| box else_if_without_else::ElseIfWithoutElse);
 +    // ...
 +
 +    store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
 +        // ...
 +        LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +        // ...
 +    ]);
 +}
 +```
 +
 +The [`rustc_lint::LintStore`][`LintStore`] provides two methods to register lints:
 +[register_early_pass][reg_early_pass] and [register_late_pass][reg_late_pass]. Both take an object
 +that implements an [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass] respectively. This is done in
 +every single lint. It's worth noting that the majority of `clippy_lints/src/lib.rs` is autogenerated by `cargo dev
 +update_lints`. When you are writing your own lint, you can use that script to save you some time.
 +
 +```rust
 +// ./clippy_lints/src/else_if_without_else.rs
 +
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +
 +// ...
 +
 +pub struct ElseIfWithoutElse;
 +
 +// ...
 +
 +impl EarlyLintPass for ElseIfWithoutElse {
 +    // ... the functions needed, to make the lint work
 +}
 +```
 +
 +The difference between `EarlyLintPass` and `LateLintPass` is that the methods of the `EarlyLintPass` trait only provide
 +AST information. The methods of the `LateLintPass` trait are executed after type checking and contain type information
 +via the `LateContext` parameter.
 +
 +That's why the `else_if_without_else` example uses the `register_early_pass` function. Because the
 +[actual lint logic][else_if_without_else] does not depend on any type information.
 +
 +[lint_crate_entry]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
 +[else_if_without_else]: https://github.com/rust-lang/rust-clippy/blob/4253aa7137cb7378acc96133c787e49a345c2b3c/clippy_lints/src/else_if_without_else.rs
 +[`LintStore`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html
 +[reg_early_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_early_pass
 +[reg_late_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LintStore.html#method.register_late_pass
 +[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
 +
 +## Issue and PR triage
 +
 +Clippy is following the [Rust triage procedure][triage] for issues and pull
 +requests.
 +
 +However, we are a smaller project with all contributors being volunteers
 +currently. Between writing new lints, fixing issues, reviewing pull requests and
 +responding to issues there may not always be enough time to stay on top of it
 +all.
 +
 +Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example
 +an ICE in a popular crate that many other crates depend on. We don't
 +want Clippy to crash on your code and we want it to be as reliable as the
 +suggestions from Rust compiler errors.
 +
 +We have prioritization labels and a sync-blocker label, which are described below.
 +- [P-low][p-low]: Requires attention (fix/response/evaluation) by a team member but isn't urgent.
 +- [P-medium][p-medium]: Should be addressed by a team member until the next sync.
 +- [P-high][p-high]: Should be immediately addressed and will require an out-of-cycle sync or a backport.
 +- [L-sync-blocker][l-sync-blocker]: An issue that "blocks" a sync.
 +Or rather: before the sync this should be addressed,
 +e.g. by removing a lint again, so it doesn't hit beta/stable.
 +
 +## Bors and Homu
 +
 +We use a bot powered by [Homu][homu] to help automate testing and landing of pull
 +requests in Clippy. The bot's username is @bors.
 +
 +You can find the Clippy bors queue [here][homu_queue].
 +
 +If you have @bors permissions, you can find an overview of the available
 +commands [here][homu_instructions].
 +
 +[triage]: https://forge.rust-lang.org/release/triage-procedure.html
 +[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
 +[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
 +[p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low
 +[p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
 +[p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
 +[l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
 +[homu]: https://github.com/rust-lang/homu
 +[homu_instructions]: https://bors.rust-lang.org/
 +[homu_queue]: https://bors.rust-lang.org/queue/clippy
 +
 +## Contributions
 +
 +Contributions to Clippy should be made in the form of GitHub pull requests. Each pull request will
 +be reviewed by a core contributor (someone with permission to land patches) and either landed in the
 +main tree or given feedback for changes that would be required.
 +
++All PRs should include a `changelog` entry with a short comment explaining the change. The rule of thumb is basically,
++"what do you believe is important from an outsider's perspective?" Often, PRs are only related to a single property of a
++lint, and then it's good to mention that one. Otherwise, it's better to include too much detail than too little.
++
++Clippy's [changelog] is created from these comments. Every release, someone gets all commits from bors with a
++`changelog: XYZ` entry and combines them into the changelog. This is a manual process.
++
++Examples:
++- New lint
++  ```
++  changelog: new lint: [`missing_trait_methods`]
++  ```
++- False positive fix
++  ```
++  changelog: Fix [`unused_peekable`] false positive when peeked in a closure or called as `f(&mut peekable)`
++  ```
++- Purely internal change
++  ```
++  changelog: none
++  ```
++
++Note this it is fine for a PR to include multiple `changelog` entries, e.g.:
++```
++changelog: Something 1
++changelog: Something 2
++changelog: Something 3
++```
++
++[changelog]: CHANGELOG.md
++
++## License
++
 +All code in this repository is under the [Apache-2.0] or the [MIT] license.
 +
 +<!-- adapted from https://github.com/servo/servo/blob/master/CONTRIBUTING.md -->
 +
 +[Apache-2.0]: https://www.apache.org/licenses/LICENSE-2.0
 +[MIT]: https://opensource.org/licenses/MIT
index 60200a88b85829b6b7cf883829791d7ebc626ed2,0000000000000000000000000000000000000000..fe425a2fb991fdb1377b985eb83d007156ad58dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- version = "0.1.66"
 +[package]
 +name = "clippy"
++version = "0.1.67"
 +description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +keywords = ["clippy", "lint", "plugin"]
 +categories = ["development-tools", "development-tools::cargo-plugins"]
 +build = "build.rs"
 +edition = "2021"
 +publish = false
 +
 +[[bin]]
 +name = "cargo-clippy"
 +test = false
 +path = "src/main.rs"
 +
 +[[bin]]
 +name = "clippy-driver"
 +path = "src/driver.rs"
 +
 +[dependencies]
 +clippy_lints = { path = "clippy_lints" }
 +semver = "1.0"
 +rustc_tools_util = "0.2.1"
 +tempfile = { version = "3.2", optional = true }
 +termize = "0.1"
 +
 +[dev-dependencies]
 +compiletest_rs = { version = "0.9", features = ["tmp"] }
 +tester = "0.9"
 +regex = "1.5"
 +toml = "0.5"
 +walkdir = "2.3"
 +# This is used by the `collect-metadata` alias.
 +filetime = "0.2"
 +
 +# A noop dependency that changes in the Rust repository, it's a bit of a hack.
 +# See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
 +# for more information.
 +rustc-workspace-hack = "1.0"
 +
 +# UI test dependencies
 +clippy_utils = { path = "clippy_utils" }
 +derive-new = "0.5"
 +if_chain = "1.0"
 +itertools = "0.10.1"
 +quote = "1.0"
 +serde = { version = "1.0.125", features = ["derive"] }
 +syn = { version = "1.0", features = ["full"] }
 +futures = "0.3"
 +parking_lot = "0.12"
 +tokio = { version = "1", features = ["io-util"] }
 +rustc-semver = "1.1"
 +
 +[build-dependencies]
 +rustc_tools_util = "0.2.1"
 +
 +[features]
 +deny-warnings = ["clippy_lints/deny-warnings"]
 +integration = ["tempfile"]
 +internal = ["clippy_lints/internal", "tempfile"]
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index a8a6b86d2a15b73da47061a1eb20810aa9aa3268,0000000000000000000000000000000000000000..f74de7de42b8b0d20d5c59b2d71cace4f776c7e6
mode 100644,000000..100644
--- /dev/null
@@@ -1,263 -1,0 +1,257 @@@
- [![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test%22+event%3Apush+branch%3Aauto)
 +# Clippy
 +
- > **Note**
- >
- > Configuration changes will not apply for code that has already been compiled and cached under `./target/`;
- > for example, adding a new string to `doc-valid-idents` may still result in Clippy flagging that string. To be sure
- > that any configuration changes are applied, you may want to run `cargo clean` and re-compile your crate from scratch.
++[![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
 +```
 +
 +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.
 +
 +> **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 inner 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 2ac3b4fe2ed4f456f91188385e6e4aa3638ccc2b,0000000000000000000000000000000000000000..510c7e852af6e9629f90ff90aeaf1886fa174fb9
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,20 @@@
- tempfile = "3.2"
 +[package]
 +name = "clippy_dev"
 +version = "0.0.1"
 +edition = "2021"
 +
 +[dependencies]
 +aho-corasick = "0.7"
 +clap = "3.2"
 +indoc = "1.0"
 +itertools = "0.10.1"
 +opener = "0.5"
 +shell-escape = "0.1"
 +walkdir = "2.3"
 +
 +[features]
 +deny-warnings = []
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index 71005449b4ddf871d46f1ec86a8cc07b89ed3a8f,0000000000000000000000000000000000000000..aafd0f71a59bc15951a87d2065d68820d1e398a9
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,47 @@@
-         // Run in a tempdir as changes to clippy do not retrigger linting
-         let target = tempfile::Builder::new()
-             .prefix("clippy")
-             .tempdir()
-             .expect("failed to create tempdir");
 +use crate::cargo_clippy_path;
 +use std::process::{self, Command, ExitStatus};
 +use std::{fs, io};
 +
 +fn exit_if_err(status: io::Result<ExitStatus>) {
 +    match status.expect("failed to run command").code() {
 +        Some(0) => {},
 +        Some(n) => process::exit(n),
 +        None => {
 +            eprintln!("Killed by signal");
 +            process::exit(1);
 +        },
 +    }
 +}
 +
 +pub fn run<'a>(path: &str, args: impl Iterator<Item = &'a String>) {
 +    let is_file = match fs::metadata(path) {
 +        Ok(metadata) => metadata.is_file(),
 +        Err(e) => {
 +            eprintln!("Failed to read {path}: {e:?}");
 +            process::exit(1);
 +        },
 +    };
 +
 +    if is_file {
 +        exit_if_err(
 +            Command::new("cargo")
 +                .args(["run", "--bin", "clippy-driver", "--"])
 +                .args(["-L", "./target/debug"])
 +                .args(["-Z", "no-codegen"])
 +                .args(["--edition", "2021"])
 +                .arg(path)
 +                .args(args)
 +                .status(),
 +        );
 +    } else {
 +        exit_if_err(Command::new("cargo").arg("build").status());
 +
-             .env("CARGO_TARGET_DIR", target.as_ref())
 +        let status = Command::new(cargo_clippy_path())
 +            .arg("clippy")
 +            .args(args)
 +            .current_dir(path)
-         target.close().expect("failed to remove tempdir");
 +            .status();
 +
 +        exit_if_err(status);
 +    }
 +}
index e690bc369cd4337203eef2e1c2e6d7ba25834751,0000000000000000000000000000000000000000..837618c9294b7511f4cb8a22b79c127a23bc7f95
mode 100644,000000..100644
--- /dev/null
@@@ -1,1404 -1,0 +1,1265 @@@
- use std::collections::{BTreeSet, HashMap, HashSet};
 +use crate::clippy_project_root;
 +use aho_corasick::AhoCorasickBuilder;
 +use indoc::writedoc;
 +use itertools::Itertools;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
-         "clippy_lints/src/lib.register_lints.rs",
++use std::collections::{HashMap, HashSet};
 +use std::ffi::OsStr;
 +use std::fmt::Write;
 +use std::fs::{self, OpenOptions};
 +use std::io::{self, Read, Seek, SeekFrom, Write as _};
 +use std::ops::Range;
 +use std::path::{Path, PathBuf};
 +use walkdir::{DirEntry, WalkDir};
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
 +pub fn update(update_mode: UpdateMode) {
 +    let (lints, deprecated_lints, renamed_lints) = gather_all();
 +    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
++    remove_old_files(update_mode);
++}
++
++/// Remove files no longer needed after <https://github.com/rust-lang/rust-clippy/pull/9541>
++/// that may be reintroduced unintentionally
++///
++/// FIXME: This is a temporary measure that should be removed when there are no more PRs that
++/// include the stray files
++fn remove_old_files(update_mode: UpdateMode) {
++    let mut failed = false;
++    let mut remove_file = |path: &Path| match update_mode {
++        UpdateMode::Check => {
++            if path.exists() {
++                failed = true;
++                println!("unexpected file: {}", path.display());
++            }
++        },
++        UpdateMode::Change => {
++            if fs::remove_file(path).is_ok() {
++                println!("removed file: {}", path.display());
++            }
++        },
++    };
++
++    let files = [
++        "clippy_lints/src/lib.register_all.rs",
++        "clippy_lints/src/lib.register_cargo.rs",
++        "clippy_lints/src/lib.register_complexity.rs",
++        "clippy_lints/src/lib.register_correctness.rs",
++        "clippy_lints/src/lib.register_internal.rs",
++        "clippy_lints/src/lib.register_lints.rs",
++        "clippy_lints/src/lib.register_nursery.rs",
++        "clippy_lints/src/lib.register_pedantic.rs",
++        "clippy_lints/src/lib.register_perf.rs",
++        "clippy_lints/src/lib.register_restriction.rs",
++        "clippy_lints/src/lib.register_style.rs",
++        "clippy_lints/src/lib.register_suspicious.rs",
++        "src/docs.rs",
++    ];
++
++    for file in files {
++        remove_file(Path::new(file));
++    }
++
++    if let Ok(docs_dir) = fs::read_dir("src/docs") {
++        for doc_file in docs_dir {
++            let path = doc_file.unwrap().path();
++            remove_file(&path);
++        }
++    }
++
++    if failed {
++        exit_with_failure();
++    }
 +}
 +
 +fn generate_lint_files(
 +    update_mode: UpdateMode,
 +    lints: &[Lint],
 +    deprecated_lints: &[DeprecatedLint],
 +    renamed_lints: &[RenamedLint],
 +) {
 +    let internal_lints = Lint::internal_lints(lints);
 +    let mut usable_lints = Lint::usable_lints(lints);
 +    usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("book/src/README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->\n",
 +        "<!-- end autogenerated links to lint list -->",
 +        |res| {
 +            for lint in usable_lints
 +                .iter()
 +                .map(|l| &*l.name)
 +                .chain(deprecated_lints.iter().map(|l| &*l.name))
 +                .chain(
 +                    renamed_lints
 +                        .iter()
 +                        .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
 +                )
 +                .sorted()
 +            {
 +                writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
 +            }
 +        },
 +    );
 +
 +    // This has to be in lib.rs, otherwise rustfmt doesn't work
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
 +        "// end lints modules, do not remove this comment, it’s used in `update_lints`",
 +        |res| {
 +            for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
 +                writeln!(res, "mod {lint_mod};").unwrap();
 +            }
 +        },
 +    );
 +
 +    process_file(
-         &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
++        "clippy_lints/src/declared_lints.rs",
 +        update_mode,
-     let all_group_lints = usable_lints.iter().filter(|l| {
-         matches!(
-             &*l.group,
-             "correctness" | "suspicious" | "style" | "complexity" | "perf"
-         )
-     });
-     let content = gen_lint_group_list("all", all_group_lints);
-     process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
-     update_docs(update_mode, &usable_lints);
-     for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
-         let content = gen_lint_group_list(&lint_group, lints.iter());
-         process_file(
-             format!("clippy_lints/src/lib.register_{lint_group}.rs"),
-             update_mode,
-             &content,
-         );
-     }
++        &gen_declared_lints(internal_lints.iter(), usable_lints.iter()),
 +    );
 +    process_file(
 +        "clippy_lints/src/lib.deprecated.rs",
 +        update_mode,
 +        &gen_deprecated(deprecated_lints),
 +    );
 +
- fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
-     replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
-         for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
-             writeln!(res, r#"    "{name}","#).unwrap();
-         }
-     });
-     if update_mode == UpdateMode::Check {
-         let mut extra = BTreeSet::new();
-         let mut lint_names = usable_lints
-             .iter()
-             .map(|lint| lint.name.clone())
-             .collect::<BTreeSet<_>>();
-         for file in std::fs::read_dir("src/docs").unwrap() {
-             let filename = file.unwrap().file_name().into_string().unwrap();
-             if let Some(name) = filename.strip_suffix(".txt") {
-                 if !lint_names.remove(name) {
-                     extra.insert(name.to_string());
-                 }
-             }
-         }
-         let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
-         if failed {
-             exit_with_failure();
-         }
-     } else {
-         if std::fs::remove_dir_all("src/docs").is_err() {
-             eprintln!("could not remove src/docs directory");
-         }
-         if std::fs::create_dir("src/docs").is_err() {
-             eprintln!("could not recreate src/docs directory");
-         }
-     }
-     for lint in usable_lints {
-         process_file(
-             Path::new("src/docs").join(lint.name.clone() + ".txt"),
-             update_mode,
-             &lint.documentation,
-         );
-     }
- }
- fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
-     if lints.is_empty() {
-         return false;
-     }
-     println!("{header}");
-     for lint in lints.iter().sorted() {
-         println!("    {lint}");
-     }
-     println!();
-     true
- }
 +    let content = gen_deprecated_lints_test(deprecated_lints);
 +    process_file("tests/ui/deprecated.rs", update_mode, &content);
 +
 +    let content = gen_renamed_lints_test(renamed_lints);
 +    process_file("tests/ui/rename.rs", update_mode, &content);
 +}
 +
-     documentation: String,
 +pub fn print_lints() {
 +    let (lint_list, _, _) = gather_all();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        println!("\n## {lint_group}");
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({DOCS_LINK}#{}) ({})", lint.name, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {usable_lint_count} lints");
 +}
 +
 +/// Runs the `rename_lint` command.
 +///
 +/// This does the following:
 +/// * Adds an entry to `renamed_lints.rs`.
 +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
 +/// * Renames the lint struct to the new name.
 +/// * Renames the module containing the lint struct to the new name if it shares a name with the
 +///   lint.
 +///
 +/// # Panics
 +/// Panics for the following conditions:
 +/// * If a file path could not read from or then written to
 +/// * If either lint name has a prefix
 +/// * If `old_name` doesn't name an existing lint.
 +/// * If `old_name` names a deprecated or renamed lint.
 +#[allow(clippy::too_many_lines)]
 +pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
 +    if let Some((prefix, _)) = old_name.split_once("::") {
 +        panic!("`{old_name}` should not contain the `{prefix}` prefix");
 +    }
 +    if let Some((prefix, _)) = new_name.split_once("::") {
 +        panic!("`{new_name}` should not contain the `{prefix}` prefix");
 +    }
 +
 +    let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
 +    let mut old_lint_index = None;
 +    let mut found_new_name = false;
 +    for (i, lint) in lints.iter().enumerate() {
 +        if lint.name == old_name {
 +            old_lint_index = Some(i);
 +        } else if lint.name == new_name {
 +            found_new_name = true;
 +        }
 +    }
 +    let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{old_name}`"));
 +
 +    let lint = RenamedLint {
 +        old_name: format!("clippy::{old_name}"),
 +        new_name: if uplift {
 +            new_name.into()
 +        } else {
 +            format!("clippy::{new_name}")
 +        },
 +    };
 +
 +    // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
 +    // case.
 +    assert!(
 +        !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
 +        "`{old_name}` has already been renamed"
 +    );
 +    assert!(
 +        !deprecated_lints.iter().any(|l| lint.old_name == l.name),
 +        "`{old_name}` has already been deprecated"
 +    );
 +
 +    // Update all lint level attributes. (`clippy::lint_name`)
 +    for file in WalkDir::new(clippy_project_root())
 +        .into_iter()
 +        .map(Result::unwrap)
 +        .filter(|f| {
 +            let name = f.path().file_name();
 +            let ext = f.path().extension();
 +            (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
 +                && name != Some(OsStr::new("rename.rs"))
 +                && name != Some(OsStr::new("renamed_lints.rs"))
 +        })
 +    {
 +        rewrite_file(file.path(), |s| {
 +            replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
 +        });
 +    }
 +
 +    renamed_lints.push(lint);
 +    renamed_lints.sort_by(|lhs, rhs| {
 +        lhs.new_name
 +            .starts_with("clippy::")
 +            .cmp(&rhs.new_name.starts_with("clippy::"))
 +            .reverse()
 +            .then_with(|| lhs.old_name.cmp(&rhs.old_name))
 +    });
 +
 +    write_file(
 +        Path::new("clippy_lints/src/renamed_lints.rs"),
 +        &gen_renamed_lints_list(&renamed_lints),
 +    );
 +
 +    if uplift {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{old_name}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually."
 +        );
 +    } else if found_new_name {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{new_name}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually."
 +        );
 +    } else {
 +        // Rename the lint struct and source files sharing a name with the lint.
 +        let lint = &mut lints[old_lint_index];
 +        let old_name_upper = old_name.to_uppercase();
 +        let new_name_upper = new_name.to_uppercase();
 +        lint.name = new_name.into();
 +
 +        // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
 +        if try_rename_file(
 +            Path::new(&format!("tests/ui/{old_name}.rs")),
 +            Path::new(&format!("tests/ui/{new_name}.rs")),
 +        ) {
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{old_name}.stderr")),
 +                Path::new(&format!("tests/ui/{new_name}.stderr")),
 +            );
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{old_name}.fixed")),
 +                Path::new(&format!("tests/ui/{new_name}.fixed")),
 +            );
 +        }
 +
 +        // Try to rename the file containing the lint if the file name matches the lint's name.
 +        let replacements;
 +        let replacements = if lint.module == old_name
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{old_name}.rs")),
 +                Path::new(&format!("clippy_lints/src/{new_name}.rs")),
 +            ) {
 +            // Edit the module name in the lint list. Note there could be multiple lints.
 +            for lint in lints.iter_mut().filter(|l| l.module == old_name) {
 +                lint.module = new_name.into();
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else if !lint.module.contains("::")
 +            // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}/{old_name}.rs", lint.module)),
 +                Path::new(&format!("clippy_lints/src/{}/{new_name}.rs", lint.module)),
 +            )
 +        {
 +            // Edit the module name in the lint list. Note there could be multiple lints, or none.
 +            let renamed_mod = format!("{}::{old_name}", lint.module);
 +            for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
 +                lint.module = format!("{}::{new_name}", lint.module);
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else {
 +            replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
 +            &replacements[0..1]
 +        };
 +
 +        // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
 +        // renamed.
 +        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
 +            rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
 +        }
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("{old_name} has been successfully renamed");
 +    }
 +
 +    println!("note: `cargo uitest` still needs to be run to update the test results");
 +}
 +
 +const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 +/// Runs the `deprecate` command
 +///
 +/// This does the following:
 +/// * Adds an entry to `deprecated_lints.rs`.
 +/// * Removes the lint declaration (and the entire file if applicable)
 +///
 +/// # Panics
 +///
 +/// If a file path could not read from or written to
 +pub fn deprecate(name: &str, reason: Option<&String>) {
 +    fn finish(
 +        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
 +        name: &str,
 +        reason: &str,
 +    ) {
 +        deprecated_lints.push(DeprecatedLint {
 +            name: name.to_string(),
 +            reason: reason.to_string(),
 +            declaration_range: Range::default(),
 +        });
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("info: `{name}` has successfully been deprecated");
 +
 +        if reason == DEFAULT_DEPRECATION_REASON {
 +            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
 +        }
 +        println!("note: you must run `cargo uitest` to update the test results");
 +    }
 +
 +    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
 +    let name_lower = name.to_lowercase();
 +    let name_upper = name.to_uppercase();
 +
 +    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
 +    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{name}`"); return; };
 +
 +    let mod_path = {
 +        let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
 +        if mod_path.is_dir() {
 +            mod_path = mod_path.join("mod");
 +        }
 +
 +        mod_path.set_extension("rs");
 +        mod_path
 +    };
 +
 +    let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
 +
 +    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
 +        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
 +        finish((lints, deprecated_lints, renamed_lints), name, reason);
 +        return;
 +    }
 +
 +    eprintln!("error: lint not found");
 +}
 +
 +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
 +    fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
 +        lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
 +    }
 +
 +    fn remove_test_assets(name: &str) {
 +        let test_file_stem = format!("tests/ui/{name}");
 +        let path = Path::new(&test_file_stem);
 +
 +        // Some lints have their own directories, delete them
 +        if path.is_dir() {
 +            fs::remove_dir_all(path).ok();
 +            return;
 +        }
 +
 +        // Remove all related test files
 +        fs::remove_file(path.with_extension("rs")).ok();
 +        fs::remove_file(path.with_extension("stderr")).ok();
 +        fs::remove_file(path.with_extension("fixed")).ok();
 +    }
 +
 +    fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
 +        let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
 +            content
 +                .find("declare_lint_pass!")
 +                .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
 +        });
 +        let mut impl_lint_pass_end = content[impl_lint_pass_start..]
 +            .find(']')
 +            .expect("failed to find `impl_lint_pass` terminator");
 +
 +        impl_lint_pass_end += impl_lint_pass_start;
 +        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
 +            let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
 +            for c in content[lint_name_end..impl_lint_pass_end].chars() {
 +                // Remove trailing whitespace
 +                if c == ',' || c.is_whitespace() {
 +                    lint_name_end += 1;
 +                } else {
 +                    break;
 +                }
 +            }
 +
 +            content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
 +        }
 +    }
 +
 +    if path.exists() {
 +        if let Some(lint) = lints.iter().find(|l| l.name == name) {
 +            if lint.module == name {
 +                // The lint name is the same as the file, we can just delete the entire file
 +                fs::remove_file(path)?;
 +            } else {
 +                // We can't delete the entire file, just remove the declaration
 +
 +                if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
 +                    // Remove clippy_lints/src/some_mod/some_lint.rs
 +                    let mut lint_mod_path = path.to_path_buf();
 +                    lint_mod_path.set_file_name(name);
 +                    lint_mod_path.set_extension("rs");
 +
 +                    fs::remove_file(lint_mod_path).ok();
 +                }
 +
 +                let mut content =
 +                    fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
 +
 +                eprintln!(
 +                    "warn: you will have to manually remove any code related to `{name}` from `{}`",
 +                    path.display()
 +                );
 +
 +                assert!(
 +                    content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
 +                    "error: `{}` does not contain lint `{}`'s declaration",
 +                    path.display(),
 +                    lint.name
 +                );
 +
 +                // Remove lint declaration (declare_clippy_lint!)
 +                content.replace_range(lint.declaration_range.clone(), "");
 +
 +                // Remove the module declaration (mod xyz;)
 +                let mod_decl = format!("\nmod {name};");
 +                content = content.replacen(&mod_decl, "", 1);
 +
 +                remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
 +                fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
 +            }
 +
 +            remove_test_assets(name);
 +            remove_lint(name, lints);
 +            return Ok(true);
 +        }
 +    }
 +
 +    Ok(false)
 +}
 +
 +fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
 +    let mut file = OpenOptions::new().write(true).open(path)?;
 +
 +    file.seek(SeekFrom::End(0))?;
 +
 +    let version = crate::new_lint::get_stabilization_version();
 +    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
 +        "TODO"
 +    } else {
 +        reason
 +    };
 +
 +    writedoc!(
 +        file,
 +        "
 +
 +        declare_deprecated_lint! {{
 +            /// ### What it does
 +            /// Nothing. This lint has been deprecated.
 +            ///
 +            /// ### Deprecation reason
 +            /// {}
 +            #[clippy::version = \"{}\"]
 +            pub {},
 +            \"{}\"
 +        }}
 +
 +        ",
 +        deprecation_reason,
 +        version,
 +        name,
 +        reason,
 +    )
 +}
 +
 +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 +/// were no replacements.
 +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
 +    fn is_ident_char(c: u8) -> bool {
 +        matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
 +    }
 +
 +    let searcher = AhoCorasickBuilder::new()
 +        .dfa(true)
 +        .match_kind(aho_corasick::MatchKind::LeftmostLongest)
 +        .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
 +        .unwrap();
 +
 +    let mut result = String::with_capacity(contents.len() + 1024);
 +    let mut pos = 0;
 +    let mut edited = false;
 +    for m in searcher.find_iter(contents) {
 +        let (old, new) = replacements[m.pattern()];
 +        result.push_str(&contents[pos..m.start()]);
 +        result.push_str(
 +            if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
 +                && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
 +            {
 +                edited = true;
 +                new
 +            } else {
 +                old
 +            },
 +        );
 +        pos = m.end();
 +    }
 +    result.push_str(&contents[pos..]);
 +    edited.then_some(result)
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
 +
 +fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
 +    if update_mode == UpdateMode::Check {
 +        let old_content =
 +            fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {e}", path.as_ref().display()));
 +        if content != old_content {
 +            exit_with_failure();
 +        }
 +    } else {
 +        fs::write(&path, content.as_bytes())
 +            .unwrap_or_else(|e| panic!("Cannot write to {}: {e}", path.as_ref().display()));
 +    }
 +}
 +
 +fn exit_with_failure() {
 +    println!(
 +        "Not all lints defined properly. \
 +                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
 +    );
 +    std::process::exit(1);
 +}
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    module: String,
 +    declaration_range: Range<usize>,
-     fn new(
-         name: &str,
-         group: &str,
-         desc: &str,
-         module: &str,
-         declaration_range: Range<usize>,
-         documentation: String,
-     ) -> Self {
 +}
 +
 +impl Lint {
 +    #[must_use]
-             documentation,
++    fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.into(),
 +            desc: remove_line_splices(desc),
 +            module: module.into(),
 +            declaration_range,
- /// Generates the code for registering a group
- fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
-     let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
-     details.sort_unstable();
-     let mut output = GENERATED_FILE_COMMENT.to_string();
-     let _ = writeln!(
-         output,
-         "store.register_group(true, \"clippy::{group_name}\", Some(\"clippy_{group_name}\"), vec![",
-     );
-     for (module, name) in details {
-         let _ = writeln!(output, "    LintId::of({module}::{name}),");
-     }
-     output.push_str("])\n");
-     output
- }
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct DeprecatedLint {
 +    name: String,
 +    reason: String,
 +    declaration_range: Range<usize>,
 +}
 +impl DeprecatedLint {
 +    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            reason: remove_line_splices(reason),
 +            declaration_range,
 +        }
 +    }
 +}
 +
 +struct RenamedLint {
 +    old_name: String,
 +    new_name: String,
 +}
 +impl RenamedLint {
 +    fn new(old_name: &str, new_name: &str) -> Self {
 +        Self {
 +            old_name: remove_line_splices(old_name),
 +            new_name: remove_line_splices(new_name),
 +        }
 +    }
 +}
 +
- fn gen_register_lint_list<'a>(
 +/// Generates the `register_removed` code
 +#[must_use]
 +fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("{\n");
 +    for lint in lints {
 +        let _ = write!(
 +            output,
 +            concat!(
 +                "    store.register_removed(\n",
 +                "        \"clippy::{}\",\n",
 +                "        \"{}\",\n",
 +                "    );\n"
 +            ),
 +            lint.name, lint.reason,
 +        );
 +    }
 +    output.push_str("}\n");
 +
 +    output
 +}
 +
 +/// Generates the code for registering lints
 +#[must_use]
-     output.push_str("store.register_lints(&[\n");
++fn gen_declared_lints<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> String {
 +    let mut details: Vec<_> = internal_lints
 +        .map(|l| (false, &l.module, l.name.to_uppercase()))
 +        .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
 +        .collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
-         let _ = writeln!(output, "    {module_name}::{lint_name},");
++    output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n");
 +
 +    for (is_public, module_name, lint_name) in details {
 +        if !is_public {
 +            output.push_str("    #[cfg(feature = \"internal\")]\n");
 +        }
-     output.push_str("])\n");
++        let _ = writeln!(output, "    crate::{module_name}::{lint_name}_INFO,");
 +    }
-         let mut docs = String::with_capacity(128);
-         let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
++    output.push_str("];\n");
 +
 +    output
 +}
 +
 +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    for lint in lints {
 +        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
 +    let mut seen_lints = HashSet::new();
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    res.push_str("// run-rustfix\n\n");
 +    for lint in lints {
 +        if seen_lints.insert(&lint.new_name) {
 +            writeln!(res, "#![allow({})]", lint.new_name).unwrap();
 +        }
 +    }
 +    seen_lints.clear();
 +    for lint in lints {
 +        if seen_lints.insert(&lint.old_name) {
 +            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
 +        }
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
 +    const HEADER: &str = "\
 +        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
 +        #[rustfmt::skip]\n\
 +        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
 +
 +    let mut res = String::from(HEADER);
 +    for lint in lints {
 +        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
 +    }
 +    res.push_str("];\n");
 +    res
 +}
 +
 +/// Gathers all lints defined in `clippy_lints/src`
 +fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
 +    let mut lints = Vec::with_capacity(1000);
 +    let mut deprecated_lints = Vec::with_capacity(50);
 +    let mut renamed_lints = Vec::with_capacity(50);
 +
 +    for (rel_path, file) in clippy_lints_src_files() {
 +        let path = file.path();
 +        let contents =
 +            fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
 +        let module = rel_path
 +            .components()
 +            .map(|c| c.as_os_str().to_str().unwrap())
 +            .collect::<Vec<_>>()
 +            .join("::");
 +
 +        // If the lints are stored in mod.rs, we get the module name from
 +        // the containing directory:
 +        let module = if let Some(module) = module.strip_suffix("::mod.rs") {
 +            module
 +        } else {
 +            module.strip_suffix(".rs").unwrap_or(&module)
 +        };
 +
 +        match module {
 +            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
 +            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
 +            _ => parse_contents(&contents, module, &mut lints),
 +        }
 +    }
 +    (lints, deprecated_lints, renamed_lints)
 +}
 +
 +fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
 +    let root_path = clippy_project_root().join("clippy_lints/src");
 +    let iter = WalkDir::new(&root_path).into_iter();
 +    iter.map(Result::unwrap)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +        .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
 +}
 +
 +macro_rules! match_tokens {
 +    ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
 +         {
 +            $(#[allow(clippy::redundant_pattern)] let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::$token $({$($fields)*})?,
 +                    content: $($capture @)? _,
 +                    ..
 +            }) = $iter.next() else {
 +                continue;
 +            };)*
 +            #[allow(clippy::unused_unit)]
 +            { ($($($capture,)?)*) }
 +        }
 +    }
 +}
 +
 +pub(crate) use match_tokens;
 +
 +pub(crate) struct LintDeclSearchResult<'a> {
 +    pub token_kind: TokenKind,
 +    pub content: &'a str,
 +    pub range: Range<usize>,
 +}
 +
 +/// Parse a source file looking for `declare_clippy_lint` macro invocations.
 +fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
 +    ) {
 +        let start = range.start;
-         let mut in_code = false;
-         while let Some(t) = iter.next() {
-             match t.token_kind {
-                 TokenKind::LineComment { .. } => {
-                     if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
-                         if line.starts_with("```") {
-                             docs += "```\n";
-                             in_code = !in_code;
-                         } else if !(in_code && line.starts_with("# ")) {
-                             docs += line;
-                             docs.push('\n');
-                         }
-                     }
-                 },
-                 TokenKind::Pound => {
-                     match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
-                     break;
-                 },
-                 TokenKind::Ident => {
-                     break;
-                 },
-                 _ => {},
-             }
++        let mut iter = iter
++            .by_ref()
++            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
 +        // matches `!{`
 +        match_tokens!(iter, Bang OpenBrace);
-         docs.pop(); // remove final newline
++        match iter.next() {
++            // #[clippy::version = "version"] pub
++            Some(LintDeclSearchResult {
++                token_kind: TokenKind::Pound,
++                ..
++            }) => {
++                match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
++            },
++            // pub
++            Some(LintDeclSearchResult {
++                token_kind: TokenKind::Ident,
++                ..
++            }) => (),
++            _ => continue,
 +        }
-             lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
 +
 +        let (name, group, desc) = match_tokens!(
 +            iter,
 +            // LINT_NAME
 +            Ident(name) Comma
 +            // group,
 +            Ident(group) Comma
 +            // "description"
 +            Literal{..}(desc)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
-                 String::new(),
++            lints.push(Lint::new(name, group, desc, module, start..range.end));
 +        }
 +    }
 +}
 +
 +/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
 +fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
 +    ) {
 +        let start = range.start;
 +
 +        let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
 +            !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
 +        });
 +        let (name, reason) = match_tokens!(
 +            iter,
 +            // !{
 +            Bang OpenBrace
 +            // #[clippy::version = "version"]
 +            Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
 +            // pub LINT_NAME,
 +            Ident Ident(name) Comma
 +            // "description"
 +            Literal{kind: LiteralKind::Str{..},..}(reason)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(DeprecatedLint::new(name, reason, start..range.end));
 +        }
 +    }
 +}
 +
 +fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
 +    for line in contents.lines() {
 +        let mut offset = 0usize;
 +        let mut iter = tokenize(line).map(|t| {
 +            let range = offset..offset + t.len as usize;
 +            offset = range.end;
 +
 +            LintDeclSearchResult {
 +                token_kind: t.kind,
 +                content: &line[range.clone()],
 +                range,
 +            }
 +        });
 +
 +        let (old_name, new_name) = match_tokens!(
 +            iter,
 +            // ("old_name",
 +            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
 +            // "new_name"),
 +            Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
 +        );
 +        lints.push(RenamedLint::new(old_name, new_name));
 +    }
 +}
 +
 +/// Removes the line splices and surrounding quotes from a string literal
 +fn remove_line_splices(s: &str) -> String {
 +    let s = s
 +        .strip_prefix('r')
 +        .unwrap_or(s)
 +        .trim_matches('#')
 +        .strip_prefix('"')
 +        .and_then(|s| s.strip_suffix('"'))
 +        .unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
 +    let mut res = String::with_capacity(s.len());
 +    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
 +        if ch.is_ok() {
 +            res.push_str(&s[range]);
 +        }
 +    });
 +    res
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +fn replace_region_in_file(
 +    update_mode: UpdateMode,
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    write_replacement: impl FnMut(&mut String),
 +) {
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
 +    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
 +        Ok(x) => x,
 +        Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
 +    };
 +
 +    match update_mode {
 +        UpdateMode::Check if contents != new_contents => exit_with_failure(),
 +        UpdateMode::Check => (),
 +        UpdateMode::Change => {
 +            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
 +                panic!("Cannot write to `{}`: {e}", path.display());
 +            }
 +        },
 +    }
 +}
 +
 +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
 +/// were found, or the missing delimiter if not.
 +fn replace_region_in_text<'a>(
 +    text: &str,
 +    start: &'a str,
 +    end: &'a str,
 +    mut write_replacement: impl FnMut(&mut String),
 +) -> Result<String, &'a str> {
 +    let (text_start, rest) = text.split_once(start).ok_or(start)?;
 +    let (_, text_end) = rest.split_once(end).ok_or(end)?;
 +
 +    let mut res = String::with_capacity(text.len() + 4096);
 +    res.push_str(text_start);
 +    res.push_str(start);
 +    write_replacement(&mut res);
 +    res.push_str(end);
 +    res.push_str(text_end);
 +
 +    Ok(res)
 +}
 +
 +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
 +    match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
 +        Ok(file) => drop(file),
 +        Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
 +        Err(e) => panic_file(e, new_name, "create"),
 +    };
 +    match fs::rename(old_name, new_name) {
 +        Ok(()) => true,
 +        Err(e) => {
 +            drop(fs::remove_file(new_name));
 +            if e.kind() == io::ErrorKind::NotFound {
 +                false
 +            } else {
 +                panic_file(e, old_name, "rename");
 +            }
 +        },
 +    }
 +}
 +
 +#[allow(clippy::needless_pass_by_value)]
 +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
 +    panic!("failed to {action} file `{}`: {error}", name.display())
 +}
 +
 +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
 +    let mut file = fs::OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .open(path)
 +        .unwrap_or_else(|e| panic_file(e, path, "open"));
 +    let mut buf = String::new();
 +    file.read_to_string(&mut buf)
 +        .unwrap_or_else(|e| panic_file(e, path, "read"));
 +    if let Some(new_contents) = f(&buf) {
 +        file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.write_all(new_contents.as_bytes())
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.set_len(new_contents.len() as u64)
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +    }
 +}
 +
 +fn write_file(path: &Path, contents: &str) {
 +    fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    #[test]
 +    fn test_parse_contents() {
 +        static CONTENTS: &str = r#"
 +            declare_clippy_lint! {
 +                #[clippy::version = "Hello Clippy!"]
 +                pub PTR_ARG,
 +                style,
 +                "really long \
 +                text"
 +            }
 +
 +            declare_clippy_lint!{
 +                #[clippy::version = "Test version"]
 +                pub DOC_MARKDOWN,
 +                pedantic,
 +                "single line"
 +            }
 +        "#;
 +        let mut result = Vec::new();
 +        parse_contents(CONTENTS, "module_name", &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![
 +            Lint::new(
 +                "ptr_arg",
 +                "style",
 +                "\"really long text\"",
 +                "module_name",
 +                Range::default(),
-                 String::new(),
 +            ),
 +            Lint::new(
 +                "doc_markdown",
 +                "pedantic",
 +                "\"single line\"",
 +                "module_name",
 +                Range::default(),
-                 String::new(),
 +            ),
 +        ];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_parse_deprecated_contents() {
 +        static DEPRECATED_CONTENTS: &str = r#"
 +            /// some doc comment
 +            declare_deprecated_lint! {
 +                #[clippy::version = "I'm a version"]
 +                pub SHOULD_ASSERT_EQ,
 +                "`assert!()` will be more flexible with RFC 2011"
 +            }
 +        "#;
 +
 +        let mut result = Vec::new();
 +        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![DeprecatedLint::new(
 +            "should_assert_eq",
 +            "\"`assert!()` will be more flexible with RFC 2011\"",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq2",
 +                "Not Deprecated",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
-                 String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
-                 String::new(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal_style",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
-             String::new(),
 +            ),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "\"abc\"",
 +            "module_name",
 +            Range::default(),
-             Lint::new(
-                 "should_assert_eq",
-                 "group1",
-                 "\"abc\"",
-                 "module_name",
-                 Range::default(),
-                 String::new(),
-             ),
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
-                 String::new(),
-             ),
-             Lint::new(
-                 "incorrect_match",
-                 "group1",
-                 "\"abc\"",
-                 "module_name",
-                 Range::default(),
-                 String::new(),
++            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
-                 Lint::new(
-                     "should_assert_eq",
-                     "group1",
-                     "\"abc\"",
-                     "module_name",
-                     Range::default(),
-                     String::new(),
-                 ),
-                 Lint::new(
-                     "incorrect_match",
-                     "group1",
-                     "\"abc\"",
-                     "module_name",
-                     Range::default(),
-                     String::new(),
-                 ),
 +            ),
++            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
-                 String::new(),
++                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
++                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
-     #[test]
-     fn test_gen_lint_group_list() {
-         let lints = vec![
-             Lint::new(
-                 "abc",
-                 "group1",
-                 "\"abc\"",
-                 "module_name",
-                 Range::default(),
-                 String::new(),
-             ),
-             Lint::new(
-                 "should_assert_eq",
-                 "group1",
-                 "\"abc\"",
-                 "module_name",
-                 Range::default(),
-                 String::new(),
-             ),
-             Lint::new(
-                 "internal",
-                 "internal_style",
-                 "\"abc\"",
-                 "module_name",
-                 Range::default(),
-                 String::new(),
-             ),
-         ];
-         let expected = GENERATED_FILE_COMMENT.to_string()
-             + &[
-                 "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
-                 "    LintId::of(module_name::ABC),",
-                 "    LintId::of(module_name::INTERNAL),",
-                 "    LintId::of(module_name::SHOULD_ASSERT_EQ),",
-                 "])",
-             ]
-             .join("\n")
-             + "\n";
-         let result = gen_lint_group_list("group1", lints.iter());
-         assert_eq!(expected, result);
-     }
 +            )],
 +        );
 +        assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_deprecated() {
 +        let lints = vec![
 +            DeprecatedLint::new(
 +                "should_assert_eq",
 +                "\"has been superseded by should_assert_eq2\"",
 +                Range::default(),
 +            ),
 +            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
 +        ];
 +
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "{",
 +                "    store.register_removed(",
 +                "        \"clippy::should_assert_eq\",",
 +                "        \"has been superseded by should_assert_eq2\",",
 +                "    );",
 +                "    store.register_removed(",
 +                "        \"clippy::another_deprecated\",",
 +                "        \"will be removed\",",
 +                "    );",
 +                "}",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        assert_eq!(expected, gen_deprecated(&lints));
 +    }
 +}
index 6fbd6401ef3ebe00ba8a516912325564c7cc8589,0000000000000000000000000000000000000000..aedff24c12c6078b7aec3236f261837008df36e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,39 @@@
- version = "0.1.66"
 +[package]
 +name = "clippy_lints"
++version = "0.1.67"
 +description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +keywords = ["clippy", "lint", "plugin"]
 +edition = "2021"
 +
 +[dependencies]
 +cargo_metadata = "0.14"
 +clippy_utils = { path = "../clippy_utils" }
++declare_clippy_lint = { path = "../declare_clippy_lint" }
 +if_chain = "1.0"
 +itertools = "0.10.1"
 +pulldown-cmark = { version = "0.9", default-features = false }
 +quine-mc_cluskey = "0.2"
 +regex-syntax = "0.6"
 +serde = { version = "1.0", features = ["derive"] }
 +serde_json = { version = "1.0", optional = true }
 +tempfile = { version = "3.2", optional = true }
 +toml = "0.5"
 +unicode-normalization = "0.1"
 +unicode-script = { version = "0.5", default-features = false }
 +semver = "1.0"
 +rustc-semver = "1.1"
 +# NOTE: cargo requires serde feat in its url dep
 +# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 +url = { version = "2.2", features = ["serde"] }
 +
 +[features]
 +deny-warnings = ["clippy_utils/deny-warnings"]
 +# build clippy with internal lints enabled, off by default
 +internal = ["clippy_utils/internal", "serde_json", "tempfile"]
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 0bd1f8b784e8f3f5552c79c2af82a4c23a5c46fa,0000000000000000000000000000000000000000..ecf8e83375dbf718173a941298d87613a6c646fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,736 -1,0 +1,763 @@@
- use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +//! checks for attributes
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::{is_panic, macro_backtrace};
 +use clippy_utils::msrvs;
 +use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
 +use clippy_utils::{extract_msrv_attr, meets_msrv};
 +use if_chain::if_chain;
 +use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
 +};
- use rustc_span::sym;
++use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
-                                                         | "unsafe_removed_from_name",
 +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"
-                     "restriction lints are not meant to be all enabled",
++                                                        | "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(),
-                     "try enabling only the lints you really need",
++                    "`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: &Lit) {
 +    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 {
 +    pub msrv: Option<RustcVersion>,
 +}
 +
 +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) {
 +        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?",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) {
 +    if_chain! {
 +        if meets_msrv(msrv, 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 34717811866d87fc0cb68922dc838d3548ccd3db,0000000000000000000000000000000000000000..d40a385435afb59e695d2d101633f4df737fbad0
mode 100644,000000..100644
--- /dev/null
@@@ -1,285 -1,0 +1,284 @@@
- use rustc_hir::def::{Namespace, Res};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{match_def_path, paths};
 +use rustc_data_structures::fx::FxHashMap;
-             if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::GeneratorInteriorTypeCause;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +
 +use crate::utils::conf::DisallowedPath;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to await while holding a non-async-aware MutexGuard.
 +    ///
 +    /// ### Why is this bad?
 +    /// The Mutex types found in std::sync and parking_lot
 +    /// are not designed to operate in an async context across await points.
 +    ///
 +    /// There are two potential solutions. One is to use an async-aware Mutex
 +    /// type. Many asynchronous foundation crates provide such a Mutex type. The
 +    /// other solution is to ensure the mutex is unlocked before calling await,
 +    /// either by introducing a scope or an explicit call to Drop::drop.
 +    ///
 +    /// ### Known problems
 +    /// Will report false positive for explicitly dropped guards
 +    /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
 +    /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &Mutex<u32>) {
 +    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
 +    ///   drop(guard); // explicit drop
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::sync::Mutex;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   {
 +    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   }
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &Mutex<u32>) {
 +    ///   {
 +    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   } // guard dropped here at end of scope
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub AWAIT_HOLDING_LOCK,
 +    suspicious,
 +    "inside an async function, holding a `MutexGuard` while calling `await`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `RefCell` refs only check for exclusive mutable access
 +    /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
 +    /// risks panics from a mutable ref shared while other refs are outstanding.
 +    ///
 +    /// ### Known problems
 +    /// Will report false positive for explicitly dropped refs
 +    /// ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is
 +    /// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::cell::RefCell;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
 +    ///   drop(y); // explicit drop
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::cell::RefCell;
 +    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   {
 +    ///      let mut y = x.borrow_mut();
 +    ///      *y += 1;
 +    ///   }
 +    ///   baz().await;
 +    /// }
 +    ///
 +    /// async fn bar(x: &RefCell<u32>) {
 +    ///   {
 +    ///     let mut y = x.borrow_mut();
 +    ///     *y += 1;
 +    ///   } // y dropped here at end of scope
 +    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub AWAIT_HOLDING_REFCELL_REF,
 +    suspicious,
 +    "inside an async function, holding a `RefCell` ref while calling `await`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Allows users to configure types which should not be held across `await`
 +    /// suspension points.
 +    ///
 +    /// ### Why is this bad?
 +    /// There are some types which are perfectly "safe" to be used concurrently
 +    /// from a memory access perspective but will cause bugs at runtime if they
 +    /// are held in such a way.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```toml
 +    /// await-holding-invalid-types = [
 +    ///   # You can specify a type name
 +    ///   "CustomLockType",
 +    ///   # You can (optionally) specify a reason
 +    ///   { path = "OtherCustomLockType", reason = "Relies on a thread local" }
 +    /// ]
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # async fn baz() {}
 +    /// struct CustomLockType;
 +    /// struct OtherCustomLockType;
 +    /// async fn foo() {
 +    ///   let _x = CustomLockType;
 +    ///   let _y = OtherCustomLockType;
 +    ///   baz().await; // Lint violation
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub AWAIT_HOLDING_INVALID_TYPE,
 +    suspicious,
 +    "holding a type across an await point which is not allowed to be held as per the configuration"
 +}
 +
 +impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
 +
 +#[derive(Debug)]
 +pub struct AwaitHolding {
 +    conf_invalid_types: Vec<DisallowedPath>,
 +    def_ids: FxHashMap<DefId, DisallowedPath>,
 +}
 +
 +impl AwaitHolding {
 +    pub(crate) fn new(conf_invalid_types: Vec<DisallowedPath>) -> Self {
 +        Self {
 +            conf_invalid_types,
 +            def_ids: FxHashMap::default(),
 +        }
 +    }
 +}
 +
 +impl LateLintPass<'_> for AwaitHolding {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        for conf in &self.conf_invalid_types {
 +            let segs: Vec<_> = conf.path().split("::").collect();
++            for id in clippy_utils::def_path_def_ids(cx, &segs) {
 +                self.def_ids.insert(id, conf.clone());
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
 +        use AsyncGeneratorKind::{Block, Closure, Fn};
 +        if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
 +            let body_id = BodyId {
 +                hir_id: body.value.hir_id,
 +            };
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            self.check_interior_types(
 +                cx,
 +                typeck_results.generator_interior_types.as_ref().skip_binder(),
 +                body.value.span,
 +            );
 +        }
 +    }
 +}
 +
 +impl AwaitHolding {
 +    fn check_interior_types(&self, cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
 +        for ty_cause in ty_causes {
 +            if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
 +                if is_mutex_guard(cx, adt.did()) {
 +                    span_lint_and_then(
 +                        cx,
 +                        AWAIT_HOLDING_LOCK,
 +                        ty_cause.span,
 +                        "this `MutexGuard` is held across an `await` point",
 +                        |diag| {
 +                            diag.help(
 +                                "consider using an async-aware `Mutex` type or ensuring the \
 +                                `MutexGuard` is dropped before calling await",
 +                            );
 +                            diag.span_note(
 +                                ty_cause.scope_span.unwrap_or(span),
 +                                "these are all the `await` points this lock is held through",
 +                            );
 +                        },
 +                    );
 +                } else if is_refcell_ref(cx, adt.did()) {
 +                    span_lint_and_then(
 +                        cx,
 +                        AWAIT_HOLDING_REFCELL_REF,
 +                        ty_cause.span,
 +                        "this `RefCell` reference is held across an `await` point",
 +                        |diag| {
 +                            diag.help("ensure the reference is dropped before calling `await`");
 +                            diag.span_note(
 +                                ty_cause.scope_span.unwrap_or(span),
 +                                "these are all the `await` points this reference is held through",
 +                            );
 +                        },
 +                    );
 +                } else if let Some(disallowed) = self.def_ids.get(&adt.did()) {
 +                    emit_invalid_type(cx, ty_cause.span, disallowed);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPath) {
 +    span_lint_and_then(
 +        cx,
 +        AWAIT_HOLDING_INVALID_TYPE,
 +        span,
 +        &format!(
 +            "`{}` may not be held across an `await` point per `clippy.toml`",
 +            disallowed.path()
 +        ),
 +        |diag| {
 +            if let Some(reason) = disallowed.reason() {
 +                diag.note(reason);
 +            }
 +        },
 +    );
 +}
 +
 +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
 +        || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
 +        || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
 +}
 +
 +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
 +}
index 001d74c2605453e02988103b8f785aab7ce7fc64,0000000000000000000000000000000000000000..bdb3a011602729b5119024893cda47203588c74e
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,122 @@@
- use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, is_integer_literal, sugg::Sugg};
++use clippy_utils::higher::If;
 +use rustc_ast::LitKind;
 +use rustc_hir::{Block, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
-     /// Coercion or `from()` is idiomatic way to convert bool to a number.
++use clippy_utils::{diagnostics::span_lint_and_then, in_constant, is_else_clause, is_integer_literal, sugg::Sugg};
 +use rustc_errors::Applicability;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Instead of using an if statement to convert a bool to an int,
 +    /// this lint suggests using a `from()` function or an `as` coercion.
 +    ///
 +    /// ### Why is this bad?
-     style,
++    /// Coercion or `from()` is another way to convert bool to a number.
 +    /// Both methods are guaranteed to return 1 for true, and 0 for false.
 +    ///
 +    /// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let condition = false;
 +    /// if condition {
 +    ///     1_i64
 +    /// } else {
 +    ///     0
 +    /// };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let condition = false;
 +    /// i64::from(condition);
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # let condition = false;
 +    /// condition as i64;
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub BOOL_TO_INT_WITH_IF,
-     fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
-         if !expr.span.from_expansion() {
-             check_if_else(ctx, expr);
++    pedantic,
 +    "using if to convert bool to int"
 +}
 +declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
 +
 +impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
- fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
-     if let ExprKind::If(check, then, Some(else_)) = expr.kind
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
++        if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
++            check_if_else(cx, expr);
 +        }
 +    }
 +}
 +
-         && let Some(else_lit) = int_literal(else_)
++fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
++    if let Some(If { cond, then, r#else: Some(r#else) }) = If::hir(expr)
 +        && let Some(then_lit) = int_literal(then)
-             let mut sugg = Sugg::hir_with_applicability(ctx, check, "..", &mut applicability);
++        && let Some(else_lit) = int_literal(r#else)
 +    {
 +        let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
 +            false
 +        } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) {
 +            true
 +        } else {
 +            // Expression isn't boolean, exit
 +            return;
 +        };
 +        let mut applicability = Applicability::MachineApplicable;
 +        let snippet = {
-         let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type
++            let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
 +            if inverted {
 +                sugg = !sugg;
 +            }
 +            sugg
 +        };
 +
-             let wrap_in_curly = is_else_clause(ctx.tcx, expr);
++        let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type
 +
 +        let suggestion = {
-         span_lint_and_then(ctx,
++            let wrap_in_curly = is_else_clause(cx.tcx, expr);
 +            let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
 +            if wrap_in_curly {
 +                s = s.blockify();
 +            }
 +            s
 +        }; // when used in else clause if statement should be wrapped in curly braces
 +
 +        let into_snippet = snippet.clone().maybe_par();
 +        let as_snippet = snippet.as_ty(ty);
 +
++        span_lint_and_then(cx,
 +            BOOL_TO_INT_WITH_IF,
 +            expr.span,
 +            "boolean to int conversion using if",
 +            |diag| {
 +            diag.span_suggestion(
 +                expr.span,
 +                "replace with from",
 +                suggestion,
 +                applicability,
 +            );
 +            diag.note(format!("`{as_snippet}` or `{into_snippet}.into()` can also be valid options"));
 +        });
 +    };
 +}
 +
 +// If block contains only a int literal expression, return literal expression
 +fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
 +    if let ExprKind::Block(block, _) = expr.kind
 +        && let Block {
 +            stmts: [],       // Shouldn't lint if statements with side effects
 +            expr: Some(expr),
 +            ..
 +        } = block
 +        && let ExprKind::Lit(lit) = &expr.kind
 +        && let LitKind::Int(_, _) = lit.node
 +    {
 +        Some(expr)
 +    } else {
 +        None
 +    }
 +}
index 08164c0b654e2a0daa18737dfc44e0134603dca0,0000000000000000000000000000000000000000..939bdbcdc7cd7e245b733bed959c419f0b336f76
mode 100644,000000..100644
--- /dev/null
@@@ -1,513 -1,0 +1,513 @@@
- fn implements_ord<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::eq_expr_value;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +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 boolean expressions that can be written more
 +    /// concisely.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability of boolean expressions suffers from
 +    /// unnecessary duplication.
 +    ///
 +    /// ### Known problems
 +    /// Ignores short circuiting behavior of `||` and
 +    /// `&&`. Ignores `|`, `&` and `^`.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if a && true {}
 +    /// if !(a == b) {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if a {}
 +    /// if a != b {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NONMINIMAL_BOOL,
 +    complexity,
 +    "boolean expressions that can be written more concisely"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for boolean expressions that contain terminals that
 +    /// can be eliminated.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is most likely a logic bug.
 +    ///
 +    /// ### Known problems
 +    /// Ignores short circuiting behavior.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // The `b` is unnecessary, the expression is equivalent to `if a`.
 +    /// if a && b || a { ... }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if a {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OVERLY_COMPLEX_BOOL_EXPR,
 +    correctness,
 +    "boolean expressions that contain terminals which can be eliminated"
 +}
 +
 +// For each pairs, both orders are considered.
 +const METHODS_WITH_NEGATION: [(&str, &str); 2] = [("is_some", "is_none"), ("is_err", "is_ok")];
 +
 +declare_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        NonminimalBoolVisitor { cx }.visit_body(body);
 +    }
 +}
 +
 +struct NonminimalBoolVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +use quine_mc_cluskey::Bool;
 +struct Hir2Qmm<'a, 'tcx, 'v> {
 +    terminals: Vec<&'v Expr<'v>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
 +    fn extract(&mut self, op: BinOpKind, a: &[&'v Expr<'_>], mut v: Vec<Bool>) -> Result<Vec<Bool>, String> {
 +        for a in a {
 +            if let ExprKind::Binary(binop, lhs, rhs) = &a.kind {
 +                if binop.node == op {
 +                    v = self.extract(op, &[lhs, rhs], v)?;
 +                    continue;
 +                }
 +            }
 +            v.push(self.run(a)?);
 +        }
 +        Ok(v)
 +    }
 +
 +    fn run(&mut self, e: &'v Expr<'_>) -> Result<Bool, String> {
 +        fn negate(bin_op_kind: BinOpKind) -> Option<BinOpKind> {
 +            match bin_op_kind {
 +                BinOpKind::Eq => Some(BinOpKind::Ne),
 +                BinOpKind::Ne => Some(BinOpKind::Eq),
 +                BinOpKind::Gt => Some(BinOpKind::Le),
 +                BinOpKind::Ge => Some(BinOpKind::Lt),
 +                BinOpKind::Lt => Some(BinOpKind::Ge),
 +                BinOpKind::Le => Some(BinOpKind::Gt),
 +                _ => None,
 +            }
 +        }
 +
 +        // prevent folding of `cfg!` macros and the like
 +        if !e.span.from_expansion() {
 +            match &e.kind {
 +                ExprKind::Unary(UnOp::Not, inner) => return Ok(Bool::Not(Box::new(self.run(inner)?))),
 +                ExprKind::Binary(binop, lhs, rhs) => match &binop.node {
 +                    BinOpKind::Or => {
 +                        return Ok(Bool::Or(self.extract(BinOpKind::Or, &[lhs, rhs], Vec::new())?));
 +                    },
 +                    BinOpKind::And => {
 +                        return Ok(Bool::And(self.extract(BinOpKind::And, &[lhs, rhs], Vec::new())?));
 +                    },
 +                    _ => (),
 +                },
 +                ExprKind::Lit(lit) => match lit.node {
 +                    LitKind::Bool(true) => return Ok(Bool::True),
 +                    LitKind::Bool(false) => return Ok(Bool::False),
 +                    _ => (),
 +                },
 +                _ => (),
 +            }
 +        }
 +        for (n, expr) in self.terminals.iter().enumerate() {
 +            if eq_expr_value(self.cx, e, expr) {
 +                #[expect(clippy::cast_possible_truncation)]
 +                return Ok(Bool::Term(n as u8));
 +            }
 +
 +            if_chain! {
 +                if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind;
 +                if implements_ord(self.cx, e_lhs);
 +                if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind;
 +                if negate(e_binop.node) == Some(expr_binop.node);
 +                if eq_expr_value(self.cx, e_lhs, expr_lhs);
 +                if eq_expr_value(self.cx, e_rhs, expr_rhs);
 +                then {
 +                    #[expect(clippy::cast_possible_truncation)]
 +                    return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
 +                }
 +            }
 +        }
 +        let n = self.terminals.len();
 +        self.terminals.push(e);
 +        if n < 32 {
 +            #[expect(clippy::cast_possible_truncation)]
 +            Ok(Bool::Term(n as u8))
 +        } else {
 +            Err("too many literals".to_owned())
 +        }
 +    }
 +}
 +
 +struct SuggestContext<'a, 'tcx, 'v> {
 +    terminals: &'v [&'v Expr<'v>],
 +    cx: &'a LateContext<'tcx>,
 +    output: String,
 +}
 +
 +impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
 +    fn recurse(&mut self, suggestion: &Bool) -> Option<()> {
 +        use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
 +        match suggestion {
 +            True => {
 +                self.output.push_str("true");
 +            },
 +            False => {
 +                self.output.push_str("false");
 +            },
 +            Not(inner) => match **inner {
 +                And(_) | Or(_) => {
 +                    self.output.push('!');
 +                    self.output.push('(');
 +                    self.recurse(inner);
 +                    self.output.push(')');
 +                },
 +                Term(n) => {
 +                    let terminal = self.terminals[n as usize];
 +                    if let Some(str) = simplify_not(self.cx, terminal) {
 +                        self.output.push_str(&str);
 +                    } else {
 +                        self.output.push('!');
 +                        let snip = snippet_opt(self.cx, terminal.span)?;
 +                        self.output.push_str(&snip);
 +                    }
 +                },
 +                True | False | Not(_) => {
 +                    self.output.push('!');
 +                    self.recurse(inner)?;
 +                },
 +            },
 +            And(v) => {
 +                for (index, inner) in v.iter().enumerate() {
 +                    if index > 0 {
 +                        self.output.push_str(" && ");
 +                    }
 +                    if let Or(_) = *inner {
 +                        self.output.push('(');
 +                        self.recurse(inner);
 +                        self.output.push(')');
 +                    } else {
 +                        self.recurse(inner);
 +                    }
 +                }
 +            },
 +            Or(v) => {
 +                for (index, inner) in v.iter().rev().enumerate() {
 +                    if index > 0 {
 +                        self.output.push_str(" || ");
 +                    }
 +                    self.recurse(inner);
 +                }
 +            },
 +            &Term(n) => {
 +                let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?;
 +                self.output.push_str(&snip);
 +            },
 +        }
 +        Some(())
 +    }
 +}
 +
 +fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    match &expr.kind {
 +        ExprKind::Binary(binop, lhs, rhs) => {
 +            if !implements_ord(cx, lhs) {
 +                return None;
 +            }
 +
 +            match binop.node {
 +                BinOpKind::Eq => Some(" != "),
 +                BinOpKind::Ne => Some(" == "),
 +                BinOpKind::Lt => Some(" >= "),
 +                BinOpKind::Gt => Some(" <= "),
 +                BinOpKind::Le => Some(" > "),
 +                BinOpKind::Ge => Some(" < "),
 +                _ => None,
 +            }
 +            .and_then(|op| {
 +                Some(format!(
 +                    "{}{op}{}",
 +                    snippet_opt(cx, lhs.span)?,
 +                    snippet_opt(cx, rhs.span)?
 +                ))
 +            })
 +        },
 +        ExprKind::MethodCall(path, receiver, [], _) => {
 +            let type_of_receiver = cx.typeck_results().expr_ty(receiver);
 +            if !is_type_diagnostic_item(cx, type_of_receiver, sym::Option)
 +                && !is_type_diagnostic_item(cx, type_of_receiver, sym::Result)
 +            {
 +                return None;
 +            }
 +            METHODS_WITH_NEGATION
 +                .iter()
 +                .copied()
 +                .flat_map(|(a, b)| vec![(a, b), (b, a)])
 +                .find(|&(a, _)| {
 +                    let path: &str = path.ident.name.as_str();
 +                    a == path
 +                })
 +                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?)))
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn suggest(cx: &LateContext<'_>, suggestion: &Bool, terminals: &[&Expr<'_>]) -> String {
 +    let mut suggest_context = SuggestContext {
 +        terminals,
 +        cx,
 +        output: String::new(),
 +    };
 +    suggest_context.recurse(suggestion);
 +    suggest_context.output
 +}
 +
 +fn simple_negate(b: Bool) -> Bool {
 +    use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
 +    match b {
 +        True => False,
 +        False => True,
 +        t @ Term(_) => Not(Box::new(t)),
 +        And(mut v) => {
 +            for el in &mut v {
 +                *el = simple_negate(::std::mem::replace(el, True));
 +            }
 +            Or(v)
 +        },
 +        Or(mut v) => {
 +            for el in &mut v {
 +                *el = simple_negate(::std::mem::replace(el, True));
 +            }
 +            And(v)
 +        },
 +        Not(inner) => *inner,
 +    }
 +}
 +
 +#[derive(Default)]
 +struct Stats {
 +    terminals: [usize; 32],
 +    negations: usize,
 +    ops: usize,
 +}
 +
 +fn terminal_stats(b: &Bool) -> Stats {
 +    fn recurse(b: &Bool, stats: &mut Stats) {
 +        match b {
 +            True | False => stats.ops += 1,
 +            Not(inner) => {
 +                match **inner {
 +                    And(_) | Or(_) => stats.ops += 1, // brackets are also operations
 +                    _ => stats.negations += 1,
 +                }
 +                recurse(inner, stats);
 +            },
 +            And(v) | Or(v) => {
 +                stats.ops += v.len() - 1;
 +                for inner in v {
 +                    recurse(inner, stats);
 +                }
 +            },
 +            &Term(n) => stats.terminals[n as usize] += 1,
 +        }
 +    }
 +    use quine_mc_cluskey::Bool::{And, False, Not, Or, Term, True};
 +    let mut stats = Stats::default();
 +    recurse(b, &mut stats);
 +    stats
 +}
 +
 +impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
 +    fn bool_expr(&self, e: &'tcx Expr<'_>) {
 +        let mut h2q = Hir2Qmm {
 +            terminals: Vec::new(),
 +            cx: self.cx,
 +        };
 +        if let Ok(expr) = h2q.run(e) {
 +            if h2q.terminals.len() > 8 {
 +                // QMC has exponentially slow behavior as the number of terminals increases
 +                // 8 is reasonable, it takes approximately 0.2 seconds.
 +                // See #825
 +                return;
 +            }
 +
 +            let stats = terminal_stats(&expr);
 +            let mut simplified = expr.simplify();
 +            for simple in Bool::Not(Box::new(expr)).simplify() {
 +                match simple {
 +                    Bool::Not(_) | Bool::True | Bool::False => {},
 +                    _ => simplified.push(Bool::Not(Box::new(simple.clone()))),
 +                }
 +                let simple_negated = simple_negate(simple);
 +                if simplified.iter().any(|s| *s == simple_negated) {
 +                    continue;
 +                }
 +                simplified.push(simple_negated);
 +            }
 +            let mut improvements = Vec::with_capacity(simplified.len());
 +            'simplified: for suggestion in &simplified {
 +                let simplified_stats = terminal_stats(suggestion);
 +                let mut improvement = false;
 +                for i in 0..32 {
 +                    // ignore any "simplifications" that end up requiring a terminal more often
 +                    // than in the original expression
 +                    if stats.terminals[i] < simplified_stats.terminals[i] {
 +                        continue 'simplified;
 +                    }
 +                    if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
 +                        span_lint_hir_and_then(
 +                            self.cx,
 +                            OVERLY_COMPLEX_BOOL_EXPR,
 +                            e.hir_id,
 +                            e.span,
 +                            "this boolean expression contains a logic bug",
 +                            |diag| {
 +                                diag.span_help(
 +                                    h2q.terminals[i].span,
 +                                    "this expression can be optimized out by applying boolean operations to the \
 +                                     outer expression",
 +                                );
 +                                diag.span_suggestion(
 +                                    e.span,
 +                                    "it would look like the following",
 +                                    suggest(self.cx, suggestion, &h2q.terminals),
 +                                    // nonminimal_bool can produce minimal but
 +                                    // not human readable expressions (#3141)
 +                                    Applicability::Unspecified,
 +                                );
 +                            },
 +                        );
 +                        // don't also lint `NONMINIMAL_BOOL`
 +                        return;
 +                    }
 +                    // if the number of occurrences of a terminal decreases or any of the stats
 +                    // decreases while none increases
 +                    improvement |= (stats.terminals[i] > simplified_stats.terminals[i])
 +                        || (stats.negations > simplified_stats.negations && stats.ops == simplified_stats.ops)
 +                        || (stats.ops > simplified_stats.ops && stats.negations == simplified_stats.negations);
 +                }
 +                if improvement {
 +                    improvements.push(suggestion);
 +                }
 +            }
 +            let nonminimal_bool_lint = |suggestions: Vec<_>| {
 +                span_lint_hir_and_then(
 +                    self.cx,
 +                    NONMINIMAL_BOOL,
 +                    e.hir_id,
 +                    e.span,
 +                    "this boolean expression can be simplified",
 +                    |diag| {
 +                        diag.span_suggestions(
 +                            e.span,
 +                            "try",
 +                            suggestions.into_iter(),
 +                            // nonminimal_bool can produce minimal but
 +                            // not human readable expressions (#3141)
 +                            Applicability::Unspecified,
 +                        );
 +                    },
 +                );
 +            };
 +            if improvements.is_empty() {
 +                let mut visitor = NotSimplificationVisitor { cx: self.cx };
 +                visitor.visit_expr(e);
 +            } else {
 +                nonminimal_bool_lint(
 +                    improvements
 +                        .into_iter()
 +                        .map(|suggestion| suggest(self.cx, suggestion, &h2q.terminals))
 +                        .collect(),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        if !e.span.from_expansion() {
 +            match &e.kind {
 +                ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
 +                    self.bool_expr(e);
 +                },
 +                ExprKind::Unary(UnOp::Not, inner) => {
 +                    if self.cx.typeck_results().node_types()[inner.hir_id].is_bool() {
 +                        self.bool_expr(e);
 +                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +        walk_expr(self, e);
 +    }
 +}
 +
++fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    cx.tcx
 +        .get_diagnostic_item(sym::Ord)
 +        .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +}
 +
 +struct NotSimplificationVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind {
 +            if let Some(suggestion) = simplify_not(self.cx, inner) {
 +                span_lint_and_sugg(
 +                    self.cx,
 +                    NONMINIMAL_BOOL,
 +                    expr.span,
 +                    "this boolean expression can be simplified",
 +                    "try",
 +                    suggestion,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index b72c4c772f1ce24d655a3d55ddf8d188d28e24f8,0000000000000000000000000000000000000000..7148b5e6ebf45e09c2da8693aaf6184410ec6bbc
mode 100644,000000..100644
--- /dev/null
@@@ -1,742 -1,0 +1,742 @@@
-     #[clippy::version = "1.64.0"]
 +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 clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### 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 {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Casts {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> 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) {
 +            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_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_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                    cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to);
 +                }
 +                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
 +            }
 +
 +            as_underscore::check(cx, expr, cast_to_hir);
 +
 +            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
 +                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 77af3b53d63338ad735d7a46f351975af3343927,0000000000000000000000000000000000000000..1c3a89a97824c31ccdb1f4888fb2b85eb67b88ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,168 @@@
- use clippy_utils::LimitStack;
 +//! calculate cognitive complexity and warn about overly complex functions
 +
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::visitors::for_each_expr;
- use rustc_hir::{Body, ExprKind, FnDecl, HirId};
++use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack};
 +use core::ops::ControlFlow;
 +use rustc_ast::ast::Attribute;
 +use rustc_hir::intravisit::FnKind;
-         body: &'tcx Body<'_>,
++use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::{sym, BytePos};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with high cognitive complexity.
 +    ///
 +    /// ### Why is this bad?
 +    /// Methods of high cognitive complexity tend to be hard to
 +    /// both read and maintain. Also LLVM will tend to optimize small methods better.
 +    ///
 +    /// ### Known problems
 +    /// Sometimes it's hard to find a way to reduce the
 +    /// complexity.
 +    ///
 +    /// ### Example
 +    /// You'll see it when you get the warning.
 +    #[clippy::version = "1.35.0"]
 +    pub COGNITIVE_COMPLEXITY,
 +    nursery,
 +    "functions that should be split up into multiple functions"
 +}
 +
 +pub struct CognitiveComplexity {
 +    limit: LimitStack,
 +}
 +
 +impl CognitiveComplexity {
 +    #[must_use]
 +    pub fn new(limit: u64) -> Self {
 +        Self {
 +            limit: LimitStack::new(limit),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
 +
 +impl CognitiveComplexity {
 +    #[expect(clippy::cast_possible_truncation)]
 +    fn check<'tcx>(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
-         let expr = body.value;
++        expr: &'tcx Expr<'_>,
 +        body_span: Span,
 +    ) {
 +        if body_span.from_expansion() {
 +            return;
 +        }
 +
-             self.check(cx, kind, decl, body, span);
 +        let mut cc = 1u64;
 +        let mut returns = 0u64;
 +        let _: Option<!> = for_each_expr(expr, |e| {
 +            match e.kind {
 +                ExprKind::If(_, _, _) => {
 +                    cc += 1;
 +                },
 +                ExprKind::Match(_, arms, _) => {
 +                    if arms.len() > 1 {
 +                        cc += 1;
 +                    }
 +                    cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
 +                },
 +                ExprKind::Ret(_) => returns += 1,
 +                _ => {},
 +            }
 +            ControlFlow::Continue(())
 +        });
 +
 +        let ret_ty = cx.typeck_results().node_type(expr.hir_id);
 +        let ret_adjust = if is_type_diagnostic_item(cx, ret_ty, sym::Result) {
 +            returns
 +        } else {
 +            #[expect(clippy::integer_division)]
 +            (returns / 2)
 +        };
 +
 +        // prevent degenerate cases where unreachable code contains `return` statements
 +        if cc >= ret_adjust {
 +            cc -= ret_adjust;
 +        }
 +
 +        if cc > self.limit.limit() {
 +            let fn_span = match kind {
 +                FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
 +                FnKind::Closure => {
 +                    let header_span = body_span.with_hi(decl.output.span().lo());
 +                    let pos = snippet_opt(cx, header_span).and_then(|snip| {
 +                        let low_offset = snip.find('|')?;
 +                        let high_offset = 1 + snip.get(low_offset + 1..)?.find('|')?;
 +                        let low = header_span.lo() + BytePos(low_offset as u32);
 +                        let high = low + BytePos(high_offset as u32 + 1);
 +
 +                        Some((low, high))
 +                    });
 +
 +                    if let Some((low, high)) = pos {
 +                        Span::new(low, high, header_span.ctxt(), header_span.parent())
 +                    } else {
 +                        return;
 +                    }
 +                },
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                COGNITIVE_COMPLEXITY,
 +                fn_span,
 +                &format!(
 +                    "the function has a cognitive complexity of ({cc}/{})",
 +                    self.limit.limit()
 +                ),
 +                None,
 +                "you could split it up into multiple smaller functions",
 +            );
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for CognitiveComplexity {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        hir_id: HirId,
 +    ) {
 +        let def_id = cx.tcx.hir().local_def_id(hir_id);
 +        if !cx.tcx.has_attr(def_id.to_def_id(), sym::test) {
++            let expr = if is_async_fn(kind) {
++                match get_async_fn_body(cx.tcx, body) {
++                    Some(b) => b,
++                    None => {
++                        return;
++                    },
++                }
++            } else {
++                body.value
++            };
++
++            self.check(cx, kind, decl, expr, span);
 +        }
 +    }
 +
 +    fn enter_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
 +        self.limit.push_attrs(cx.sess(), attrs, "cognitive_complexity");
 +    }
 +    fn exit_lint_attrs(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
 +        self.limit.pop_attrs(cx.sess(), attrs, "cognitive_complexity");
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0d3fc43a6443b69f23e38a42eb3569411aad39b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,628 @@@
++// 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::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::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 218dbeaddcadee32539160ee831786d991e5edc7,0000000000000000000000000000000000000000..9da64ffc13e169d93faae6c153f15c4845a80883
mode 100644,000000..100644
--- /dev/null
@@@ -1,1569 -1,0 +1,1633 @@@
-         let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
-             x
-         } else {
 +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::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::has_enclosing_paren;
 +use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
 +use clippy_utils::{
 +    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
 +    walk_to_expr_usage,
 +};
 +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, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
 +    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
 +};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span, Symbol};
 +use rustc_trait_selection::infer::InferCtxtExt as _;
 +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.
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl<'tcx> Dereferencing<'tcx> {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> 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();
-                         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 => {
-                                 if 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()
-                                 }
-                             },
++        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);
 +                let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv);
 +                match kind {
 +                    RefOp::Deref => {
 +                        if let Position::FieldAccess {
 +                            name,
 +                            of_union: false,
 +                        } = 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 },
 +                            ));
 +                        }
 +                    }
 +                    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,
 +                                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,
 +                                }),
 +                                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,
 +                                    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<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> (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)| {
-                 ExprKind::MethodCall(_, receiver, args, _) => {
++                        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()
++                                    }
++                                },
++                            }
 +                        })
 +                    }),
-                         if let ty::Param(param_ty) = ty.kind() {
++                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 = match cx
 +                                .typeck_results()
 +                                .node_substs_opt(parent.hir_id)
 +                                .and_then(|subs| subs.get(1..))
 +                            {
 +                                Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
 +                                None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
 +                            } && 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
 +                                .type_implements_trait(trait_id, impl_ty, subs, 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];
- #[expect(clippy::too_many_arguments)]
++                        // `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.
-             | ty::Projection(_) => Position::DerefStable(
-                 precedence,
-                 ty.is_sized(cx.tcx, cx.param_env.without_caller_bounds()),
-             )
-             .into(),
++#[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,
 +    msrv: Option<RustcVersion>,
 +) -> 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::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::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::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()
 +                && !meets_msrv(msrv, 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 5ab7144e29098b7d4a877804d8dab723943f65c5,0000000000000000000000000000000000000000..68122b4cef577fede678085f08f58baa9181cbe1
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,150 @@@
- use rustc_hir::def::{Namespace, Res};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::macros::macro_backtrace;
 +use rustc_data_structures::fx::FxHashSet;
-                             diag.note(&format!("{reason} (from clippy.toml)"));
 +use rustc_hir::def_id::DefIdMap;
 +use rustc_hir::{Expr, ForeignItem, HirId, ImplItem, Item, Pat, Path, Stmt, TraitItem, Ty};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{ExpnId, Span};
 +
 +use crate::utils::conf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Denies the configured macros in clippy.toml
 +    ///
 +    /// Note: Even though this lint is warn-by-default, it will only trigger if
 +    /// macros are defined in the clippy.toml file.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some macros are undesirable in certain contexts, and it's beneficial to
 +    /// lint for them as needed.
 +    ///
 +    /// ### Example
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
 +    /// disallowed-macros = [
 +    ///     # Can use a string as the path of the disallowed macro.
 +    ///     "std::print",
 +    ///     # Can also use an inline table with a `path` key.
 +    ///     { path = "std::println" },
 +    ///     # When using an inline table, can add a `reason` for why the macro
 +    ///     # is disallowed.
 +    ///     { path = "serde::Serialize", reason = "no serializing" },
 +    /// ]
 +    /// ```
 +    /// ```
 +    /// use serde::Serialize;
 +    ///
 +    /// // Example code where clippy issues a warning
 +    /// println!("warns");
 +    ///
 +    /// // The diagnostic will contain the message "no serializing"
 +    /// #[derive(Serialize)]
 +    /// struct Data {
 +    ///     name: String,
 +    ///     value: usize,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.65.0"]
 +    pub DISALLOWED_MACROS,
 +    style,
 +    "use of a disallowed macro"
 +}
 +
 +pub struct DisallowedMacros {
 +    conf_disallowed: Vec<conf::DisallowedPath>,
 +    disallowed: DefIdMap<usize>,
 +    seen: FxHashSet<ExpnId>,
 +}
 +
 +impl DisallowedMacros {
 +    pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
 +        Self {
 +            conf_disallowed,
 +            disallowed: DefIdMap::default(),
 +            seen: FxHashSet::default(),
 +        }
 +    }
 +
 +    fn check(&mut self, cx: &LateContext<'_>, span: Span) {
 +        if self.conf_disallowed.is_empty() {
 +            return;
 +        }
 +
 +        for mac in macro_backtrace(span) {
 +            if !self.seen.insert(mac.expn) {
 +                return;
 +            }
 +
 +            if let Some(&index) = self.disallowed.get(&mac.def_id) {
 +                let conf = &self.conf_disallowed[index];
 +
 +                span_lint_and_then(
 +                    cx,
 +                    DISALLOWED_MACROS,
 +                    mac.span,
 +                    &format!("use of a disallowed macro `{}`", conf.path()),
 +                    |diag| {
 +                        if let Some(reason) = conf.reason() {
-             if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::MacroNS)) {
++                            diag.note(reason);
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
 +
 +impl LateLintPass<'_> for DisallowedMacros {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        for (index, conf) in self.conf_disallowed.iter().enumerate() {
 +            let segs: Vec<_> = conf.path().split("::").collect();
++            for id in clippy_utils::def_path_def_ids(cx, &segs) {
 +                self.disallowed.insert(id, index);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        self.check(cx, expr.span);
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
 +        self.check(cx, stmt.span);
 +    }
 +
 +    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &Ty<'_>) {
 +        self.check(cx, ty.span);
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) {
 +        self.check(cx, pat.span);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        self.check(cx, item.span);
 +        self.check(cx, item.vis_span);
 +    }
 +
 +    fn check_foreign_item(&mut self, cx: &LateContext<'_>, item: &ForeignItem<'_>) {
 +        self.check(cx, item.span);
 +        self.check(cx, item.vis_span);
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
 +        self.check(cx, item.span);
 +        self.check(cx, item.vis_span);
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
 +        self.check(cx, item.span);
 +    }
 +
 +    fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) {
 +        self.check(cx, path.span);
 +    }
 +}
index 6ac85606d9c7cf86827b7f038701fe152887e74b,0000000000000000000000000000000000000000..ca8671c8f1aa0ea94dcc678433d9b08b3bf2d83d
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,110 @@@
- use rustc_hir::def::{Namespace, Res};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
 +
-             if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, Some(Namespace::ValueNS)) {
 +use rustc_hir::def_id::DefIdMap;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +use crate::utils::conf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Denies the configured methods and functions in clippy.toml
 +    ///
 +    /// Note: Even though this lint is warn-by-default, it will only trigger if
 +    /// methods are defined in the clippy.toml file.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some methods are undesirable in certain contexts, and it's beneficial to
 +    /// lint for them as needed.
 +    ///
 +    /// ### Example
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
 +    /// disallowed-methods = [
 +    ///     # Can use a string as the path of the disallowed method.
 +    ///     "std::boxed::Box::new",
 +    ///     # Can also use an inline table with a `path` key.
 +    ///     { path = "std::time::Instant::now" },
 +    ///     # When using an inline table, can add a `reason` for why the method
 +    ///     # is disallowed.
 +    ///     { path = "std::vec::Vec::leak", reason = "no leaking memory" },
 +    /// ]
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// // Example code where clippy issues a warning
 +    /// let xs = vec![1, 2, 3, 4];
 +    /// xs.leak(); // Vec::leak is disallowed in the config.
 +    /// // The diagnostic contains the message "no leaking memory".
 +    ///
 +    /// let _now = Instant::now(); // Instant::now is disallowed in the config.
 +    ///
 +    /// let _box = Box::new(3); // Box::new is disallowed in the config.
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // Example code which does not raise clippy warning
 +    /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config.
 +    /// xs.push(123); // Vec::push is _not_ disallowed in the config.
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub DISALLOWED_METHODS,
 +    style,
 +    "use of a disallowed method call"
 +}
 +
 +#[derive(Clone, Debug)]
 +pub struct DisallowedMethods {
 +    conf_disallowed: Vec<conf::DisallowedPath>,
 +    disallowed: DefIdMap<usize>,
 +}
 +
 +impl DisallowedMethods {
 +    pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
 +        Self {
 +            conf_disallowed,
 +            disallowed: DefIdMap::default(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        for (index, conf) in self.conf_disallowed.iter().enumerate() {
 +            let segs: Vec<_> = conf.path().split("::").collect();
-                 diag.note(&format!("{reason} (from clippy.toml)"));
++            for id in clippy_utils::def_path_def_ids(cx, &segs) {
 +                self.disallowed.insert(id, index);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let uncalled_path = if let Some(parent) = get_parent_expr(cx, expr)
 +            && let ExprKind::Call(receiver, _) = parent.kind
 +            && receiver.hir_id == expr.hir_id
 +        {
 +            None
 +        } else {
 +            path_def_id(cx, expr)
 +        };
 +        let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else {
 +            return
 +        };
 +        let conf = match self.disallowed.get(&def_id) {
 +            Some(&index) => &self.conf_disallowed[index],
 +            None => return,
 +        };
 +        let msg = format!("use of a disallowed method `{}`", conf.path());
 +        span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| {
 +            if let Some(reason) = conf.reason() {
++                diag.note(reason);
 +            }
 +        });
 +    }
 +}
index c7131fc164d3e47d08feb845f720245d9e94e829,0000000000000000000000000000000000000000..aee3d8c4f08527234f622a6ea81dc6f551a9920f
mode 100644,000000..100644
--- /dev/null
@@@ -1,134 -1,0 +1,136 @@@
- use rustc_hir::def::{Namespace, Res};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +
 +use rustc_data_structures::fx::FxHashMap;
-     def_ids: FxHashMap<DefId, Option<String>>,
-     prim_tys: FxHashMap<PrimTy, Option<String>>,
++use rustc_hir::def::Res;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +use crate::utils::conf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Denies the configured types in clippy.toml.
 +    ///
 +    /// Note: Even though this lint is warn-by-default, it will only trigger if
 +    /// types are defined in the clippy.toml file.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some types are undesirable in certain contexts.
 +    ///
 +    /// ### Example:
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
 +    /// disallowed-types = [
 +    ///     # Can use a string as the path of the disallowed type.
 +    ///     "std::collections::BTreeMap",
 +    ///     # Can also use an inline table with a `path` key.
 +    ///     { path = "std::net::TcpListener" },
 +    ///     # When using an inline table, can add a `reason` for why the type
 +    ///     # is disallowed.
 +    ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
 +    /// ]
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// use std::collections::BTreeMap;
 +    /// // or its use
 +    /// let x = std::collections::BTreeMap::new();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // A similar type that is allowed by the config
 +    /// use std::collections::HashMap;
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub DISALLOWED_TYPES,
 +    style,
 +    "use of disallowed types"
 +}
 +#[derive(Clone, Debug)]
 +pub struct DisallowedTypes {
 +    conf_disallowed: Vec<conf::DisallowedPath>,
-                 if let Some(reason) = self.def_ids.get(did) {
-                     emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
++    def_ids: FxHashMap<DefId, usize>,
++    prim_tys: FxHashMap<PrimTy, usize>,
 +}
 +
 +impl DisallowedTypes {
 +    pub fn new(conf_disallowed: Vec<conf::DisallowedPath>) -> Self {
 +        Self {
 +            conf_disallowed,
 +            def_ids: FxHashMap::default(),
 +            prim_tys: FxHashMap::default(),
 +        }
 +    }
 +
 +    fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
 +        match res {
 +            Res::Def(_, did) => {
-                 if let Some(reason) = self.prim_tys.get(prim) {
-                     emit(cx, prim.name_str(), span, reason.as_deref());
++                if let Some(&index) = self.def_ids.get(did) {
++                    emit(cx, &cx.tcx.def_path_str(*did), span, &self.conf_disallowed[index]);
 +                }
 +            },
 +            Res::PrimTy(prim) => {
-         for conf in &self.conf_disallowed {
++                if let Some(&index) = self.prim_tys.get(prim) {
++                    emit(cx, prim.name_str(), span, &self.conf_disallowed[index]);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
-             let reason = conf.reason().map(|reason| format!("{reason} (from clippy.toml)"));
-             match clippy_utils::def_path_res(cx, &segs, Some(Namespace::TypeNS)) {
-                 Res::Def(_, id) => {
-                     self.def_ids.insert(id, reason);
-                 },
-                 Res::PrimTy(ty) => {
-                     self.prim_tys.insert(ty, reason);
-                 },
-                 _ => {},
++        for (index, conf) in self.conf_disallowed.iter().enumerate() {
 +            let segs: Vec<_> = conf.path().split("::").collect();
- fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
++
++            for res in clippy_utils::def_path_res(cx, &segs) {
++                match res {
++                    Res::Def(_, id) => {
++                        self.def_ids.insert(id, index);
++                    },
++                    Res::PrimTy(ty) => {
++                        self.prim_tys.insert(ty, index);
++                    },
++                    _ => {},
++                }
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
 +            self.check_res_emit(cx, &path.res, item.span);
 +        }
 +    }
 +
 +    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
 +        if let TyKind::Path(path) = &ty.kind {
 +            self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
 +        }
 +    }
 +
 +    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) {
 +        self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
 +    }
 +}
 +
-             if let Some(reason) = reason {
++fn emit(cx: &LateContext<'_>, name: &str, span: Span, conf: &conf::DisallowedPath) {
 +    span_lint_and_then(
 +        cx,
 +        DISALLOWED_TYPES,
 +        span,
 +        &format!("`{name}` is not allowed according to config"),
 +        |diag| {
++            if let Some(reason) = conf.reason() {
 +                diag.note(reason);
 +            }
 +        },
 +    );
 +}
index daaab79fef9ae880e1c12d392409d20b5c3409f7,0000000000000000000000000000000000000000..4557e43288542eac8e232f059e0257039ce27ddd
mode 100644,000000..100644
--- /dev/null
@@@ -1,908 -1,0 +1,937 @@@
- use rustc_errors::{Applicability, Handler, MultiSpan, SuggestionStyle};
 +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;
-     NEEDLESS_DOCTEST_MAIN
++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,
++    style,
++    "`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,
-         let headers = check_attrs(cx, &self.valid_idents, attrs);
++    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());
-                     lint_for_missing_headers(
-                         cx,
-                         item.owner_id.def_id,
-                         item.span,
-                         sig,
-                         headers,
-                         Some(body_id),
-                         fpu.panic_span,
-                     );
++        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);
-             hir::ItemKind::Trait(_, unsafety, ..) => {
-                 if !headers.safety && unsafety == hir::Unsafety::Unsafe {
-                     span_lint(
-                         cx,
-                         MISSING_SAFETY_DOC,
-                         item.span,
-                         "docs for unsafe trait missing `# Safety` section",
-                     );
-                 }
++                    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();
 +            },
-         let headers = check_attrs(cx, &self.valid_idents, attrs);
++            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());
-                 lint_for_missing_headers(cx, item.owner_id.def_id, item.span, sig, headers, None, None);
++        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) {
-         let headers = check_attrs(cx, &self.valid_idents, attrs);
++                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());
-             lint_for_missing_headers(
-                 cx,
-                 item.owner_id.def_id,
-                 item.span,
-                 sig,
-                 headers,
-                 Some(body_id),
-                 fpu.panic_span,
-             );
++        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);
- fn lint_for_missing_headers<'tcx>(
-     cx: &LateContext<'tcx>,
++            lint_for_missing_headers(cx, item.owner_id.def_id, sig, headers, Some(body_id), fpu.panic_span);
 +        }
 +    }
 +}
 +
-     span: impl Into<MultiSpan> + Copy,
++fn lint_for_missing_headers(
++    cx: &LateContext<'_>,
 +    def_id: LocalDefId,
-     if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
-         span_lint(
 +    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",
- fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
++        ),
++        (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::Opaque(_, subs) = ret_ty.kind();
 +                if let Some(gen) = subs.types().next();
 +                if let ty::Generator(_, subs, _) = gen.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,
 +}
 +
-             return DocHeaders {
-                 safety: true,
-                 errors: true,
-                 panics: true,
-             };
++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 DocHeaders::default();
++            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() {
-     check_doc(cx, valid_idents, events, &spans)
++        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 223545fa79846cb4b5ac40f90a4e37571debfa39,0000000000000000000000000000000000000000..b77b5621b4c68be05a7fef4177687bbb13e823e4
mode 100644,000000..100644
--- /dev/null
@@@ -1,305 -1,0 +1,305 @@@
-             if let Some(&(ref mod_name, ref mod_camel)) = self.modules.last() {
 +//! lint on enum variants that are prefixed or suffixed by the same characters
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::source::is_present_in_source;
 +use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start};
 +use rustc_hir::{EnumDef, Item, ItemKind, Variant};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::Symbol;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects enumeration variants that are prefixed or suffixed
 +    /// by the same characters.
 +    ///
 +    /// ### Why is this bad?
 +    /// Enumeration variant names should specify their variant,
 +    /// not repeat the enumeration name.
 +    ///
 +    /// ### Limitations
 +    /// Characters with no casing will be considered when comparing prefixes/suffixes
 +    /// This applies to numbers and non-ascii characters without casing
 +    /// e.g. `Foo1` and `Foo2` is considered to have different prefixes
 +    /// (the prefixes are `Foo1` and `Foo2` respectively), as also `Bar螃`, `Bar蟹`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Cake {
 +    ///     BlackForestCake,
 +    ///     HummingbirdCake,
 +    ///     BattenbergCake,
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// enum Cake {
 +    ///     BlackForest,
 +    ///     Hummingbird,
 +    ///     Battenberg,
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ENUM_VARIANT_NAMES,
 +    style,
 +    "enums where all variants share a prefix/postfix"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects type names that are prefixed or suffixed by the
 +    /// containing module's name.
 +    ///
 +    /// ### Why is this bad?
 +    /// It requires the user to type the module name twice.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// mod cake {
 +    ///     struct BlackForestCake;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// mod cake {
 +    ///     struct BlackForest;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub MODULE_NAME_REPETITIONS,
 +    pedantic,
 +    "type names prefixed/postfixed with their containing module's name"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for modules that have the same name as their
 +    /// parent module
 +    ///
 +    /// ### Why is this bad?
 +    /// A typical beginner mistake is to have `mod foo;` and
 +    /// again `mod foo { ..
 +    /// }` in `foo.rs`.
 +    /// The expectation is that items inside the inner `mod foo { .. }` are then
 +    /// available
 +    /// through `foo::x`, but they are only available through
 +    /// `foo::foo::x`.
 +    /// If this is done on purpose, it would be better to choose a more
 +    /// representative module name.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // lib.rs
 +    /// mod foo;
 +    /// // foo.rs
 +    /// mod foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MODULE_INCEPTION,
 +    style,
 +    "modules that have the same name as their parent module"
 +}
 +
 +pub struct EnumVariantNames {
 +    modules: Vec<(Symbol, String)>,
 +    threshold: u64,
 +    avoid_breaking_exported_api: bool,
 +}
 +
 +impl EnumVariantNames {
 +    #[must_use]
 +    pub fn new(threshold: u64, avoid_breaking_exported_api: bool) -> Self {
 +        Self {
 +            modules: Vec::new(),
 +            threshold,
 +            avoid_breaking_exported_api,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(EnumVariantNames => [
 +    ENUM_VARIANT_NAMES,
 +    MODULE_NAME_REPETITIONS,
 +    MODULE_INCEPTION
 +]);
 +
 +fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
 +    let name = variant.ident.name.as_str();
 +    let item_name_chars = item_name.chars().count();
 +
 +    if count_match_start(item_name, name).char_count == item_name_chars
 +        && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
 +        && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
 +    {
 +        span_lint(
 +            cx,
 +            ENUM_VARIANT_NAMES,
 +            variant.span,
 +            "variant name starts with the enum's name",
 +        );
 +    }
 +}
 +
 +fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>) {
 +    let name = variant.ident.name.as_str();
 +    let item_name_chars = item_name.chars().count();
 +
 +    if count_match_end(item_name, name).char_count == item_name_chars {
 +        span_lint(
 +            cx,
 +            ENUM_VARIANT_NAMES,
 +            variant.span,
 +            "variant name ends with the enum's name",
 +        );
 +    }
 +}
 +
 +fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_name: &str, span: Span) {
 +    if (def.variants.len() as u64) < threshold {
 +        return;
 +    }
 +
 +    let first = &def.variants[0].ident.name.as_str();
 +    let mut pre = camel_case_split(first);
 +    let mut post = pre.clone();
 +    post.reverse();
 +    for var in def.variants {
 +        check_enum_start(cx, item_name, var);
 +        check_enum_end(cx, item_name, var);
 +        let name = var.ident.name.as_str();
 +
 +        let variant_split = camel_case_split(name);
 +        if variant_split.len() == 1 {
 +            return;
 +        }
 +
 +        pre = pre
 +            .iter()
 +            .zip(variant_split.iter())
 +            .take_while(|(a, b)| a == b)
 +            .map(|e| *e.0)
 +            .collect();
 +        post = post
 +            .iter()
 +            .zip(variant_split.iter().rev())
 +            .take_while(|(a, b)| a == b)
 +            .map(|e| *e.0)
 +            .collect();
 +    }
 +    let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) {
 +        (true, true) => return,
 +        (false, _) => ("pre", pre.join("")),
 +        (true, false) => {
 +            post.reverse();
 +            ("post", post.join(""))
 +        },
 +    };
 +    span_lint_and_help(
 +        cx,
 +        ENUM_VARIANT_NAMES,
 +        span,
 +        &format!("all variants have the same {what}fix: `{value}`"),
 +        None,
 +        &format!(
 +            "remove the {what}fixes and use full paths to \
 +             the variants instead of glob imports"
 +        ),
 +    );
 +}
 +
 +#[must_use]
 +fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
 +    prefixes.iter().all(|p| p == &"" || p == &"_")
 +}
 +
 +#[must_use]
 +fn to_camel_case(item_name: &str) -> String {
 +    let mut s = String::new();
 +    let mut up = true;
 +    for c in item_name.chars() {
 +        if c.is_uppercase() {
 +            // we only turn snake case text into CamelCase
 +            return item_name.to_string();
 +        }
 +        if c == '_' {
 +            up = true;
 +            continue;
 +        }
 +        if up {
 +            up = false;
 +            s.extend(c.to_uppercase());
 +        } else {
 +            s.push(c);
 +        }
 +    }
 +    s
 +}
 +
 +impl LateLintPass<'_> for EnumVariantNames {
 +    fn check_item_post(&mut self, _cx: &LateContext<'_>, _item: &Item<'_>) {
 +        let last = self.modules.pop();
 +        assert!(last.is_some());
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        let item_name = item.ident.name.as_str();
 +        let item_camel = to_camel_case(item_name);
 +        if !item.span.from_expansion() && is_present_in_source(cx, item.span) {
++            if let Some((mod_name, mod_camel)) = self.modules.last() {
 +                // constants don't have surrounding modules
 +                if !mod_camel.is_empty() {
 +                    if mod_name == &item.ident.name {
 +                        if let ItemKind::Mod(..) = item.kind {
 +                            span_lint(
 +                                cx,
 +                                MODULE_INCEPTION,
 +                                item.span,
 +                                "module has the same name as its containing module",
 +                            );
 +                        }
 +                    }
 +                    // The `module_name_repetitions` lint should only trigger if the item has the module in its
 +                    // name. Having the same name is accepted.
 +                    if cx.tcx.visibility(item.owner_id).is_public() && item_camel.len() > mod_camel.len() {
 +                        let matching = count_match_start(mod_camel, &item_camel);
 +                        let rmatching = count_match_end(mod_camel, &item_camel);
 +                        let nchars = mod_camel.chars().count();
 +
 +                        let is_word_beginning = |c: char| c == '_' || c.is_uppercase() || c.is_numeric();
 +
 +                        if matching.char_count == nchars {
 +                            match item_camel.chars().nth(nchars) {
 +                                Some(c) if is_word_beginning(c) => span_lint(
 +                                    cx,
 +                                    MODULE_NAME_REPETITIONS,
 +                                    item.span,
 +                                    "item name starts with its containing module's name",
 +                                ),
 +                                _ => (),
 +                            }
 +                        }
 +                        if rmatching.char_count == nchars {
 +                            span_lint(
 +                                cx,
 +                                MODULE_NAME_REPETITIONS,
 +                                item.span,
 +                                "item name ends with its containing module's name",
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        if let ItemKind::Enum(ref def, _) = item.kind {
 +            if !(self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(item.owner_id.def_id)) {
 +                check_variant(cx, self.threshold, def, item_name, item.span);
 +            }
 +        }
 +        self.modules.push((item.ident.name, item_camel));
 +    }
 +}
index b40cb7cddaf178155a41bb54f4459cf5b9895ddb,0000000000000000000000000000000000000000..c691e6c5402d4808fc4b0be6ec2bc2079b5c1f41
mode 100644,000000..100644
--- /dev/null
@@@ -1,102 -1,0 +1,113 @@@
- use if_chain::if_chain;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::ty::implements_trait;
-         if_chain! {
-             if !in_external_macro(cx.sess(), expr.span);
-             if let ExprKind::Let(let_expr) = expr.kind;
-             if unary_pattern(let_expr.pat);
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Pat, PatKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for pattern matchings that can be expressed using equality.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// * It reads better and has less cognitive load because equality won't cause binding.
 +    /// * It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely
 +    /// criticized for increasing the cognitive load of reading the code.
 +    /// * Equality is a simple bool expression and can be merged with `&&` and `||` and
 +    /// reuse if blocks
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if let Some(2) = x {
 +    ///     do_thing();
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// if x == Some(2) {
 +    ///     do_thing();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub EQUATABLE_IF_LET,
 +    nursery,
 +    "using pattern matching instead of equality"
 +}
 +
 +declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]);
 +
 +/// detects if pattern matches just one thing
 +fn unary_pattern(pat: &Pat<'_>) -> bool {
 +    fn array_rec(pats: &[Pat<'_>]) -> bool {
 +        pats.iter().all(unary_pattern)
 +    }
 +    match &pat.kind {
 +        PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => {
 +            false
 +        },
 +        PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
 +        PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
 +        PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
 +        PatKind::Path(_) | PatKind::Lit(_) => true,
 +    }
 +}
 +
 +fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
 +    if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
 +        implements_trait(cx, ty, def_id, &[other.into()])
 +    } else {
 +        false
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for PatternEquality {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-             if is_structural_partial_eq(cx, exp_ty, pat_ty);
-             then {
++        if !in_external_macro(cx.sess(), expr.span)
++            && let ExprKind::Let(let_expr) = expr.kind
++            && unary_pattern(let_expr.pat) {
 +            let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
 +            let pat_ty = cx.typeck_results().pat_ty(let_expr.pat);
-                 let mut applicability = Applicability::MachineApplicable;
++            let mut applicability = Applicability::MachineApplicable;
 +
++            if is_structural_partial_eq(cx, exp_ty, pat_ty) {
 +                let pat_str = match let_expr.pat.kind {
 +                    PatKind::Struct(..) => format!(
 +                        "({})",
 +                        snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                    ),
 +                    _ => snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(),
 +                };
 +                span_lint_and_sugg(
 +                    cx,
 +                    EQUATABLE_IF_LET,
 +                    expr.span,
 +                    "this pattern matching can be expressed using equality",
 +                    "try",
 +                    format!(
 +                        "{} == {pat_str}",
 +                        snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                    ),
 +                    applicability,
 +                );
++            } else {
++                span_lint_and_sugg(
++                    cx,
++                    EQUATABLE_IF_LET,
++                    expr.span,
++                    "this pattern matching can be expressed using `matches!`",
++                    "try",
++                    format!(
++                        "matches!({}, {})",
++                        snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
++                        snippet_with_context(cx, let_expr.pat.span, expr.span.ctxt(), "..", &mut applicability).0,
++                    ),
++                    applicability,
++                );
 +            }
 +        }
 +    }
 +}
index 7f1a4c4beb1f2dfeded29686c799bd3faac32762,0000000000000000000000000000000000000000..1d09adec12f3ffe7bdf58bb5785dc947aa86f0f9
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,191 @@@
-     fn fake_read(
-         &mut self,
-         _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
-         _: FakeReadCause,
-         _: HirId,
-     ) {
-     }
 +use clippy_utils::diagnostics::span_lint_hir;
 +use rustc_hir::intravisit;
 +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
 +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::FakeReadCause;
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, TraitRef, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::kw;
 +use rustc_target::spec::abi::Abi;
 +
 +#[derive(Copy, Clone)]
 +pub struct BoxedLocal {
 +    pub too_large_for_stack: u64,
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `Box<T>` where an unboxed `T` would
 +    /// work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is an unnecessary allocation, and bad for
 +    /// performance. It is only necessary to allocate if you wish to move the box
 +    /// into something.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: Box<u32>) {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo(x: u32) {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub BOXED_LOCAL,
 +    perf,
 +    "using `Box<T>` where unnecessary"
 +}
 +
 +fn is_non_trait_box(ty: Ty<'_>) -> bool {
 +    ty.is_box() && !ty.boxed_ty().is_trait()
 +}
 +
 +struct EscapeDelegate<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    set: HirIdSet,
 +    trait_self_ty: Option<Ty<'tcx>>,
 +    too_large_for_stack: u64,
 +}
 +
 +impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: intravisit::FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        hir_id: HirId,
 +    ) {
 +        if let Some(header) = fn_kind.header() {
 +            if header.abi != Abi::Rust {
 +                return;
 +            }
 +        }
 +
 +        let parent_id = cx.tcx.hir().get_parent_item(hir_id).def_id;
 +        let parent_node = cx.tcx.hir().find_by_def_id(parent_id);
 +
 +        let mut trait_self_ty = None;
 +        if let Some(Node::Item(item)) = parent_node {
 +            // If the method is an impl for a trait, don't warn.
 +            if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind {
 +                return;
 +            }
 +
 +            // find `self` ty for this trait if relevant
 +            if let ItemKind::Trait(_, _, _, _, items) = item.kind {
 +                for trait_item in items {
 +                    if trait_item.id.hir_id() == hir_id {
 +                        // be sure we have `self` parameter in this function
 +                        if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
 +                            trait_self_ty = Some(
 +                                TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id())
 +                                    .self_ty()
 +                                    .skip_binder(),
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        let mut v = EscapeDelegate {
 +            cx,
 +            set: HirIdSet::default(),
 +            trait_self_ty,
 +            too_large_for_stack: self.too_large_for_stack,
 +        };
 +
 +        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
 +        let infcx = cx.tcx.infer_ctxt().build();
 +        ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
 +
 +        for node in v.set {
 +            span_lint_hir(
 +                cx,
 +                BOXED_LOCAL,
 +                node,
 +                cx.tcx.hir().span(node),
 +                "local variable doesn't need to be boxed here",
 +            );
 +        }
 +    }
 +}
 +
 +// TODO: Replace with Map::is_argument(..) when it's fixed
 +fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
 +    match map.find(id) {
 +        Some(Node::Pat(Pat {
 +            kind: PatKind::Binding(..),
 +            ..
 +        })) => (),
 +        _ => return false,
 +    }
 +
 +    matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_)))
 +}
 +
 +impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
 +    fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        if cmt.place.projections.is_empty() {
 +            if let PlaceBase::Local(lid) = cmt.place.base {
 +                self.set.remove(&lid);
 +            }
 +        }
 +    }
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
 +        if cmt.place.projections.is_empty() {
 +            if let PlaceBase::Local(lid) = cmt.place.base {
 +                self.set.remove(&lid);
 +            }
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        if cmt.place.projections.is_empty() {
 +            let map = &self.cx.tcx.hir();
 +            if is_argument(*map, cmt.hir_id) {
 +                // Skip closure arguments
 +                let parent_id = map.get_parent_node(cmt.hir_id);
 +                if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
 +                    return;
 +                }
 +
 +                // skip if there is a `self` parameter binding to a type
 +                // that contains `Self` (i.e.: `self: Box<Self>`), see #4804
 +                if let Some(trait_self_ty) = self.trait_self_ty {
 +                    if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
 +                        return;
 +                    }
 +                }
 +
 +                if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
 +                    self.set.insert(cmt.hir_id);
 +                }
 +            }
 +        }
 +    }
 +
++    fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
 +    fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
 +        // Large types need to be boxed to avoid stack overflows.
 +        if ty.is_box() {
 +            self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
 +        } else {
 +            false
 +        }
 +    }
 +}
index 453471c8cdda6f4b6a09a706f2c17caf5bdb2814,0000000000000000000000000000000000000000..fc2912f696e0384baa554564d683d4ba23f80fcd
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,183 @@@
- use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
- use rustc_lint::{EarlyContext, EarlyLintPass};
 +use clippy_utils::diagnostics::span_lint_and_help;
- use rustc_span::{sym, Span};
++use clippy_utils::{get_parent_as_impl, has_repr_attr, is_bool};
++use rustc_hir::intravisit::FnKind;
++use rustc_hir::{Body, FnDecl, HirId, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty};
++use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
-     fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
-         match fn_sig.header.ext {
-             Extern::Implicit(_) | Extern::Explicit(_, _) => return,
-             Extern::None => (),
++use rustc_span::Span;
++use rustc_target::spec::abi::Abi;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for excessive
 +    /// use of bools in structs.
 +    ///
 +    /// ### Why is this bad?
 +    /// Excessive bools in a struct
 +    /// is often a sign that it's used as a state machine,
 +    /// which is much better implemented as an enum.
 +    /// If it's not the case, excessive bools usually benefit
 +    /// from refactoring into two-variant enums for better
 +    /// readability and API.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S {
 +    ///     is_pending: bool,
 +    ///     is_processing: bool,
 +    ///     is_finished: bool,
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// enum S {
 +    ///     Pending,
 +    ///     Processing,
 +    ///     Finished,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub STRUCT_EXCESSIVE_BOOLS,
 +    pedantic,
 +    "using too many bools in a struct"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for excessive use of
 +    /// bools in function definitions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calls to such functions
 +    /// are confusing and error prone, because it's
 +    /// hard to remember argument order and you have
 +    /// no type system support to back you up. Using
 +    /// two-variant enums instead of bools often makes
 +    /// API easier to use.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn f(is_round: bool, is_hot: bool) { ... }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// enum Shape {
 +    ///     Round,
 +    ///     Spiky,
 +    /// }
 +    ///
 +    /// enum Temperature {
 +    ///     Hot,
 +    ///     IceCold,
 +    /// }
 +    ///
 +    /// fn f(shape: Shape, temperature: Temperature) { ... }
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub FN_PARAMS_EXCESSIVE_BOOLS,
 +    pedantic,
 +    "using too many bools in function parameters"
 +}
 +
 +pub struct ExcessiveBools {
 +    max_struct_bools: u64,
 +    max_fn_params_bools: u64,
 +}
 +
++#[derive(Eq, PartialEq, Debug, Copy, Clone)]
++enum Kind {
++    Struct,
++    Fn,
++}
++
 +impl ExcessiveBools {
 +    #[must_use]
 +    pub fn new(max_struct_bools: u64, max_fn_params_bools: u64) -> Self {
 +        Self {
 +            max_struct_bools,
 +            max_fn_params_bools,
 +        }
 +    }
 +
-         let fn_sig_bools = fn_sig
-             .decl
-             .inputs
-             .iter()
-             .filter(|param| is_bool_ty(&param.ty))
-             .count()
-             .try_into()
-             .unwrap();
-         if self.max_fn_params_bools < fn_sig_bools {
++    fn too_many_bools<'tcx>(&self, tys: impl Iterator<Item = &'tcx Ty<'tcx>>, kind: Kind) -> bool {
++        if let Ok(bools) = tys.filter(|ty| is_bool(ty)).count().try_into() {
++            (if Kind::Fn == kind {
++                self.max_fn_params_bools
++            } else {
++                self.max_struct_bools
++            }) < bools
++        } else {
++            false
 +        }
++    }
 +
- fn is_bool_ty(ty: &Ty) -> bool {
-     if let TyKind::Path(None, path) = &ty.kind {
-         if let [name] = path.segments.as_slice() {
-             return name.ident.name == sym::bool;
++    fn check_fn_sig(&self, cx: &LateContext<'_>, fn_decl: &FnDecl<'_>, span: Span) {
++        if !span.from_expansion() && self.too_many_bools(fn_decl.inputs.iter(), Kind::Fn) {
 +            span_lint_and_help(
 +                cx,
 +                FN_PARAMS_EXCESSIVE_BOOLS,
 +                span,
 +                &format!("more than {} bools in function parameters", self.max_fn_params_bools),
 +                None,
 +                "consider refactoring bools into two-variant enums",
 +            );
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
 +
-     false
- }
++impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        if item.span.from_expansion() {
++            return;
++        }
++        if let ItemKind::Struct(variant_data, _) = &item.kind {
++            if has_repr_attr(cx, item.hir_id()) {
++                return;
++            }
++
++            if self.too_many_bools(variant_data.fields().iter().map(|field| field.ty), Kind::Struct) {
++                span_lint_and_help(
++                    cx,
++                    STRUCT_EXCESSIVE_BOOLS,
++                    item.span,
++                    &format!("more than {} bools in a struct", self.max_struct_bools),
++                    None,
++                    "consider using a state machine or refactoring bools into two-variant enums",
++                );
++            }
 +        }
 +    }
- impl EarlyLintPass for ExcessiveBools {
-     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-         if item.span.from_expansion() {
-             return;
 +
-         match &item.kind {
-             ItemKind::Struct(variant_data, _) => {
-                 if item.attrs.iter().any(|attr| attr.has_name(sym::repr)) {
-                     return;
-                 }
++    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, trait_item: &'tcx TraitItem<'tcx>) {
++        // functions with a body are already checked by `check_fn`
++        if let TraitItemKind::Fn(fn_sig, TraitFn::Required(_)) = &trait_item.kind
++            && fn_sig.header.abi == Abi::Rust
++            {
++            self.check_fn_sig(cx, fn_sig.decl, fn_sig.span);
 +        }
-                 let struct_bools = variant_data
-                     .fields()
-                     .iter()
-                     .filter(|field| is_bool_ty(&field.ty))
-                     .count()
-                     .try_into()
-                     .unwrap();
-                 if self.max_struct_bools < struct_bools {
-                     span_lint_and_help(
-                         cx,
-                         STRUCT_EXCESSIVE_BOOLS,
-                         item.span,
-                         &format!("more than {} bools in a struct", self.max_struct_bools),
-                         None,
-                         "consider using a state machine or refactoring bools into two-variant enums",
-                     );
-                 }
-             },
-             ItemKind::Impl(box Impl {
-                 of_trait: None, items, ..
-             })
-             | ItemKind::Trait(box Trait { items, .. }) => {
-                 for item in items {
-                     if let AssocItemKind::Fn(box Fn { sig, .. }) = &item.kind {
-                         self.check_fn_sig(cx, sig, item.span);
-                     }
-                 }
-             },
-             ItemKind::Fn(box Fn { sig, .. }) => self.check_fn_sig(cx, sig, item.span),
-             _ => (),
++    }
 +
++    fn check_fn(
++        &mut self,
++        cx: &LateContext<'tcx>,
++        fn_kind: FnKind<'tcx>,
++        fn_decl: &'tcx FnDecl<'tcx>,
++        _: &'tcx Body<'tcx>,
++        span: Span,
++        hir_id: HirId,
++    ) {
++        if let Some(fn_header) = fn_kind.header()
++            && fn_header.abi == Abi::Rust
++            && get_parent_as_impl(cx.tcx, hir_id)
++                .map_or(true,
++                    |impl_item| impl_item.of_trait.is_none()
++                )
++            {
++            self.check_fn_sig(cx, fn_decl, span);
 +        }
 +    }
 +}
index 0a633f242a5f1672a7d6c48ff680402685bc7e9a,0000000000000000000000000000000000000000..9a1058470e18e6155af9a4c5a1ee711dde9553da
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
- fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 +use clippy_utils::method_chain_args;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for impls of `From<..>` that contain `panic!()` or `unwrap()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `TryFrom` should be used if there's a possibility of failure.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo(i32);
 +    ///
 +    /// impl From<String> for Foo {
 +    ///     fn from(s: String) -> Self {
 +    ///         Foo(s.parse().unwrap())
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// struct Foo(i32);
 +    ///
 +    /// impl TryFrom<String> for Foo {
 +    ///     type Error = ();
 +    ///     fn try_from(s: String) -> Result<Self, Self::Error> {
 +    ///         if let Ok(parsed) = s.parse() {
 +    ///             Ok(Foo(parsed))
 +    ///         } else {
 +    ///             Err(())
 +    ///         }
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FALLIBLE_IMPL_FROM,
 +    nursery,
 +    "Warn on impls of `From<..>` that contain `panic!()` or `unwrap()`"
 +}
 +
 +declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        // check for `impl From<???> for ..`
 +        if_chain! {
 +            if let hir::ItemKind::Impl(impl_) = &item.kind;
 +            if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id);
 +            if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.def_id);
 +            then {
 +                lint_impl_body(cx, item.span, impl_.items);
 +            }
 +        }
 +    }
 +}
 +
++fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
 +    use rustc_hir::intravisit::{self, Visitor};
 +    use rustc_hir::{Expr, ImplItemKind};
 +
 +    struct FindPanicUnwrap<'a, 'tcx> {
 +        lcx: &'a LateContext<'tcx>,
 +        typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +        result: Vec<Span>,
 +    }
 +
 +    impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +            if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) {
 +                if is_panic(self.lcx, macro_call.def_id) {
 +                    self.result.push(expr.span);
 +                }
 +            }
 +
 +            // 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.lcx, receiver_ty, sym::Option)
 +                    || is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
 +                {
 +                    self.result.push(expr.span);
 +                }
 +            }
 +
 +            // and check sub-expressions
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    for impl_item in impl_items {
 +        if_chain! {
 +            if impl_item.ident.name == sym::from;
 +            if let ImplItemKind::Fn(_, body_id) =
 +                cx.tcx.hir().impl_item(impl_item.id).kind;
 +            then {
 +                // check the body for `begin_panic` or `unwrap`
 +                let body = cx.tcx.hir().body(body_id);
 +                let mut fpu = FindPanicUnwrap {
 +                    lcx: cx,
 +                    typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id),
 +                    result: Vec::new(),
 +                };
 +                fpu.visit_expr(body.value);
 +
 +                // if we've found one, lint
 +                if !fpu.result.is_empty() {
 +                    span_lint_and_then(
 +                        cx,
 +                        FALLIBLE_IMPL_FROM,
 +                        impl_span,
 +                        "consider implementing `TryFrom` instead",
 +                        move |diag| {
 +                            diag.help(
 +                                "`From` is intended for infallible conversions only. \
 +                                Use `TryFrom` if there's a possibility for the conversion to fail");
 +                            diag.span_note(fpu.result, "potential failure(s)");
 +                        });
 +                }
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..00f5ba56496ecd5486a2c395f3d28a4fdcf2e6a6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::ty::is_c_void;
++use clippy_utils::{match_def_path, path_def_id, paths};
++use rustc_hir::def_id::DefId;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::RawPtr;
++use rustc_middle::ty::TypeAndMut;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks if we're passing a `c_void` raw pointer to `{Box,Rc,Arc,Weak}::from_raw(_)`
++    ///
++    /// ### Why is this bad?
++    /// When dealing with `c_void` raw pointers in FFI, it is easy to run into the pitfall of calling `from_raw` with the `c_void` pointer.
++    /// The type signature of `Box::from_raw` is `fn from_raw(raw: *mut T) -> Box<T>`, so if you pass a `*mut c_void` you will get a `Box<c_void>` (and similarly for `Rc`, `Arc` and `Weak`).
++    /// For this to be safe, `c_void` would need to have the same memory layout as the original type, which is often not the case.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # use std::ffi::c_void;
++    /// let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
++    /// let _ = unsafe { Box::from_raw(ptr) };
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # use std::ffi::c_void;
++    /// # let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
++    /// let _ = unsafe { Box::from_raw(ptr as *mut usize) };
++    /// ```
++    ///
++    #[clippy::version = "1.66.0"]
++    pub FROM_RAW_WITH_VOID_PTR,
++    suspicious,
++    "creating a `Box` from a void raw pointer"
++}
++declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
++
++impl LateLintPass<'_> for FromRawWithVoidPtr {
++    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
++        if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
++        && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
++        && seg.ident.name == sym!(from_raw)
++        && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
++        && let arg_kind = cx.typeck_results().expr_ty(arg).kind()
++        && let RawPtr(TypeAndMut { ty, .. }) = arg_kind
++        && is_c_void(cx, *ty) {
++            let msg = format!("creating a `{type_str}` from a void raw pointer");
++            span_lint_and_help(cx, FROM_RAW_WITH_VOID_PTR, expr.span, &msg, Some(arg.span), "cast this to a pointer of the appropriate type");
++        }
++    }
++}
++
++/// Checks whether a `DefId` matches `Box`, `Rc`, `Arc`, or one of the `Weak` types.
++/// Returns a static string slice with the name of the type, if one was found.
++fn def_id_matches_type(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> {
++    // Box
++    if Some(def_id) == cx.tcx.lang_items().owned_box() {
++        return Some("Box");
++    }
++
++    if let Some(symbol) = cx.tcx.get_diagnostic_name(def_id) {
++        if symbol == sym::Arc {
++            return Some("Arc");
++        } else if symbol == sym::Rc {
++            return Some("Rc");
++        }
++    }
++
++    if match_def_path(cx, def_id, &paths::WEAK_RC) || match_def_path(cx, def_id, &paths::WEAK_ARC) {
++        Some("Weak")
++    } else {
++        None
++    }
++}
index 90911e0bf2595ca667b9b3f14b0a755c0e4290f6,0000000000000000000000000000000000000000..ae0e08334463803dbfce17b707bdbbab3fda0773
mode 100644,000000..100644
--- /dev/null
@@@ -1,322 -1,0 +1,322 @@@
-     #[clippy::version = "1.64.0"]
 +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"
 +}
 +
 +#[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,
 +]);
 +
 +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);
 +    }
 +
 +    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 bff69f9151846d6bfa4831a75a8f2e175951b5fc,0000000000000000000000000000000000000000..d22bede36b419944b372f84594dd294b926442e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,269 -1,0 +1,261 @@@
-         } else if is_public && !is_proc_macro(cx.sess(), attrs) && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::{DefIdSet, LocalDefId};
 +use rustc_hir::{self as hir, def::Res, QPath};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::{
 +    lint::in_external_macro,
 +    ty::{self, Ty},
 +};
 +use rustc_span::{sym, Span, Symbol};
 +
 +use clippy_utils::attrs::is_proc_macro;
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_must_use_ty;
 +use clippy_utils::visitors::for_each_expr;
 +use clippy_utils::{return_ty, trait_ref_of_method};
 +
 +use core::ops::ControlFlow;
 +
 +use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT};
 +
 +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
 +    if let hir::ItemKind::Fn(ref sig, _generics, ref body_id) = item.kind {
 +        let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if is_public && !is_proc_macro(cx.sess(), attrs) && !attrs.iter().any(|a| a.has_name(sym::no_mangle)) {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.owner_id.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this function could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +    if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind {
 +        let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
-         is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), pat.span, tys)
++        } else if is_public
++            && !is_proc_macro(cx.sess(), attrs)
++            && trait_ref_of_method(cx, item.owner_id.def_id).is_none()
 +        {
 +            check_must_use_candidate(
 +                cx,
 +                sig.decl,
 +                cx.tcx.hir().body(*body_id),
 +                item.span,
 +                item.owner_id.def_id,
 +                item.span.with_hi(sig.decl.output.span().hi()),
 +                "this method could have a `#[must_use]` attribute",
 +            );
 +        }
 +    }
 +}
 +
 +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +    if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind {
 +        let is_public = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 +        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
 +
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let attr = cx.tcx.get_attr(item.owner_id.to_def_id(), sym::must_use);
 +        if let Some(attr) = attr {
 +            check_needless_must_use(cx, sig.decl, item.hir_id(), item.span, fn_header_span, attr);
 +        } else if let hir::TraitFn::Provided(eid) = *eid {
 +            let body = cx.tcx.hir().body(eid);
 +            if attr.is_none() && is_public && !is_proc_macro(cx.sess(), attrs) {
 +                check_must_use_candidate(
 +                    cx,
 +                    sig.decl,
 +                    body,
 +                    item.span,
 +                    item.owner_id.def_id,
 +                    item.span.with_hi(sig.decl.output.span().hi()),
 +                    "this method could have a `#[must_use]` attribute",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_needless_must_use(
 +    cx: &LateContext<'_>,
 +    decl: &hir::FnDecl<'_>,
 +    item_id: hir::HirId,
 +    item_span: Span,
 +    fn_header_span: Span,
 +    attr: &Attribute,
 +) {
 +    if in_external_macro(cx.sess(), item_span) {
 +        return;
 +    }
 +    if returns_unit(decl) {
 +        span_lint_and_then(
 +            cx,
 +            MUST_USE_UNIT,
 +            fn_header_span,
 +            "this unit-returning function has a `#[must_use]` attribute",
 +            |diag| {
 +                diag.span_suggestion(attr.span, "remove the attribute", "", Applicability::MachineApplicable);
 +            },
 +        );
 +    } else if attr.value_str().is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) {
 +        span_lint_and_help(
 +            cx,
 +            DOUBLE_MUST_USE,
 +            fn_header_span,
 +            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
 +            None,
 +            "either add some descriptive text or remove the attribute",
 +        );
 +    }
 +}
 +
 +fn check_must_use_candidate<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: &'tcx hir::FnDecl<'_>,
 +    body: &'tcx hir::Body<'_>,
 +    item_span: Span,
 +    item_id: LocalDefId,
 +    fn_span: Span,
 +    msg: &str,
 +) {
 +    if has_mutable_arg(cx, body)
 +        || mutates_static(cx, body)
 +        || in_external_macro(cx.sess(), item_span)
 +        || returns_unit(decl)
 +        || !cx.effective_visibilities.is_exported(item_id)
 +        || is_must_use_ty(cx, return_ty(cx, cx.tcx.hir().local_def_id_to_hir_id(item_id)))
 +    {
 +        return;
 +    }
 +    span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
 +        if let Some(snippet) = snippet_opt(cx, fn_span) {
 +            diag.span_suggestion(
 +                fn_span,
 +                "add the attribute",
 +                format!("#[must_use] {snippet}"),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    });
 +}
 +
 +fn returns_unit(decl: &hir::FnDecl<'_>) -> bool {
 +    match decl.output {
 +        hir::FnRetTy::DefaultReturn(_) => true,
 +        hir::FnRetTy::Return(ty) => match ty.kind {
 +            hir::TyKind::Tup(tys) => tys.is_empty(),
 +            hir::TyKind::Never => true,
 +            _ => false,
 +        },
 +    }
 +}
 +
 +fn has_mutable_arg(cx: &LateContext<'_>, body: &hir::Body<'_>) -> bool {
 +    let mut tys = DefIdSet::default();
 +    body.params.iter().any(|param| is_mutable_pat(cx, param.pat, &mut tys))
 +}
 +
 +fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet) -> bool {
 +    if let hir::PatKind::Wild = pat.kind {
 +        return false; // ignore `_` patterns
 +    }
 +    if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
- fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, tys: &mut DefIdSet) -> bool {
++        is_mutable_ty(cx, cx.tcx.typeck(pat.hir_id.owner.def_id).pat_ty(pat), tys)
 +    } else {
 +        false
 +    }
 +}
 +
 +static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
 +
-                     && substs.types().any(|ty| is_mutable_ty(cx, ty, span, tys))
++fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
 +    match *ty.kind() {
 +        // primitive types are never mutable
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
 +        ty::Adt(adt, substs) => {
 +            tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
 +                || KNOWN_WRAPPER_TYS
 +                    .iter()
 +                    .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
-         ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, span, tys)),
-         ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, span, tys),
++                    && substs.types().any(|ty| is_mutable_ty(cx, ty, tys))
 +        },
-             mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, span, tys)
++        ty::Tuple(substs) => substs.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
++        ty::Array(ty, _) | ty::Slice(ty) => is_mutable_ty(cx, ty, tys),
 +        ty::RawPtr(ty::TypeAndMut { ty, mutbl }) | ty::Ref(_, ty, mutbl) => {
-                         && is_mutable_ty(
-                             cx,
-                             cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
-                             arg.span,
-                             &mut tys,
-                         )
++            mutbl == hir::Mutability::Mut || is_mutable_ty(cx, ty, tys)
 +        },
 +        // calling something constitutes a side effect, so return true on all callables
 +        // also never calls need not be used, so return true for them, too
 +        _ => true,
 +    }
 +}
 +
 +fn is_mutated_static(e: &hir::Expr<'_>) -> bool {
 +    use hir::ExprKind::{Field, Index, Path};
 +
 +    match e.kind {
 +        Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)),
 +        Path(_) => true,
 +        Field(inner, _) | Index(inner, _) => is_mutated_static(inner),
 +        _ => false,
 +    }
 +}
 +
 +fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bool {
 +    for_each_expr(body.value, |e| {
 +        use hir::ExprKind::{AddrOf, Assign, AssignOp, Call, MethodCall};
 +
 +        match e.kind {
 +            Call(_, args) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in args {
 +                    if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
-                         && is_mutable_ty(
-                             cx,
-                             cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg),
-                             arg.span,
-                             &mut tys,
-                         )
++                        && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys)
 +                        && is_mutated_static(arg)
 +                    {
 +                        return ControlFlow::Break(());
 +                    }
 +                    tys.clear();
 +                }
 +                ControlFlow::Continue(())
 +            },
 +            MethodCall(_, receiver, args, _) => {
 +                let mut tys = DefIdSet::default();
 +                for arg in std::iter::once(receiver).chain(args.iter()) {
 +                    if cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
++                        && is_mutable_ty(cx, cx.tcx.typeck(arg.hir_id.owner.def_id).expr_ty(arg), &mut tys)
 +                        && is_mutated_static(arg)
 +                    {
 +                        return ControlFlow::Break(());
 +                    }
 +                    tys.clear();
 +                }
 +                ControlFlow::Continue(())
 +            },
 +            Assign(target, ..) | AssignOp(_, target, _) | AddrOf(_, hir::Mutability::Mut, target)
 +                if is_mutated_static(target) =>
 +            {
 +                ControlFlow::Break(())
 +            },
 +            _ => ControlFlow::Continue(()),
 +        }
 +    })
 +    .is_some()
 +}
index 5c63fb2acb117f08f729c1658edcd6ff9041aa4e,0000000000000000000000000000000000000000..f7e30b051a694a84d6be001e103f067a804d58b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,140 @@@
- use rustc_middle::ty::{self, Ty};
 +use rustc_errors::Diagnostic;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
- use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
++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;
-     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}>`"));
-             },
-         );
++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);
++            if variants_size[0].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(
++                            def.variants[variants_size[0].ind].span,
++                            format!("the largest variant contains at least {} bytes", variants_size[0].size),
++                        );
++
++                        for variant in &variants_size[1..] {
++                            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 94e06cf704ba24c4845ddae70501bc75babd33df,0000000000000000000000000000000000000000..64a4a3fa741bca2e6af1c40663564a3d5c3e1f83
mode 100644,000000..100644
--- /dev/null
@@@ -1,387 -1,0 +1,387 @@@
-         fn suggestion<'tcx>(
-             cx: &LateContext<'tcx>,
 +use std::borrow::Cow;
 +use std::collections::BTreeMap;
 +
 +use rustc_errors::Diagnostic;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
 +use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{Ty, TypeckResults};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::sym;
 +
 +use if_chain::if_chain;
 +
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_opt};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public `impl` or `fn` missing generalization
 +    /// over different hashers and implicitly defaulting to the default hashing
 +    /// algorithm (`SipHash`).
 +    ///
 +    /// ### Why is this bad?
 +    /// `HashMap` or `HashSet` with custom hashers cannot be
 +    /// used with them.
 +    ///
 +    /// ### Known problems
 +    /// Suggestions for replacing constructors can contain
 +    /// false-positives. Also applying suggestions can require modification of other
 +    /// pieces of code, possibly including external crates.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # use std::hash::{Hash, BuildHasher};
 +    /// # trait Serialize {};
 +    /// impl<K: Hash + Eq, V> Serialize for HashMap<K, V> { }
 +    ///
 +    /// pub fn foo(map: &mut HashMap<i32, i32>) { }
 +    /// ```
 +    /// could be rewritten as
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # use std::hash::{Hash, BuildHasher};
 +    /// # trait Serialize {};
 +    /// impl<K: Hash + Eq, V, S: BuildHasher> Serialize for HashMap<K, V, S> { }
 +    ///
 +    /// pub fn foo<S: BuildHasher>(map: &mut HashMap<i32, i32, S>) { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IMPLICIT_HASHER,
 +    pedantic,
 +    "missing generalization over different hashers"
 +}
 +
 +declare_lint_pass!(ImplicitHasher => [IMPLICIT_HASHER]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
 +    #[expect(clippy::cast_possible_truncation, clippy::too_many_lines)]
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        use rustc_span::BytePos;
 +
++        fn suggestion(
++            cx: &LateContext<'_>,
 +            diag: &mut Diagnostic,
 +            generics_span: Span,
 +            generics_suggestion_span: Span,
 +            target: &ImplicitHasherType<'_>,
 +            vis: ImplicitHasherConstructorVisitor<'_, '_, '_>,
 +        ) {
 +            let generics_snip = snippet(cx, generics_span, "");
 +            // trim `<` `>`
 +            let generics_snip = if generics_snip.is_empty() {
 +                ""
 +            } else {
 +                &generics_snip[1..generics_snip.len() - 1]
 +            };
 +
 +            multispan_sugg(
 +                diag,
 +                "consider adding a type parameter",
 +                vec![
 +                    (
 +                        generics_suggestion_span,
 +                        format!(
 +                            "<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
 +                            if generics_snip.is_empty() { "" } else { ", " },
 +                            if vis.suggestions.is_empty() {
 +                                ""
 +                            } else {
 +                                // request users to add `Default` bound so that generic constructors can be used
 +                                " + Default"
 +                            },
 +                        ),
 +                    ),
 +                    (
 +                        target.span(),
 +                        format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
 +                    ),
 +                ],
 +            );
 +
 +            if !vis.suggestions.is_empty() {
 +                multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
 +            }
 +        }
 +
 +        if !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
 +            return;
 +        }
 +
 +        match item.kind {
 +            ItemKind::Impl(impl_) => {
 +                let mut vis = ImplicitHasherTypeVisitor::new(cx);
 +                vis.visit_ty(impl_.self_ty);
 +
 +                for target in &vis.found {
 +                    if item.span.ctxt() != target.span().ctxt() {
 +                        return;
 +                    }
 +
 +                    let generics_suggestion_span = impl_.generics.span.substitute_dummy({
 +                        let pos = snippet_opt(cx, item.span.until(target.span()))
 +                            .and_then(|snip| Some(item.span.lo() + BytePos(snip.find("impl")? as u32 + 4)));
 +                        if let Some(pos) = pos {
 +                            Span::new(pos, pos, item.span.ctxt(), item.span.parent())
 +                        } else {
 +                            return;
 +                        }
 +                    });
 +
 +                    let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
 +                    for item in impl_.items.iter().map(|item| cx.tcx.hir().impl_item(item.id)) {
 +                        ctr_vis.visit_impl_item(item);
 +                    }
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        IMPLICIT_HASHER,
 +                        target.span(),
 +                        &format!(
 +                            "impl for `{}` should be generalized over different hashers",
 +                            target.type_name()
 +                        ),
 +                        move |diag| {
 +                            suggestion(cx, diag, impl_.generics.span, generics_suggestion_span, target, ctr_vis);
 +                        },
 +                    );
 +                }
 +            },
 +            ItemKind::Fn(ref sig, generics, body_id) => {
 +                let body = cx.tcx.hir().body(body_id);
 +
 +                for ty in sig.decl.inputs {
 +                    let mut vis = ImplicitHasherTypeVisitor::new(cx);
 +                    vis.visit_ty(ty);
 +
 +                    for target in &vis.found {
 +                        if in_external_macro(cx.sess(), generics.span) {
 +                            continue;
 +                        }
 +                        let generics_suggestion_span = generics.span.substitute_dummy({
 +                            let pos = snippet_opt(
 +                                cx,
 +                                Span::new(
 +                                    item.span.lo(),
 +                                    body.params[0].pat.span.lo(),
 +                                    item.span.ctxt(),
 +                                    item.span.parent(),
 +                                ),
 +                            )
 +                            .and_then(|snip| {
 +                                let i = snip.find("fn")?;
 +                                Some(item.span.lo() + BytePos((i + snip[i..].find('(')?) as u32))
 +                            })
 +                            .expect("failed to create span for type parameters");
 +                            Span::new(pos, pos, item.span.ctxt(), item.span.parent())
 +                        });
 +
 +                        let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
 +                        ctr_vis.visit_body(body);
 +
 +                        span_lint_and_then(
 +                            cx,
 +                            IMPLICIT_HASHER,
 +                            target.span(),
 +                            &format!(
 +                                "parameter of type `{}` should be generalized over different hashers",
 +                                target.type_name()
 +                            ),
 +                            move |diag| {
 +                                suggestion(cx, diag, generics.span, generics_suggestion_span, target, ctr_vis);
 +                            },
 +                        );
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +enum ImplicitHasherType<'tcx> {
 +    HashMap(Span, Ty<'tcx>, Cow<'static, str>, Cow<'static, str>),
 +    HashSet(Span, Ty<'tcx>, Cow<'static, str>),
 +}
 +
 +impl<'tcx> ImplicitHasherType<'tcx> {
 +    /// Checks that `ty` is a target type without a `BuildHasher`.
 +    fn new(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'_>) -> Option<Self> {
 +        if let TyKind::Path(QPath::Resolved(None, path)) = hir_ty.kind {
 +            let params: Vec<_> = path
 +                .segments
 +                .last()
 +                .as_ref()?
 +                .args
 +                .as_ref()?
 +                .args
 +                .iter()
 +                .filter_map(|arg| match arg {
 +                    GenericArg::Type(ty) => Some(ty),
 +                    _ => None,
 +                })
 +                .collect();
 +            let params_len = params.len();
 +
 +            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
 +
 +            if is_type_diagnostic_item(cx, ty, sym::HashMap) && params_len == 2 {
 +                Some(ImplicitHasherType::HashMap(
 +                    hir_ty.span,
 +                    ty,
 +                    snippet(cx, params[0].span, "K"),
 +                    snippet(cx, params[1].span, "V"),
 +                ))
 +            } else if is_type_diagnostic_item(cx, ty, sym::HashSet) && params_len == 1 {
 +                Some(ImplicitHasherType::HashSet(
 +                    hir_ty.span,
 +                    ty,
 +                    snippet(cx, params[0].span, "T"),
 +                ))
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn type_name(&self) -> &'static str {
 +        match *self {
 +            ImplicitHasherType::HashMap(..) => "HashMap",
 +            ImplicitHasherType::HashSet(..) => "HashSet",
 +        }
 +    }
 +
 +    fn type_arguments(&self) -> String {
 +        match *self {
 +            ImplicitHasherType::HashMap(.., ref k, ref v) => format!("{k}, {v}"),
 +            ImplicitHasherType::HashSet(.., ref t) => format!("{t}"),
 +        }
 +    }
 +
 +    fn ty(&self) -> Ty<'tcx> {
 +        match *self {
 +            ImplicitHasherType::HashMap(_, ty, ..) | ImplicitHasherType::HashSet(_, ty, ..) => ty,
 +        }
 +    }
 +
 +    fn span(&self) -> Span {
 +        match *self {
 +            ImplicitHasherType::HashMap(span, ..) | ImplicitHasherType::HashSet(span, ..) => span,
 +        }
 +    }
 +}
 +
 +struct ImplicitHasherTypeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    found: Vec<ImplicitHasherType<'tcx>>,
 +}
 +
 +impl<'a, 'tcx> ImplicitHasherTypeVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self { cx, found: vec![] }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ImplicitHasherTypeVisitor<'a, 'tcx> {
 +    fn visit_ty(&mut self, t: &'tcx hir::Ty<'_>) {
 +        if let Some(target) = ImplicitHasherType::new(self.cx, t) {
 +            self.found.push(target);
 +        }
 +
 +        walk_ty(self, t);
 +    }
 +
 +    fn visit_infer(&mut self, inf: &'tcx hir::InferArg) {
 +        if let Some(target) = ImplicitHasherType::new(self.cx, &inf.to_ty()) {
 +            self.found.push(target);
 +        }
 +
 +        walk_inf(self, inf);
 +    }
 +}
 +
 +/// Looks for default-hasher-dependent constructors like `HashMap::new`.
 +struct ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    target: &'b ImplicitHasherType<'tcx>,
 +    suggestions: BTreeMap<Span, String>,
 +}
 +
 +impl<'a, 'b, 'tcx> ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>, target: &'b ImplicitHasherType<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            target,
 +            suggestions: BTreeMap::new(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_body(&mut self, body: &'tcx Body<'_>) {
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body.id()));
 +        walk_body(self, body);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(fun, args) = e.kind;
 +            if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind;
 +            if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind;
 +            if let Some(ty_did) = ty_path.res.opt_def_id();
 +            then {
 +                if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) {
 +                    return;
 +                }
 +
 +                if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) {
 +                    if method.ident.name == sym::new {
 +                        self.suggestions
 +                            .insert(e.span, "HashMap::default()".to_string());
 +                    } else if method.ident.name == sym!(with_capacity) {
 +                        self.suggestions.insert(
 +                            e.span,
 +                            format!(
 +                                "HashMap::with_capacity_and_hasher({}, Default::default())",
 +                                snippet(self.cx, args[0].span, "capacity"),
 +                            ),
 +                        );
 +                    }
 +                } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) {
 +                    if method.ident.name == sym::new {
 +                        self.suggestions
 +                            .insert(e.span, "HashSet::default()".to_string());
 +                    } else if method.ident.name == sym!(with_capacity) {
 +                        self.suggestions.insert(
 +                            e.span,
 +                            format!(
 +                                "HashSet::with_capacity_and_hasher({}, Default::default())",
 +                                snippet(self.cx, args[0].span, "capacity"),
 +                            ),
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +
 +        walk_expr(self, e);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index c7b5badaae51b0c21a392cc9762a65be80c63016,0000000000000000000000000000000000000000..0d5099bde6de015b5aa069e5bdeb6611afd9d471
mode 100644,000000..100644
--- /dev/null
@@@ -1,277 -1,0 +1,277 @@@
- fn filter_lintable_slices<'a, 'tcx>(
-     cx: &'a LateContext<'tcx>,
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::IfLet;
 +use clippy_utils::ty::is_copy;
 +use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, 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;
 +use rustc_semver::RustcVersion;
 +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,
 +    nursery,
 +    "avoid indexing on slices which could be destructed"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct IndexRefutableSlice {
 +    max_suggested_slice: u64,
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl IndexRefutableSlice {
 +    pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> 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 meets_msrv(self.msrv, 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 af40a5a8187ee5dfa4adae5bbc9d7d28d981eb1d,0000000000000000000000000000000000000000..4cd7dff4cfd762c95352f230beb6b8b3f0652458
mode 100644,000000..100644
--- /dev/null
@@@ -1,204 -1,0 +1,200 @@@
- fn to_const_range<'tcx>(
-     cx: &LateContext<'tcx>,
-     range: higher::Range<'_>,
-     array_size: u128,
- ) -> (Option<u128>, Option<u128>) {
 +//! lint on indexing and slicing operations
 +
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::higher;
 +use rustc_ast::ast::RangeLimits;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for out of bounds array indexing with a constant
 +    /// index.
 +    ///
 +    /// ### Why is this bad?
 +    /// This will always panic at runtime.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// let x = [1, 2, 3, 4];
 +    ///
 +    /// x[9];
 +    /// &x[2..9];
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = [1, 2, 3, 4];
 +    /// // Index within bounds
 +    ///
 +    /// x[0];
 +    /// x[3];
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OUT_OF_BOUNDS_INDEXING,
 +    correctness,
 +    "out of bounds constant indexing"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of indexing or slicing. Arrays are special cases, this lint
 +    /// does report on arrays if we can tell that slicing operations are in bounds and does not
 +    /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indexing and slicing can panic at runtime and there are
 +    /// safe alternatives.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// // Vector
 +    /// let x = vec![0; 5];
 +    ///
 +    /// x[2];
 +    /// &x[2..100];
 +    ///
 +    /// // Array
 +    /// let y = [0, 1, 2, 3];
 +    ///
 +    /// &y[10..100];
 +    /// &y[10..];
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    ///
 +    /// # let x = vec![0; 5];
 +    /// # let y = [0, 1, 2, 3];
 +    /// x.get(2);
 +    /// x.get(2..100);
 +    ///
 +    /// y.get(10);
 +    /// y.get(10..100);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INDEXING_SLICING,
 +    restriction,
 +    "indexing/slicing usage"
 +}
 +
 +declare_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
 +            return;
 +        }
 +
 +        if let ExprKind::Index(array, index) = &expr.kind {
 +            let ty = cx.typeck_results().expr_ty(array).peel_refs();
 +            if let Some(range) = higher::Range::hir(index) {
 +                // Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
 +                if let ty::Array(_, s) = ty.kind() {
 +                    let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
 +                        size.into()
 +                    } else {
 +                        return;
 +                    };
 +
 +                    let const_range = to_const_range(cx, range, size);
 +
 +                    if let (Some(start), _) = const_range {
 +                        if start > size {
 +                            span_lint(
 +                                cx,
 +                                OUT_OF_BOUNDS_INDEXING,
 +                                range.start.map_or(expr.span, |start| start.span),
 +                                "range is out of bounds",
 +                            );
 +                            return;
 +                        }
 +                    }
 +
 +                    if let (_, Some(end)) = const_range {
 +                        if end > size {
 +                            span_lint(
 +                                cx,
 +                                OUT_OF_BOUNDS_INDEXING,
 +                                range.end.map_or(expr.span, |end| end.span),
 +                                "range is out of bounds",
 +                            );
 +                            return;
 +                        }
 +                    }
 +
 +                    if let (Some(_), Some(_)) = const_range {
 +                        // early return because both start and end are constants
 +                        // and we have proven above that they are in bounds
 +                        return;
 +                    }
 +                }
 +
 +                let help_msg = match (range.start, range.end) {
 +                    (None, Some(_)) => "consider using `.get(..n)`or `.get_mut(..n)` instead",
 +                    (Some(_), None) => "consider using `.get(n..)` or .get_mut(n..)` instead",
 +                    (Some(_), Some(_)) => "consider using `.get(n..m)` or `.get_mut(n..m)` instead",
 +                    (None, None) => return, // [..] is ok.
 +                };
 +
 +                span_lint_and_help(cx, INDEXING_SLICING, expr.span, "slicing may panic", None, help_msg);
 +            } else {
 +                // Catchall non-range index, i.e., [n] or [n << m]
 +                if let ty::Array(..) = ty.kind() {
 +                    // Index is a const block.
 +                    if let ExprKind::ConstBlock(..) = index.kind {
 +                        return;
 +                    }
 +                    // Index is a constant uint.
 +                    if let Some(..) = constant(cx, cx.typeck_results(), index) {
 +                        // Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
 +                        return;
 +                    }
 +                }
 +
 +                span_lint_and_help(
 +                    cx,
 +                    INDEXING_SLICING,
 +                    expr.span,
 +                    "indexing may panic",
 +                    None,
 +                    "consider using `.get(n)` or `.get_mut(n)` instead",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Returns a tuple of options with the start and end (exclusive) values of
 +/// the range. If the start or end is not constant, None is returned.
++fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) {
 +    let s = range
 +        .start
 +        .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));
 +    let start = match s {
 +        Some(Some(Constant::Int(x))) => Some(x),
 +        Some(_) => None,
 +        None => Some(0),
 +    };
 +
 +    let e = range
 +        .end
 +        .map(|expr| constant(cx, cx.typeck_results(), expr).map(|(c, _)| c));
 +    let end = match e {
 +        Some(Some(Constant::Int(x))) => {
 +            if range.limits == RangeLimits::Closed {
 +                Some(x + 1)
 +            } else {
 +                Some(x)
 +            }
 +        },
 +        Some(_) => None,
 +        None => Some(array_size),
 +    };
 +
 +    (start, end)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..60754b224fc840a8f21ff7b153f028f3b5f48ee0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,184 @@@
++use clippy_utils::{
++    diagnostics::{self, span_lint_and_sugg},
++    meets_msrv, msrvs, source,
++    sugg::Sugg,
++    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>,
++}
++
++impl InstantSubtraction {
++    #[must_use]
++    pub fn new(msrv: Option<RustcVersion>) -> 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 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 f793abdfda34a83d0ac69893ee450a2cfafee12b,0000000000000000000000000000000000000000..1b14e525d9a84462dd3ed9e4daf24c455bcd0f97
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,168 @@@
-             (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
 +//! lint on blocks unnecessarily using >= with a + 1 or - 1
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
 +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
 +use rustc_ast::token;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability -- better to use `> y` instead of `>= y + 1`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// # let y = 1;
 +    /// if x >= y + 1 {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 1;
 +    /// # let y = 1;
 +    /// if x > y {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INT_PLUS_ONE,
 +    complexity,
 +    "instead of using `x >= y + 1`, use `x > y`"
 +}
 +
 +declare_lint_pass!(IntPlusOne => [INT_PLUS_ONE]);
 +
 +// cases:
 +// BinOpKind::Ge
 +// x >= y + 1
 +// x - 1 >= y
 +//
 +// BinOpKind::Le
 +// x + 1 <= y
 +// x <= y - 1
 +
 +#[derive(Copy, Clone)]
 +enum Side {
 +    Lhs,
 +    Rhs,
 +}
 +
 +impl IntPlusOne {
 +    #[expect(clippy::cast_sign_loss)]
 +    fn check_lit(token_lit: token::Lit, target_value: i128) -> bool {
 +        if let Ok(LitKind::Int(value, ..)) = LitKind::from_token_lit(token_lit) {
 +            return value == (target_value as u128);
 +        }
 +        false
 +    }
 +
 +    fn check_binop(cx: &EarlyContext<'_>, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<String> {
 +        match (binop, &lhs.kind, &rhs.kind) {
 +            // case where `x - 1 >= ...` or `-1 + x >= ...`
-                     (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => {
++            (BinOpKind::Ge, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) => {
 +                match (lhskind.node, &lhslhs.kind, &lhsrhs.kind) {
 +                    // `-1 + x`
-                     (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
++                    (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => {
 +                        Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
 +                    },
 +                    // `x - 1`
-             (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs))
-                 if rhskind.node == BinOpKind::Add =>
-             {
++                    (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
 +                        Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            // case where `... >= y + 1` or `... >= 1 + y`
-                     (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => {
++            (BinOpKind::Ge, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) if rhskind.node == BinOpKind::Add => {
 +                match (&rhslhs.kind, &rhsrhs.kind) {
 +                    // `y + 1` and `1 + y`
-                     (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
++                    (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => {
 +                        Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
 +                    },
-             (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _)
-                 if lhskind.node == BinOpKind::Add =>
-             {
++                    (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
 +                        Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            // case where `x + 1 <= ...` or `1 + x <= ...`
-                     (&ExprKind::Lit(lit), _) if Self::check_lit(lit, 1) => {
++            (BinOpKind::Le, ExprKind::Binary(lhskind, lhslhs, lhsrhs), _) if lhskind.node == BinOpKind::Add => {
 +                match (&lhslhs.kind, &lhsrhs.kind) {
 +                    // `1 + x` and `x + 1`
-                     (_, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
++                    (ExprKind::Lit(lit), _) if Self::check_lit(*lit, 1) => {
 +                        Self::generate_recommendation(cx, binop, lhsrhs, rhs, Side::Lhs)
 +                    },
-             (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
++                    (_, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
 +                        Self::generate_recommendation(cx, binop, lhslhs, rhs, Side::Lhs)
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            // case where `... >= y - 1` or `... >= -1 + y`
-                     (BinOpKind::Add, &ExprKind::Lit(lit), _) if Self::check_lit(lit, -1) => {
++            (BinOpKind::Le, _, ExprKind::Binary(rhskind, rhslhs, rhsrhs)) => {
 +                match (rhskind.node, &rhslhs.kind, &rhsrhs.kind) {
 +                    // `-1 + y`
-                     (BinOpKind::Sub, _, &ExprKind::Lit(lit)) if Self::check_lit(lit, 1) => {
++                    (BinOpKind::Add, ExprKind::Lit(lit), _) if Self::check_lit(*lit, -1) => {
 +                        Self::generate_recommendation(cx, binop, rhsrhs, lhs, Side::Rhs)
 +                    },
 +                    // `y - 1`
++                    (BinOpKind::Sub, _, ExprKind::Lit(lit)) if Self::check_lit(*lit, 1) => {
 +                        Self::generate_recommendation(cx, binop, rhslhs, lhs, Side::Rhs)
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    fn generate_recommendation(
 +        cx: &EarlyContext<'_>,
 +        binop: BinOpKind,
 +        node: &Expr,
 +        other_side: &Expr,
 +        side: Side,
 +    ) -> Option<String> {
 +        let binop_string = match binop {
 +            BinOpKind::Ge => ">",
 +            BinOpKind::Le => "<",
 +            _ => return None,
 +        };
 +        if let Some(snippet) = snippet_opt(cx, node.span) {
 +            if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
 +                let rec = match side {
 +                    Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
 +                    Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
 +                };
 +                return rec;
 +            }
 +        }
 +        None
 +    }
 +
 +    fn emit_warning(cx: &EarlyContext<'_>, block: &Expr, recommendation: String) {
 +        span_lint_and_sugg(
 +            cx,
 +            INT_PLUS_ONE,
 +            block.span,
 +            "unnecessary `>= y + 1` or `x - 1 >=`",
 +            "change it to",
 +            recommendation,
 +            Applicability::MachineApplicable, // snippet
 +        );
 +    }
 +}
 +
 +impl EarlyLintPass for IntPlusOne {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, item: &Expr) {
 +        if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.kind {
 +            if let Some(rec) = Self::check_binop(cx, kind.node, lhs, rhs) {
 +                Self::emit_warning(cx, item, rec);
 +            }
 +        }
 +    }
 +}
index 0ef77e03de9062bb5cc98ea27ec07c3ce94fd050,0000000000000000000000000000000000000000..6ea637412d5b1b47eb655871a0ab252d135cd0e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,159 @@@
- fn numeric_cast_precast_bounds<'a>(cx: &LateContext<'_>, expr: &'a Expr<'_>) -> Option<(FullInt, FullInt)> {
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, IntTy, UintTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +use clippy_utils::comparisons;
 +use clippy_utils::comparisons::Rel;
 +use clippy_utils::consts::{constant_full_int, FullInt};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::source::snippet;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons where the relation is always either
 +    /// true or false, but where one side has been upcast so that the comparison is
 +    /// necessary. Only integer types are checked.
 +    ///
 +    /// ### Why is this bad?
 +    /// An expression like `let x : u8 = ...; (x as u32) > 300`
 +    /// will mistakenly imply that it is possible for `x` to be outside the range of
 +    /// `u8`.
 +    ///
 +    /// ### Known problems
 +    /// https://github.com/rust-lang/rust-clippy/issues/886
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: u8 = 1;
 +    /// (x as u32) > 300;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INVALID_UPCAST_COMPARISONS,
 +    pedantic,
 +    "a comparison involving an upcast which is always true or false"
 +}
 +
 +declare_lint_pass!(InvalidUpcastComparisons => [INVALID_UPCAST_COMPARISONS]);
 +
++fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(FullInt, FullInt)> {
 +    if let ExprKind::Cast(cast_exp, _) = expr.kind {
 +        let pre_cast_ty = cx.typeck_results().expr_ty(cast_exp);
 +        let cast_ty = cx.typeck_results().expr_ty(expr);
 +        // if it's a cast from i32 to u32 wrapping will invalidate all these checks
 +        if cx.layout_of(pre_cast_ty).ok().map(|l| l.size) == cx.layout_of(cast_ty).ok().map(|l| l.size) {
 +            return None;
 +        }
 +        match pre_cast_ty.kind() {
 +            ty::Int(int_ty) => Some(match int_ty {
 +                IntTy::I8 => (FullInt::S(i128::from(i8::MIN)), FullInt::S(i128::from(i8::MAX))),
 +                IntTy::I16 => (FullInt::S(i128::from(i16::MIN)), FullInt::S(i128::from(i16::MAX))),
 +                IntTy::I32 => (FullInt::S(i128::from(i32::MIN)), FullInt::S(i128::from(i32::MAX))),
 +                IntTy::I64 => (FullInt::S(i128::from(i64::MIN)), FullInt::S(i128::from(i64::MAX))),
 +                IntTy::I128 => (FullInt::S(i128::MIN), FullInt::S(i128::MAX)),
 +                IntTy::Isize => (FullInt::S(isize::MIN as i128), FullInt::S(isize::MAX as i128)),
 +            }),
 +            ty::Uint(uint_ty) => Some(match uint_ty {
 +                UintTy::U8 => (FullInt::U(u128::from(u8::MIN)), FullInt::U(u128::from(u8::MAX))),
 +                UintTy::U16 => (FullInt::U(u128::from(u16::MIN)), FullInt::U(u128::from(u16::MAX))),
 +                UintTy::U32 => (FullInt::U(u128::from(u32::MIN)), FullInt::U(u128::from(u32::MAX))),
 +                UintTy::U64 => (FullInt::U(u128::from(u64::MIN)), FullInt::U(u128::from(u64::MAX))),
 +                UintTy::U128 => (FullInt::U(u128::MIN), FullInt::U(u128::MAX)),
 +                UintTy::Usize => (FullInt::U(usize::MIN as u128), FullInt::U(usize::MAX as u128)),
 +            }),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
 +    if let ExprKind::Cast(cast_val, _) = expr.kind {
 +        span_lint(
 +            cx,
 +            INVALID_UPCAST_COMPARISONS,
 +            span,
 +            &format!(
 +                "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
 +                snippet(cx, cast_val.span, "the expression"),
 +                if always { "true" } else { "false" },
 +            ),
 +        );
 +    }
 +}
 +
 +fn upcast_comparison_bounds_err<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    rel: comparisons::Rel,
 +    lhs_bounds: Option<(FullInt, FullInt)>,
 +    lhs: &'tcx Expr<'_>,
 +    rhs: &'tcx Expr<'_>,
 +    invert: bool,
 +) {
 +    if let Some((lb, ub)) = lhs_bounds {
 +        if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
 +            if rel == Rel::Eq || rel == Rel::Ne {
 +                if norm_rhs_val < lb || norm_rhs_val > ub {
 +                    err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
 +                }
 +            } else if match rel {
 +                Rel::Lt => {
 +                    if invert {
 +                        norm_rhs_val < lb
 +                    } else {
 +                        ub < norm_rhs_val
 +                    }
 +                },
 +                Rel::Le => {
 +                    if invert {
 +                        norm_rhs_val <= lb
 +                    } else {
 +                        ub <= norm_rhs_val
 +                    }
 +                },
 +                Rel::Eq | Rel::Ne => unreachable!(),
 +            } {
 +                err_upcast_comparison(cx, span, lhs, true);
 +            } else if match rel {
 +                Rel::Lt => {
 +                    if invert {
 +                        norm_rhs_val >= ub
 +                    } else {
 +                        lb >= norm_rhs_val
 +                    }
 +                },
 +                Rel::Le => {
 +                    if invert {
 +                        norm_rhs_val > ub
 +                    } else {
 +                        lb > norm_rhs_val
 +                    }
 +                },
 +                Rel::Eq | Rel::Ne => unreachable!(),
 +            } {
 +                err_upcast_comparison(cx, span, lhs, false);
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidUpcastComparisons {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
 +            let normalized = comparisons::normalize_comparison(cmp.node, lhs, rhs);
 +            let Some((rel, normalized_lhs, normalized_rhs)) = normalized else {
 +                return;
 +            };
 +
 +            let lhs_bounds = numeric_cast_precast_bounds(cx, normalized_lhs);
 +            let rhs_bounds = numeric_cast_precast_bounds(cx, normalized_rhs);
 +
 +            upcast_comparison_bounds_err(cx, expr.span, rel, lhs_bounds, normalized_lhs, normalized_rhs, false);
 +            upcast_comparison_bounds_err(cx, expr.span, rel, rhs_bounds, normalized_rhs, normalized_lhs, true);
 +        }
 +    }
 +}
index 06e957285499cc49f382f3cf5cee0d1e54b2e89e,0000000000000000000000000000000000000000..b18456ee523401759104bbb383178e7d87b5cb5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,219 -1,0 +1,179 @@@
- use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
 +//! lint when there is a large size difference between variants on an enum
 +
 +use clippy_utils::source::snippet_with_applicability;
- use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
++use clippy_utils::{
++    diagnostics::span_lint_and_then,
++    ty::{approx_ty_size, is_copy, AdtVariantInfo},
++};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
- struct FieldInfo {
-     ind: usize,
-     size: u64,
- }
- struct VariantInfo {
-     ind: usize,
-     size: u64,
-     fields_size: Vec<FieldInfo>,
- }
- fn variants_size<'tcx>(
-     cx: &LateContext<'tcx>,
-     adt: AdtDef<'tcx>,
-     subst: &'tcx List<GenericArg<'tcx>>,
- ) -> Vec<VariantInfo> {
-     let mut variants_size = adt
-         .variants()
-         .iter()
-         .enumerate()
-         .map(|(i, variant)| {
-             let mut fields_size = variant
-                 .fields
-                 .iter()
-                 .enumerate()
-                 .map(|(i, f)| FieldInfo {
-                     ind: i,
-                     size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
-                 })
-                 .collect::<Vec<_>>();
-             fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
-             VariantInfo {
-                 ind: i,
-                 size: fields_size.iter().map(|info| info.size).sum(),
-                 fields_size,
-             }
-         })
-         .collect::<Vec<_>>();
-     variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
-     variants_size
- }
++use rustc_middle::ty::{Adt, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for large size differences between variants on
 +    /// `enum`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Enum size is bounded by the largest variant. Having one
 +    /// large variant can penalize the memory layout of that enum.
 +    ///
 +    /// ### Known problems
 +    /// This lint obviously cannot take the distribution of
 +    /// variants in your running program into account. It is possible that the
 +    /// smaller variants make up less than 1% of all instances, in which case
 +    /// the overhead is negligible and the boxing is counter-productive. Always
 +    /// measure the change this lint suggests.
 +    ///
 +    /// For types that implement `Copy`, the suggestion to `Box` a variant's
 +    /// data would require removing the trait impl. The types can of course
 +    /// still be `Clone`, but that is worse ergonomically. Depending on the
 +    /// use case it may be possible to store the large data in an auxiliary
 +    /// structure (e.g. Arena or ECS).
 +    ///
 +    /// The lint will ignore the impact of generic types to the type layout by
 +    /// assuming every type parameter is zero-sized. Depending on your use case,
 +    /// this may lead to a false positive.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Test {
 +    ///     A(i32),
 +    ///     B([i32; 8000]),
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Possibly better
 +    /// enum Test2 {
 +    ///     A(i32),
 +    ///     B(Box<[i32; 8000]>),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LARGE_ENUM_VARIANT,
 +    perf,
 +    "large size difference between variants on an enum"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct LargeEnumVariant {
 +    maximum_size_difference_allowed: u64,
 +}
 +
 +impl LargeEnumVariant {
 +    #[must_use]
 +    pub fn new(maximum_size_difference_allowed: u64) -> Self {
 +        Self {
 +            maximum_size_difference_allowed,
 +        }
 +    }
 +}
 +
-             let variants_size = variants_size(cx, *adt, subst);
 +impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let ItemKind::Enum(ref def, _) = item.kind {
 +            let ty = cx.tcx.type_of(item.owner_id);
 +            let Adt(adt, subst) = ty.kind() else {
 +                panic!("already checked whether this is an enum")
 +            };
 +            if adt.variants().len() <= 1 {
 +                return;
 +            }
-                                 .map_while(|val| {
++            let variants_size = AdtVariantInfo::new(cx, *adt, subst);
 +
 +            let mut difference = variants_size[0].size - variants_size[1].size;
 +            if difference > self.maximum_size_difference_allowed {
 +                let help_text = "consider boxing the large fields to reduce the total size of the enum";
 +                span_lint_and_then(
 +                    cx,
 +                    LARGE_ENUM_VARIANT,
 +                    item.span,
 +                    "large size difference between variants",
 +                    |diag| {
 +                        diag.span_label(
 +                            item.span,
 +                            format!("the entire enum is at least {} bytes", approx_ty_size(cx, ty)),
 +                        );
 +                        diag.span_label(
 +                            def.variants[variants_size[0].ind].span,
 +                            format!("the largest variant contains at least {} bytes", variants_size[0].size),
 +                        );
 +                        diag.span_label(
 +                            def.variants[variants_size[1].ind].span,
 +                            &if variants_size[1].fields_size.is_empty() {
 +                                "the second-largest variant carries no data at all".to_owned()
 +                            } else {
 +                                format!(
 +                                    "the second-largest variant contains at least {} bytes",
 +                                    variants_size[1].size
 +                                )
 +                            },
 +                        );
 +
 +                        let fields = def.variants[variants_size[0].ind].data.fields();
 +                        let mut applicability = Applicability::MaybeIncorrect;
 +                        if is_copy(cx, ty) || maybe_copy(cx, ty) {
 +                            diag.span_note(
 +                                item.ident.span,
 +                                "boxing a variant would require the type no longer be `Copy`",
 +                            );
 +                        } else {
 +                            let sugg: Vec<(Span, String)> = variants_size[0]
 +                                .fields_size
 +                                .iter()
 +                                .rev()
-                                         difference = difference.saturating_sub(val.size);
++                                .map_while(|&(ind, size)| {
 +                                    if difference > self.maximum_size_difference_allowed {
-                                             fields[val.ind].ty.span,
++                                        difference = difference.saturating_sub(size);
 +                                        Some((
-                                                     fields[val.ind].ty.span,
++                                            fields[ind].ty.span,
 +                                            format!(
 +                                                "Box<{}>",
 +                                                snippet_with_applicability(
 +                                                    cx,
++                                                    fields[ind].ty.span,
 +                                                    "..",
 +                                                    &mut applicability
 +                                                )
 +                                                .into_owned()
 +                                            ),
 +                                        ))
 +                                    } else {
 +                                        None
 +                                    }
 +                                })
 +                                .collect();
 +
 +                            if !sugg.is_empty() {
 +                                diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
 +                                return;
 +                            }
 +                        }
 +                        diag.span_help(def.variants[variants_size[0].ind].span, help_text);
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn maybe_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if let Adt(_def, substs) = ty.kind()
 +        && substs.types().next().is_some()
 +        && let Some(copy_trait) = cx.tcx.lang_items().copy_trait()
 +    {
 +        return cx.tcx.non_blanket_impls_for_ty(copy_trait, ty).next().is_some();
 +    }
 +    false
 +}
index b0cba40c27a5d5ccdaca7a0166cbef37fb3eb6b4,0000000000000000000000000000000000000000..4c133c06a157ac9e0fcc6ce422bc1c2191404536
mode 100644,000000..100644
--- /dev/null
@@@ -1,502 -1,0 +1,501 @@@
-     if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
-     {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefIdSet;
 +use rustc_hir::{
 +    def_id::DefId, AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, ImplItem, ImplItemKind, ImplicitSelfKind, Item,
 +    ItemKind, Mutability, Node, TraitItemRef, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{
 +    source_map::{Span, Spanned, Symbol},
 +    symbol::sym,
 +};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for getting the length of something via `.len()`
 +    /// just to compare to zero, and suggests using `.is_empty()` where applicable.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some structures can answer `.is_empty()` much faster
 +    /// than calculating their length. So it is good to get into the habit of using
 +    /// `.is_empty()`, and having it is cheap.
 +    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if x.len() == 0 {
 +    ///     ..
 +    /// }
 +    /// if y.len() != 0 {
 +    ///     ..
 +    /// }
 +    /// ```
 +    /// instead use
 +    /// ```ignore
 +    /// if x.is_empty() {
 +    ///     ..
 +    /// }
 +    /// if !y.is_empty() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LEN_ZERO,
 +    style,
 +    "checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items that implement `.len()` but not
 +    /// `.is_empty()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is good custom to have both methods, because for
 +    /// some data structures, asking about the length will be a costly operation,
 +    /// whereas `.is_empty()` can usually answer in constant time. Also it used to
 +    /// lead to false positives on the [`len_zero`](#len_zero) lint – currently that
 +    /// lint will ignore such entities.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// impl X {
 +    ///     pub fn len(&self) -> usize {
 +    ///         ..
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LEN_WITHOUT_IS_EMPTY,
 +    style,
 +    "traits or impls with a public `len` method but no corresponding `is_empty` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparing to an empty slice such as `""` or `[]`,
 +    /// and suggests using `.is_empty()` where applicable.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some structures can answer `.is_empty()` much faster
 +    /// than checking for equality. So it is good to get into the habit of using
 +    /// `.is_empty()`, and having it is cheap.
 +    /// Besides, it makes the intent clearer than a manual comparison in some contexts.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```ignore
 +    /// if s == "" {
 +    ///     ..
 +    /// }
 +    ///
 +    /// if arr == [] {
 +    ///     ..
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// if s.is_empty() {
 +    ///     ..
 +    /// }
 +    ///
 +    /// if arr.is_empty() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub COMPARISON_TO_EMPTY,
 +    style,
 +    "checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
 +}
 +
 +declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMPTY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LenZero {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if item.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind {
 +            check_trait_items(cx, item, trait_items);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if_chain! {
 +            if item.ident.name == sym::len;
 +            if let ImplItemKind::Fn(sig, _) = &item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if cx.effective_visibilities.is_exported(item.owner_id.def_id);
 +            if matches!(sig.decl.output, FnRetTy::Return(_));
 +            if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id());
 +            if imp.of_trait.is_none();
 +            if let TyKind::Path(ty_path) = &imp.self_ty.kind;
 +            if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id();
 +            if let Some(local_id) = ty_id.as_local();
 +            let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id);
 +            if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id);
 +            if let Some(output) = parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).skip_binder());
 +            then {
 +                let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
 +                    Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
 +                    Some(Node::Item(x)) => match x.kind {
 +                        ItemKind::Struct(..) => (x.ident.name, "struct"),
 +                        ItemKind::Enum(..) => (x.ident.name, "enum"),
 +                        ItemKind::Union(..) => (x.ident.name, "union"),
 +                        _ => (x.ident.name, "type"),
 +                    }
 +                    _ => return,
 +                };
 +                check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind)
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
 +            match cmp {
 +                BinOpKind::Eq => {
 +                    check_cmp(cx, expr.span, left, right, "", 0); // len == 0
 +                    check_cmp(cx, expr.span, right, left, "", 0); // 0 == len
 +                },
 +                BinOpKind::Ne => {
 +                    check_cmp(cx, expr.span, left, right, "!", 0); // len != 0
 +                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 != len
 +                },
 +                BinOpKind::Gt => {
 +                    check_cmp(cx, expr.span, left, right, "!", 0); // len > 0
 +                    check_cmp(cx, expr.span, right, left, "", 1); // 1 > len
 +                },
 +                BinOpKind::Lt => {
 +                    check_cmp(cx, expr.span, left, right, "", 1); // len < 1
 +                    check_cmp(cx, expr.span, right, left, "!", 0); // 0 < len
 +                },
 +                BinOpKind::Ge => check_cmp(cx, expr.span, left, right, "!", 1), // len >= 1
 +                BinOpKind::Le => check_cmp(cx, expr.span, right, left, "!", 1), // 1 <= len
 +                _ => (),
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, trait_items: &[TraitItemRef]) {
 +    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
 +        item.ident.name == name
 +            && if let AssocItemKind::Fn { has_self } = item.kind {
 +                has_self && { cx.tcx.fn_sig(item.id.owner_id).inputs().skip_binder().len() == 1 }
 +            } else {
 +                false
 +            }
 +    }
 +
 +    // fill the set with current and super traits
 +    fn fill_trait_set(traitt: DefId, set: &mut DefIdSet, cx: &LateContext<'_>) {
 +        if set.insert(traitt) {
 +            for supertrait in rustc_trait_selection::traits::supertrait_def_ids(cx.tcx, traitt) {
 +                fill_trait_set(supertrait, set, cx);
 +            }
 +        }
 +    }
 +
 +    if cx.effective_visibilities.is_exported(visited_trait.owner_id.def_id)
 +        && trait_items.iter().any(|i| is_named_self(cx, i, sym::len))
 +    {
 +        let mut current_and_super_traits = DefIdSet::default();
 +        fill_trait_set(visited_trait.owner_id.to_def_id(), &mut current_and_super_traits, cx);
 +        let is_empty = sym!(is_empty);
 +
 +        let is_empty_method_found = current_and_super_traits
 +            .iter()
 +            .flat_map(|&i| cx.tcx.associated_items(i).filter_by_name_unhygienic(is_empty))
 +            .any(|i| {
 +                i.kind == ty::AssocKind::Fn
 +                    && i.fn_has_self_parameter
 +                    && cx.tcx.fn_sig(i.def_id).inputs().skip_binder().len() == 1
 +            });
 +
 +        if !is_empty_method_found {
 +            span_lint(
 +                cx,
 +                LEN_WITHOUT_IS_EMPTY,
 +                visited_trait.span,
 +                &format!(
 +                    "trait `{}` has a `len` method but no (possibly inherited) `is_empty` method",
 +                    visited_trait.ident.name
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum LenOutput<'tcx> {
 +    Integral,
 +    Option(DefId),
 +    Result(DefId, Ty<'tcx>),
 +}
 +fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> {
 +    match *sig.output().kind() {
 +        ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
 +        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
 +            subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
 +        },
 +        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => subs
 +            .type_at(0)
 +            .is_integral()
 +            .then(|| LenOutput::Result(adt.did(), subs.type_at(1))),
 +        _ => None,
 +    }
 +}
 +
 +impl<'tcx> LenOutput<'tcx> {
 +    fn matches_is_empty_output(self, ty: Ty<'tcx>) -> bool {
 +        match (self, ty.kind()) {
 +            (_, &ty::Bool) => true,
 +            (Self::Option(id), &ty::Adt(adt, subs)) if id == adt.did() => subs.type_at(0).is_bool(),
 +            (Self::Result(id, err_ty), &ty::Adt(adt, subs)) if id == adt.did() => {
 +                subs.type_at(0).is_bool() && subs.type_at(1) == err_ty
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn expected_sig(self, self_kind: ImplicitSelfKind) -> String {
 +        let self_ref = match self_kind {
 +            ImplicitSelfKind::ImmRef => "&",
 +            ImplicitSelfKind::MutRef => "&mut ",
 +            _ => "",
 +        };
 +        match self {
 +            Self::Integral => format!("expected signature: `({self_ref}self) -> bool`"),
 +            Self::Option(_) => {
 +                format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Option<bool>")
 +            },
 +            Self::Result(..) => {
 +                format!("expected signature: `({self_ref}self) -> bool` or `({self_ref}self) -> Result<bool>")
 +            },
 +        }
 +    }
 +}
 +
 +/// Checks if the given signature matches the expectations for `is_empty`
 +fn check_is_empty_sig<'tcx>(sig: FnSig<'tcx>, self_kind: ImplicitSelfKind, len_output: LenOutput<'tcx>) -> bool {
 +    match &**sig.inputs_and_output {
 +        [arg, res] if len_output.matches_is_empty_output(*res) => {
 +            matches!(
 +                (arg.kind(), self_kind),
 +                (ty::Ref(_, _, Mutability::Not), ImplicitSelfKind::ImmRef)
 +                    | (ty::Ref(_, _, Mutability::Mut), ImplicitSelfKind::MutRef)
 +            ) || (!arg.is_ref() && matches!(self_kind, ImplicitSelfKind::Imm | ImplicitSelfKind::Mut))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the given type has an `is_empty` method with the appropriate signature.
 +fn check_for_is_empty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    self_kind: ImplicitSelfKind,
 +    output: LenOutput<'tcx>,
 +    impl_ty: DefId,
 +    item_name: Symbol,
 +    item_kind: &str,
 +) {
 +    let is_empty = Symbol::intern("is_empty");
 +    let is_empty = cx
 +        .tcx
 +        .inherent_impls(impl_ty)
 +        .iter()
 +        .flat_map(|&id| cx.tcx.associated_items(id).filter_by_name_unhygienic(is_empty))
 +        .find(|item| item.kind == AssocKind::Fn);
 +
 +    let (msg, is_empty_span, self_kind) = match is_empty {
 +        None => (
 +            format!(
 +                "{item_kind} `{}` has a public `len` method, but no `is_empty` method",
 +                item_name.as_str(),
 +            ),
 +            None,
 +            None,
 +        ),
 +        Some(is_empty) if !cx.effective_visibilities.is_exported(is_empty.def_id.expect_local()) => (
 +            format!(
 +                "{item_kind} `{}` has a public `len` method, but a private `is_empty` method",
 +                item_name.as_str(),
 +            ),
 +            Some(cx.tcx.def_span(is_empty.def_id)),
 +            None,
 +        ),
 +        Some(is_empty)
 +            if !(is_empty.fn_has_self_parameter
 +                && check_is_empty_sig(cx.tcx.fn_sig(is_empty.def_id).skip_binder(), self_kind, output)) =>
 +        {
 +            (
 +                format!(
 +                    "{item_kind} `{}` has a public `len` method, but the `is_empty` method has an unexpected signature",
 +                    item_name.as_str(),
 +                ),
 +                Some(cx.tcx.def_span(is_empty.def_id)),
 +                Some(self_kind),
 +            )
 +        },
 +        Some(_) => return,
 +    };
 +
 +    span_lint_and_then(cx, LEN_WITHOUT_IS_EMPTY, span, &msg, |db| {
 +        if let Some(span) = is_empty_span {
 +            db.span_note(span, "`is_empty` defined here");
 +        }
 +        if let Some(self_kind) = self_kind {
 +            db.note(&output.expected_sig(self_kind));
 +        }
 +    });
 +}
 +
 +fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
++    if let (&ExprKind::MethodCall(method_path, receiver, args, _), ExprKind::Lit(lit)) = (&method.kind, &lit.kind) {
 +        // check if we are in an is_empty() method
 +        if let Some(name) = get_item_name(cx, method) {
 +            if name.as_str() == "is_empty" {
 +                return;
 +            }
 +        }
 +
 +        check_len(
 +            cx,
 +            span,
 +            method_path.ident.name,
 +            receiver,
 +            args,
 +            &lit.node,
 +            op,
 +            compare_to,
 +        );
 +    } else {
 +        check_empty_expr(cx, span, method, lit, op);
 +    }
 +}
 +
 +// FIXME(flip1995): Figure out how to reduce the number of arguments
 +#[allow(clippy::too_many_arguments)]
 +fn check_len(
 +    cx: &LateContext<'_>,
 +    span: Span,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    args: &[Expr<'_>],
 +    lit: &LitKind,
 +    op: &str,
 +    compare_to: u32,
 +) {
 +    if let LitKind::Int(lit, _) = *lit {
 +        // check if length is compared to the specified number
 +        if lit != u128::from(compare_to) {
 +            return;
 +        }
 +
 +        if method_name == sym::len && args.is_empty() && has_is_empty(cx, receiver) {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                LEN_ZERO,
 +                span,
 +                &format!("length comparison to {}", if compare_to == 0 { "zero" } else { "one" }),
 +                &format!("using `{op}is_empty` is clearer and more explicit"),
 +                format!(
 +                    "{op}{}.is_empty()",
 +                    snippet_with_applicability(cx, receiver.span, "_", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_empty_expr(cx: &LateContext<'_>, span: Span, lit1: &Expr<'_>, lit2: &Expr<'_>, op: &str) {
 +    if (is_empty_array(lit2) || is_empty_string(lit2)) && has_is_empty(cx, lit1) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            COMPARISON_TO_EMPTY,
 +            span,
 +            "comparison to empty slice",
 +            &format!("using `{op}is_empty` is clearer and more explicit"),
 +            format!(
 +                "{op}{}.is_empty()",
 +                snippet_with_applicability(cx, lit1.span, "_", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn is_empty_string(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Lit(ref lit) = expr.kind {
 +        if let LitKind::Str(lit, _) = lit.node {
 +            let lit = lit.as_str();
 +            return lit.is_empty();
 +        }
 +    }
 +    false
 +}
 +
 +fn is_empty_array(expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Array(arr) = expr.kind {
 +        return arr.is_empty();
 +    }
 +    false
 +}
 +
 +/// Checks if this type has an `is_empty` method.
 +fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Gets an `AssocItem` and return true if it matches `is_empty(self)`.
 +    fn is_is_empty(cx: &LateContext<'_>, item: &ty::AssocItem) -> bool {
 +        if item.kind == ty::AssocKind::Fn {
 +            let sig = cx.tcx.fn_sig(item.def_id);
 +            let ty = sig.skip_binder();
 +            ty.inputs().len() == 1
 +        } else {
 +            false
 +        }
 +    }
 +
 +    /// Checks the inherent impl's items for an `is_empty(self)` method.
 +    fn has_is_empty_impl(cx: &LateContext<'_>, id: DefId) -> bool {
 +        let is_empty = sym!(is_empty);
 +        cx.tcx.inherent_impls(id).iter().any(|imp| {
 +            cx.tcx
 +                .associated_items(*imp)
 +                .filter_by_name_unhygienic(is_empty)
 +                .any(|item| is_is_empty(cx, item))
 +        })
 +    }
 +
 +    let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
 +    match ty.kind() {
 +        ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
 +            let is_empty = sym!(is_empty);
 +            cx.tcx
 +                .associated_items(principal.def_id())
 +                .filter_by_name_unhygienic(is_empty)
 +                .any(|item| is_is_empty(cx, item))
 +        }),
 +        ty::Projection(ref proj) => has_is_empty_impl(cx, proj.item_def_id),
 +        ty::Adt(id, _) => has_is_empty_impl(cx, id.did()),
 +        ty::Array(..) | ty::Slice(..) | ty::Str => true,
 +        _ => false,
 +    }
 +}
index b7798b1c1d749ae488a6ed08fee2824f7ca9999f,0000000000000000000000000000000000000000..61f87b91400d76405f8fcb914066040f49a9b4ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,174 -1,0 +1,153 @@@
- use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, match_type};
 +use clippy_utils::diagnostics::span_lint_and_help;
- use if_chain::if_chain;
++use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
 +use clippy_utils::{is_must_use_func_call, paths};
- use rustc_span::{sym, Symbol};
 +use rustc_hir::{Local, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
-     "non-binding let on a `#[must_use]` expression"
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let _ = <expr>` where expr is `#[must_use]`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's better to explicitly handle the value of a `#[must_use]`
 +    /// expr
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn f() -> Result<u32, u32> {
 +    ///     Ok(0)
 +    /// }
 +    ///
 +    /// let _ = f();
 +    /// // is_ok() is marked #[must_use]
 +    /// let _ = f().is_ok();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub LET_UNDERSCORE_MUST_USE,
 +    restriction,
-     /// Checks for `let _ = sync_lock`.
-     /// This supports `mutex` and `rwlock` in `std::sync` and `parking_lot`.
++    "non-binding `let` on a `#[must_use]` expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     "non-binding let on a synchronization lock"
++    /// Checks for `let _ = sync_lock`. This supports `mutex` and `rwlock` in
++    /// `parking_lot`. For `std` locks see the `rustc` lint
++    /// [`let_underscore_lock`](https://doc.rust-lang.org/nightly/rustc/lints/listing/deny-by-default.html#let-underscore-lock)
 +    ///
 +    /// ### Why is this bad?
 +    /// This statement immediately drops the lock instead of
 +    /// extending its lifetime to the end of the scope, which is often not intended.
 +    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
 +    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
 +    /// `std::mem::drop` conveys your intention better and is less error-prone.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _ = mutex.lock();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _lock = mutex.lock();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub LET_UNDERSCORE_LOCK,
 +    correctness,
-     /// Checks for `let _ = <expr>`
-     /// where expr has a type that implements `Drop`
++    "non-binding `let` on a synchronization lock"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// This statement immediately drops the initializer
-     /// expression instead of extending its lifetime to the end of the scope, which
-     /// is often not intended. To extend the expression's lifetime to the end of the
-     /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
-     /// explicitly drop the expression, `std::mem::drop` conveys your intention
-     /// better and is less error-prone.
++    /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
 +    ///
 +    /// ### Why is this bad?
-     /// # struct DroppableItem;
-     /// {
-     ///     let _ = DroppableItem;
-     ///     //                   ^ dropped here
-     ///     /* more code */
++    /// Futures must be polled for work to be done. The original intention was most likely to await the future
++    /// and ignore the resulting value.
 +    ///
 +    /// ### Example
 +    /// ```rust
-     /// # struct DroppableItem;
-     /// {
-     ///     let _droppable = DroppableItem;
-     ///     /* more code */
-     ///     // dropped at end of scope
++    /// async fn foo() -> Result<(), ()> {
++    ///     Ok(())
 +    /// }
++    /// let _ = foo();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
-     #[clippy::version = "1.50.0"]
-     pub LET_UNDERSCORE_DROP,
-     pedantic,
-     "non-binding let on a type that implements `Drop`"
++    /// # async fn context() {
++    /// async fn foo() -> Result<(), ()> {
++    ///     Ok(())
 +    /// }
++    /// let _ = foo().await;
++    /// # }
 +    /// ```
- declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
- const SYNC_GUARD_SYMS: [Symbol; 3] = [sym::MutexGuard, sym::RwLockReadGuard, sym::RwLockWriteGuard];
++    #[clippy::version = "1.66"]
++    pub LET_UNDERSCORE_FUTURE,
++    suspicious,
++    "non-binding `let` on a future"
 +}
 +
-         if in_external_macro(cx.tcx.sess, local.span) {
-             return;
-         }
-         if_chain! {
-             if let PatKind::Wild = local.pat.kind;
-             if let Some(init) = local.init;
-             then {
-                 let init_ty = cx.typeck_results().expr_ty(init);
-                 let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
-                     GenericArgKind::Type(inner_ty) => {
-                         SYNC_GUARD_SYMS
-                             .iter()
-                             .any(|&sym| is_type_diagnostic_item(cx, inner_ty, sym))
-                             || SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
-                     },
-                     GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-                 });
-                 if contains_sync_guard {
-                     span_lint_and_help(
-                         cx,
-                         LET_UNDERSCORE_LOCK,
-                         local.span,
-                         "non-binding let on a synchronization lock",
-                         None,
-                         "consider using an underscore-prefixed named \
-                             binding or dropping explicitly with `std::mem::drop`",
-                     );
-                 } else if init_ty.needs_drop(cx.tcx, cx.param_env) {
-                     span_lint_and_help(
-                         cx,
-                         LET_UNDERSCORE_DROP,
-                         local.span,
-                         "non-binding `let` on a type that implements `Drop`",
-                         None,
-                         "consider using an underscore-prefixed named \
++declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE]);
 +
 +const SYNC_GUARD_PATHS: [&[&str]; 3] = [
 +    &paths::PARKING_LOT_MUTEX_GUARD,
 +    &paths::PARKING_LOT_RWLOCK_READ_GUARD,
 +    &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
 +];
 +
 +impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
-                     );
-                 } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
-                     span_lint_and_help(
-                         cx,
-                         LET_UNDERSCORE_MUST_USE,
-                         local.span,
-                         "non-binding let on an expression with `#[must_use]` type",
-                         None,
-                         "consider explicitly using expression value",
-                     );
-                 } else if is_must_use_func_call(cx, init) {
-                     span_lint_and_help(
-                         cx,
-                         LET_UNDERSCORE_MUST_USE,
-                         local.span,
-                         "non-binding let on a result of a `#[must_use]` function",
-                         None,
-                         "consider explicitly using function result",
-                     );
-                 }
++        if !in_external_macro(cx.tcx.sess, local.span)
++            && let PatKind::Wild = local.pat.kind
++            && let Some(init) = local.init
++        {
++            let init_ty = cx.typeck_results().expr_ty(init);
++            let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
++                GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
++                GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
++            });
++            if contains_sync_guard {
++                span_lint_and_help(
++                    cx,
++                    LET_UNDERSCORE_LOCK,
++                    local.span,
++                    "non-binding `let` on a synchronization lock",
++                    None,
++                    "consider using an underscore-prefixed named \
 +                            binding or dropping explicitly with `std::mem::drop`",
++                );
++            } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
++                && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) {
++                span_lint_and_help(
++                    cx,
++                    LET_UNDERSCORE_FUTURE,
++                    local.span,
++                    "non-binding `let` on a future",
++                    None,
++                    "consider awaiting the future or dropping explicitly with `std::mem::drop`"
++                );
++            } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
++                span_lint_and_help(
++                    cx,
++                    LET_UNDERSCORE_MUST_USE,
++                    local.span,
++                    "non-binding `let` on an expression with `#[must_use]` type",
++                    None,
++                    "consider explicitly using expression value",
++                );
++            } else if is_must_use_func_call(cx, init) {
++                span_lint_and_help(
++                    cx,
++                    LET_UNDERSCORE_MUST_USE,
++                    local.span,
++                    "non-binding `let` on a result of a `#[must_use]` function",
++                    None,
++                    "consider explicitly using function result",
++                );
 +            }
 +        }
 +    }
 +}
index 1307096b28d7bae1c627d777ff69d55e4907e825,0000000000000000000000000000000000000000..b481314abedc8b8f91178583863b2e5253a32ed6
mode 100644,000000..100644
--- /dev/null
@@@ -1,983 -1,0 +1,986 @@@
- extern crate rustc_hir_typeck;
 +#![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_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_analysis;
- use rustc_lint::LintId;
 +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 clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
- /// Macro used to declare a Clippy lint.
- ///
- /// Every lint declaration consists of 4 parts:
- ///
- /// 1. The documentation, which is used for the website
- /// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
- /// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
- ///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
- /// 4. The `description` that contains a short explanation on what's wrong with code where the
- ///    lint is triggered.
- ///
- /// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
- /// enabled by default. As said in the README.md of this repository, if the lint level mapping
- /// changes, please update README.md.
- ///
- /// # Example
- ///
- /// ```
- /// #![feature(rustc_private)]
- /// extern crate rustc_session;
- /// use rustc_session::declare_tool_lint;
- /// use clippy_lints::declare_clippy_lint;
- ///
- /// 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
- ///     /// Insert a short example of code that triggers the lint
- ///     /// ```
- ///     ///
- ///     /// Use instead:
- ///     /// ```rust
- ///     /// Insert a short example of improved code that doesn't trigger the lint
- ///     /// ```
- ///     pub LINT_NAME,
- ///     pedantic,
- ///     "description"
- /// }
- /// ```
- /// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
- #[macro_export]
- macro_rules! declare_clippy_lint {
-     { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
-         }
-     };
-     { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
-         declare_tool_lint! {
-             $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
-         }
-     };
- }
++use rustc_lint::{Lint, LintId};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +
- mod manual_instant_elapsed;
 +#[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;
- pub use crate::utils::conf::Conf;
++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`
 +
- pub fn read_conf(sess: &Session) -> Conf {
-     let file_name = match utils::conf::lookup_conf_file() {
 +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 = 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
 +    }
 +}
 +
 +#[doc(hidden)]
-     let TryConf { conf, errors, warnings } = utils::conf::read(&file_name);
++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();
 +        },
 +    };
 +
-     include!("lib.register_lints.rs");
-     include!("lib.register_restriction.rs");
-     include!("lib.register_pedantic.rs");
-     #[cfg(feature = "internal")]
-     include!("lib.register_internal.rs");
-     include!("lib.register_all.rs");
-     include!("lib.register_style.rs");
-     include!("lib.register_complexity.rs");
-     include!("lib.register_correctness.rs");
-     include!("lib.register_suspicious.rs");
-     include!("lib.register_perf.rs");
-     include!("lib.register_cargo.rs");
-     include!("lib.register_nursery.rs");
++    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");
 +
-     store.register_late_pass(|_| Box::new(mut_key::MutableKeyType));
 +    #[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));
 +
 +    let msrv = read_msrv(conf, sess);
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    let allow_expect_in_tests = conf.allow_expect_in_tests;
 +    let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
 +    store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv)));
 +    store.register_late_pass(move |_| {
 +        Box::new(methods::Methods::new(
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        ))
 +    });
 +    store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv)));
++    let matches_for_let_else = conf.matches_for_let_else;
++    store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 +    store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
 +    store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
 +    let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
 +    store.register_late_pass(move |_| {
 +        Box::new(index_refutable_slice::IndexRefutableSlice::new(
 +            max_suggested_slice_pattern_length,
 +            msrv,
 +        ))
 +    });
 +    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(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));
-     store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
++    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(move || {
++    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(|_| Box::<write::Write>::default());
++    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(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(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(format_args::FormatArgs::new(msrv)));
 +    store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
 +    store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
 +    store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
 +    store.register_late_pass(|_| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
 +    store.register_late_pass(|_| Box::new(init_numbered_fields::NumberedFields));
 +    store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
 +    store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|_| 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_late_pass(|_| Box::new(manual_instant_elapsed::ManualInstantElapsed));
 +    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_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
 +    store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef));
 +    store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch));
 +    store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec));
 +    store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
 +    store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
 +    store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv)));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
 +    store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
++    store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv)));
 +    store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
 +    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 54c316358a14b161ec9fc75a28239c15ebc27cf0,0000000000000000000000000000000000000000..0bb9eca15287d13256f863186e68ea0067638e07
mode 100644,000000..100644
--- /dev/null
@@@ -1,615 -1,0 +1,660 @@@
- use clippy_utils::diagnostics::span_lint;
++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::{
 +    walk_fn_decl, 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, ParamName, 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.name != LifetimeName::Static && !bound.is_elided() {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
-     if could_use_elision(cx, decl, body, trait_sig, generics.params) {
-         span_lint(
++
++    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()),
-             "explicit lifetimes given in parameter types where they could be elided \
-              (or replaced with `'_` if needed by type declaration)",
++            &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<'_>],
- ) -> bool {
++) -> 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 false;
++        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 false;
++            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 false;
++            return None;
 +        }
 +
 +        let mut checker = BodyLifetimeChecker {
 +            lifetimes_used_in_body: false,
 +        };
 +        checker.visit_expr(body.value);
 +        if checker.lifetimes_used_in_body {
-             return false;
++            return None;
 +        }
 +    }
 +
 +    // check for lifetimes from higher scopes
 +    for lt in input_lts.iter().chain(output_lts.iter()) {
 +        if !allowed_lts.contains(lt) {
-             return false;
++            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 false;
++                    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 false;
++                    return None;
 +                }
 +            }
 +        }
 +    }
 +
-     // no input lifetimes? easy case!
-     if input_lts.is_empty() {
-         false
-     } else if output_lts.is_empty() {
-         // no output lifetimes, check distinctness of input lifetimes
++    // 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<_>>();
 +
-         // only unnamed and static, ok
-         let unnamed_and_static = input_lts.iter().all(|lt| *lt == RefLt::Unnamed || *lt == RefLt::Static);
-         if unnamed_and_static {
-             return false;
-         }
-         // we have no output reference, so we only need all distinct lifetimes
-         input_lts.len() == unique_lifetimes(&input_lts)
++    if elidable_lts.is_empty() {
++        None
 +    } else {
-         // we have output references, so we need one input reference,
-         // and all output lifetimes must be the same
-         if unique_lifetimes(&output_lts) > 1 {
-             return false;
-         }
-         if input_lts.len() == 1 {
-             match (&input_lts[0], &output_lts[0]) {
-                 (&RefLt::Named(n1), &RefLt::Named(n2)) if n1 == n2 => true,
-                 (&RefLt::Named(_), &RefLt::Unnamed) => true,
-                 _ => false, /* already elided, different named lifetimes
-                              * or something static going on */
-             }
-         } else {
-             false
-         }
++        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 unique lifetimes in the given vector.
++/// Number of times each named lifetime occurs in the given slice. Returns a vector to preserve
++/// relative order.
 +#[must_use]
- fn unique_lifetimes(lts: &[RefLt]) -> usize {
-     lts.iter().collect::<FxHashSet<_>>().len()
++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.name == LifetimeName::Static {
 +                self.lts.push(RefLt::Static);
 +            } else if let LifetimeName::Param(_, ParamName::Fresh) = lt.name {
 +                // Fresh lifetimes generated should be ignored.
 +                self.lts.push(RefLt::Unnamed);
 +            } else if lt.is_elided() {
 +                self.lts.push(RefLt::Unnamed);
 +            } else if let LifetimeName::Param(def_id, _) = lt.name {
 +                self.lts.push(RefLt::Named(def_id));
 +            } else {
 +                self.lts.push(RefLt::Unnamed);
 +            }
 +        } 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.name {
 +                        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());
 +            },
 +            TyKind::TraitObject(bounds, ref 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.name
++        {
++            self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.span);
++        }
++        // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands.
++        // walk_generic_arg(self, generic_arg);
++        match generic_arg {
++            GenericArg::Lifetime(lt) => self.visit_lifetime(lt),
++            GenericArg::Type(ty) => self.visit_ty(ty),
++            GenericArg::Const(ct) => self.visit_anon_const(&ct.value),
++            GenericArg::Infer(inf) => self.visit_infer(inf),
++        }
++    }
 +}
 +
 +/// 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.name.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 { .. } => 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 { .. } => 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.name.ident().name != kw::UnderscoreLifetime && lifetime.name.ident().name != kw::StaticLifetime {
 +            self.lifetimes_used_in_body = true;
 +        }
 +    }
 +}
index bcf278d9c8339c374449c0168a885a139e98a74c,0000000000000000000000000000000000000000..8e52cac4323c3102541c8e6efa238ca0b1c1bd69
mode 100644,000000..100644
--- /dev/null
@@@ -1,715 -1,0 +1,689 @@@
- mod needless_collect;
 +mod empty_loop;
 +mod explicit_counter_loop;
 +mod explicit_into_iter_loop;
 +mod explicit_iter_loop;
 +mod for_kv_map;
 +mod iter_next_loop;
 +mod manual_find;
 +mod manual_flatten;
 +mod manual_memcpy;
 +mod missing_spin_loop;
 +mod mut_range_bound;
- 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,
-     perf,
-     "collecting an iterator when collect is not needed"
- }
 +mod needless_range_loop;
 +mod never_loop;
 +mod same_item_push;
 +mod single_element_loop;
 +mod utils;
 +mod while_immutable_condition;
 +mod while_let_loop;
 +mod while_let_on_iterator;
 +
 +use clippy_utils::higher;
 +use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for for-loops that manually copy items between
 +    /// slices that could be optimized by having a memcpy.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not as fast as a memcpy.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let src = vec![1];
 +    /// # let mut dst = vec![0; 65];
 +    /// for i in 0..src.len() {
 +    ///     dst[i + 64] = src[i];
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let src = vec![1];
 +    /// # let mut dst = vec![0; 65];
 +    /// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MANUAL_MEMCPY,
 +    perf,
 +    "manually copying items between slices"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for looping over the range of `0..len` of some
 +    /// collection just to get the values by index.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just iterating the collection itself makes the intent
 +    /// more clear and is probably faster.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec!['a', 'b', 'c'];
 +    /// for i in 0..vec.len() {
 +    ///     println!("{}", vec[i]);
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec!['a', 'b', 'c'];
 +    /// for i in vec {
 +    ///     println!("{}", i);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_RANGE_LOOP,
 +    style,
 +    "for-looping over a range of indices where an iterator over items would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops on `x.iter()` where `&x` will do, and
 +    /// suggests the latter.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Known problems
 +    /// False negatives. We currently only warn on some known
 +    /// types.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // with `y` a `Vec` or slice:
 +    /// # let y = vec![1];
 +    /// for x in y.iter() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// for x in &y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPLICIT_ITER_LOOP,
 +    pedantic,
 +    "for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops on `y.into_iter()` where `y` will do, and
 +    /// suggests the latter.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// // with `y` a `Vec` or slice:
 +    /// for x in y.into_iter() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    /// can be rewritten to
 +    /// ```rust
 +    /// # let y = vec![1];
 +    /// for x in y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPLICIT_INTO_ITER_LOOP,
 +    pedantic,
 +    "for-looping over `_.into_iter()` when `_` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops on `x.next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `next()` returns either `Some(value)` if there was a
 +    /// value, or `None` otherwise. The insidious thing is that `Option<_>`
 +    /// implements `IntoIterator`, so that possibly one value will be iterated,
 +    /// leading to some hard to find bugs. No one will want to write such code
 +    /// [except to win an Underhanded Rust
 +    /// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// for x in y.next() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_NEXT_LOOP,
 +    correctness,
 +    "for-looping over `_.next()` which is probably not intended"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `loop + match` combinations that are easier
 +    /// written as a `while let` loop.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `while let` loop is usually shorter and more
 +    /// readable.
 +    ///
 +    /// ### Known problems
 +    /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)).
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # let y = Some(1);
 +    /// loop {
 +    ///     let x = match y {
 +    ///         Some(x) => x,
 +    ///         None => break,
 +    ///     };
 +    ///     // .. do something with x
 +    /// }
 +    /// // is easier written as
 +    /// while let Some(x) = y {
 +    ///     // .. do something with x
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WHILE_LET_LOOP,
 +    complexity,
 +    "`loop { if let { ... } else break }`, which can be written as a `while let` loop"
 +}
 +
-     NEEDLESS_COLLECT,
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks `for` loops over slices with an explicit counter
 +    /// and suggests the use of `.enumerate()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `.enumerate()` makes the intent more clear,
 +    /// declutters the code and may be faster in some instances.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let v = vec![1];
 +    /// # fn bar(bar: usize, baz: usize) {}
 +    /// let mut i = 0;
 +    /// for item in &v {
 +    ///     bar(i, *item);
 +    ///     i += 1;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let v = vec![1];
 +    /// # fn bar(bar: usize, baz: usize) {}
 +    /// for (i, item) in v.iter().enumerate() { bar(i, *item); }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPLICIT_COUNTER_LOOP,
 +    complexity,
 +    "for-looping with an explicit counter when `_.enumerate()` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for empty `loop` expressions.
 +    ///
 +    /// ### Why is this bad?
 +    /// These busy loops burn CPU cycles without doing
 +    /// anything. It is _almost always_ a better idea to `panic!` than to have
 +    /// a busy loop.
 +    ///
 +    /// If panicking isn't possible, think of the environment and either:
 +    ///   - block on something
 +    ///   - sleep the thread for some microseconds
 +    ///   - yield or pause the thread
 +    ///
 +    /// For `std` targets, this can be done with
 +    /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
 +    /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
 +    ///
 +    /// For `no_std` targets, doing this is more complicated, especially because
 +    /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
 +    /// probably need to invoke some target-specific intrinsic. Examples include:
 +    ///   - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
 +    ///   - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
 +    ///
 +    /// ### Example
 +    /// ```no_run
 +    /// loop {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EMPTY_LOOP,
 +    suspicious,
 +    "empty `loop {}`, which should block or sleep"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `while let` expressions on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. A simple `for` loop is shorter and conveys
 +    /// the intent better.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// while let Some(val) = iter.next() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```ignore
 +    /// for val in &mut iter {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WHILE_LET_ON_ITERATOR,
 +    style,
 +    "using a `while let` loop instead of a for loop on an iterator"
 +}
 +
 +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 don't need the values or keys.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// for (k, _) in &map {
 +    ///     ..
 +    /// }
 +    /// ```
 +    ///
 +    /// could be replaced by
 +    ///
 +    /// ```ignore
 +    /// for k in map.keys() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FOR_KV_MAP,
 +    style,
 +    "looping on a map using `iter` when `keys` or `values` would do"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops that will always `break`, `return` or
 +    /// `continue` an outer loop.
 +    ///
 +    /// ### Why is this bad?
 +    /// This loop never loops, all it does is obfuscating the
 +    /// code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// loop {
 +    ///     ..;
 +    ///     break;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEVER_LOOP,
 +    correctness,
 +    "any loop that will always `break` or `return`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for loops which have a range bound that is a mutable variable
 +    ///
 +    /// ### Why is this bad?
 +    /// One might think that modifying the mutable variable changes the loop bounds
 +    ///
 +    /// ### Known problems
 +    /// False positive when mutation is followed by a `break`, but the `break` is not immediately
 +    /// after the mutation:
 +    ///
 +    /// ```rust
 +    /// let mut x = 5;
 +    /// for _ in 0..x {
 +    ///     x += 1; // x is a range bound that is mutated
 +    ///     ..; // some other expression
 +    ///     break; // leaves the loop, so mutation is not an issue
 +    /// }
 +    /// ```
 +    ///
 +    /// False positive on nested loops ([#6072](https://github.com/rust-lang/rust-clippy/issues/6072))
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut foo = 42;
 +    /// for i in 0..foo {
 +    ///     foo -= 1;
 +    ///     println!("{}", i); // prints numbers from 0 to 42, not 0 to 21
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MUT_RANGE_BOUND,
 +    suspicious,
 +    "for loop over a range where one of the bounds is a mutable variable"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks whether variables used within while loop condition
 +    /// can be (and are) mutated in the body.
 +    ///
 +    /// ### Why is this bad?
 +    /// If the condition is unchanged, entering the body of the loop
 +    /// will lead to an infinite loop.
 +    ///
 +    /// ### Known problems
 +    /// If the `while`-loop is in a closure, the check for mutation of the
 +    /// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
 +    /// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let i = 0;
 +    /// while i > 10 {
 +    ///     println!("let me loop forever!");
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WHILE_IMMUTABLE_CONDITION,
 +    correctness,
 +    "variables used within while expression are not mutated in the body"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks whether a for loop is being used to push a constant
 +    /// value into a Vec.
 +    ///
 +    /// ### Why is this bad?
 +    /// This kind of operation can be expressed more succinctly with
 +    /// `vec![item; SIZE]` or `vec.resize(NEW_SIZE, item)` and using these alternatives may also
 +    /// have better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item2 = 3;
 +    /// let mut vec: Vec<u8> = Vec::new();
 +    /// for _ in 0..20 {
 +    ///    vec.push(item1);
 +    /// }
 +    /// for _ in 0..30 {
 +    ///     vec.push(item2);
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item2 = 3;
 +    /// let mut vec: Vec<u8> = vec![item1; 20];
 +    /// vec.resize(20 + 30, item2);
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub SAME_ITEM_PUSH,
 +    style,
 +    "the same item is pushed inside of a for loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks whether a for loop has a single element.
 +    ///
 +    /// ### Why is this bad?
 +    /// There is no reason to have a loop of a
 +    /// single element.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// for item in &[item1] {
 +    ///     println!("{}", item);
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let item1 = 2;
 +    /// let item = &item1;
 +    /// println!("{}", item);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub SINGLE_ELEMENT_LOOP,
 +    complexity,
 +    "there is no reason to have a single element loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for unnecessary `if let` usage in a for loop
 +    /// where only the `Some` or `Ok` variant of the iterator element is used.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is verbose and can be simplified
 +    /// by first calling the `flatten` method on the `Iterator`.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// let x = vec![Some(1), Some(2), Some(3)];
 +    /// for n in x {
 +    ///     if let Some(n) = n {
 +    ///         println!("{}", n);
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![Some(1), Some(2), Some(3)];
 +    /// for n in x.into_iter().flatten() {
 +    ///     println!("{}", n);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub MANUAL_FLATTEN,
 +    complexity,
 +    "for loops over `Option`s or `Result`s with a single expression can be simplified"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for empty spin loops
 +    ///
 +    /// ### Why is this bad?
 +    /// The loop body should have something like `thread::park()` or at least
 +    /// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
 +    /// energy. Perhaps even better use an actual lock, if possible.
 +    ///
 +    /// ### Known problems
 +    /// This lint doesn't currently trigger on `while let` or
 +    /// `loop { match .. { .. } }` loops, which would be considered idiomatic in
 +    /// combination with e.g. `AtomicBool::compare_exchange_weak`.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```ignore
 +    /// use core::sync::atomic::{AtomicBool, Ordering};
 +    /// let b = AtomicBool::new(true);
 +    /// // give a ref to `b` to another thread,wait for it to become false
 +    /// while b.load(Ordering::Acquire) {};
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,no_run
 +    ///# use core::sync::atomic::{AtomicBool, Ordering};
 +    ///# let b = AtomicBool::new(true);
 +    /// while b.load(Ordering::Acquire) {
 +    ///     std::hint::spin_loop()
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub MISSING_SPIN_LOOP,
 +    perf,
 +    "An empty busy waiting loop"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check for manual implementations of Iterator::find
 +    ///
 +    /// ### Why is this bad?
 +    /// It doesn't affect performance, but using `find` is shorter and easier to read.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// fn example(arr: Vec<i32>) -> Option<i32> {
 +    ///     for el in arr {
 +    ///         if el == 1 {
 +    ///             return Some(el);
 +    ///         }
 +    ///     }
 +    ///     None
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn example(arr: Vec<i32>) -> Option<i32> {
 +    ///     arr.into_iter().find(|&el| el == 1)
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub MANUAL_FIND,
 +    complexity,
 +    "manual implementation of `Iterator::find`"
 +}
 +
 +declare_lint_pass!(Loops => [
 +    MANUAL_MEMCPY,
 +    MANUAL_FLATTEN,
 +    NEEDLESS_RANGE_LOOP,
 +    EXPLICIT_ITER_LOOP,
 +    EXPLICIT_INTO_ITER_LOOP,
 +    ITER_NEXT_LOOP,
 +    WHILE_LET_LOOP,
-         needless_collect::check(expr, cx);
 +    EXPLICIT_COUNTER_LOOP,
 +    EMPTY_LOOP,
 +    WHILE_LET_ON_ITERATOR,
 +    FOR_KV_MAP,
 +    NEVER_LOOP,
 +    MUT_RANGE_BOUND,
 +    WHILE_IMMUTABLE_CONDITION,
 +    SAME_ITEM_PUSH,
 +    SINGLE_ELEMENT_LOOP,
 +    MISSING_SPIN_LOOP,
 +    MANUAL_FIND,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Loops {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let for_loop = higher::ForLoop::hir(expr);
 +        if let Some(higher::ForLoop {
 +            pat,
 +            arg,
 +            body,
 +            loop_id,
 +            span,
 +        }) = for_loop
 +        {
 +            // we don't want to check expanded macros
 +            // this check is not at the top of the function
 +            // since higher::for_loop expressions are marked as expansions
 +            if body.span.from_expansion() {
 +                return;
 +            }
 +            check_for_loop(cx, pat, arg, body, expr, span);
 +            if let ExprKind::Block(block, _) = body.kind {
 +                never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
 +            }
 +        }
 +
 +        // we don't want to check expanded macros
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        // check for never_loop
 +        if let ExprKind::Loop(block, ..) = expr.kind {
 +            never_loop::check(cx, block, expr.hir_id, expr.span, None);
 +        }
 +
 +        // check for `loop { if let {} else break }` that could be `while let`
 +        // (also matches an explicit "match" instead of "if let")
 +        // (even if the "match" or "if let" is used for declaration)
 +        if let ExprKind::Loop(block, _, LoopSource::Loop, _) = expr.kind {
 +            // also check for empty `loop {}` statements, skipping those in #[panic_handler]
 +            empty_loop::check(cx, expr, block);
 +            while_let_loop::check(cx, expr, block);
 +        }
 +
 +        while_let_on_iterator::check(cx, expr);
 +
 +        if let Some(higher::While { condition, body }) = higher::While::hir(expr) {
 +            while_immutable_condition::check(cx, condition, body);
 +            missing_spin_loop::check(cx, condition, body);
 +        }
 +    }
 +}
 +
 +fn check_for_loop<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +    span: Span,
 +) {
 +    let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
 +    if !is_manual_memcpy_triggered {
 +        needless_range_loop::check(cx, pat, arg, body, expr);
 +        explicit_counter_loop::check(cx, pat, arg, body, expr);
 +    }
 +    check_for_loop_arg(cx, pat, arg);
 +    for_kv_map::check(cx, pat, arg, body);
 +    mut_range_bound::check(cx, arg, body);
 +    single_element_loop::check(cx, pat, arg, body, expr);
 +    same_item_push::check(cx, pat, arg, body, expr);
 +    manual_flatten::check(cx, pat, arg, body, span);
 +    manual_find::check(cx, pat, arg, body, span, expr);
 +}
 +
 +fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
 +    if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
 +        let method_name = method.ident.as_str();
 +        // check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
 +        match method_name {
 +            "iter" | "iter_mut" => {
 +                explicit_iter_loop::check(cx, self_arg, arg, method_name);
 +            },
 +            "into_iter" => {
 +                explicit_iter_loop::check(cx, self_arg, arg, method_name);
 +                explicit_into_iter_loop::check(cx, self_arg, arg);
 +            },
 +            "next" => {
 +                iter_next_loop::check(cx, arg);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
index 91b321c4474794711e4c03a91405a97b907fbe18,0000000000000000000000000000000000000000..4dae93f6028d421cd9fa7f759bcb4eaf1190959e
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,166 @@@
- fn check_for_mutation<'tcx>(
-     cx: &LateContext<'tcx>,
 +use super::MUT_RANGE_BOUND;
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::{get_enclosing_block, higher, path_to_local};
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
 +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::{mir::FakeReadCause, ty};
 +use rustc_span::source_map::Span;
 +
 +pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range {
 +            start: Some(start),
 +            end: Some(end),
 +            ..
 +        }) = higher::Range::hir(arg);
 +        let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end));
 +        if mut_id_start.is_some() || mut_id_end.is_some();
 +        then {
 +            let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end);
 +            mut_warn_with_span(cx, span_low);
 +            mut_warn_with_span(cx, span_high);
 +        }
 +    }
 +}
 +
 +fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
 +    if let Some(sp) = span {
 +        span_lint_and_note(
 +            cx,
 +            MUT_RANGE_BOUND,
 +            sp,
 +            "attempt to mutate range bound within loop",
 +            None,
 +            "the range of the loop is unchanged",
 +        );
 +    }
 +}
 +
 +fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
 +    if_chain! {
 +        if let Some(hir_id) = path_to_local(bound);
 +        if let Node::Pat(pat) = cx.tcx.hir().get(hir_id);
 +        if let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind;
 +        then {
 +            return Some(hir_id);
 +        }
 +    }
 +    None
 +}
 +
-     fn fake_read(
-         &mut self,
-         _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
-         _: FakeReadCause,
-         _: HirId,
-     ) {
-     }
++fn check_for_mutation(
++    cx: &LateContext<'_>,
 +    body: &Expr<'_>,
 +    bound_id_start: Option<HirId>,
 +    bound_id_end: Option<HirId>,
 +) -> (Option<Span>, Option<Span>) {
 +    let mut delegate = MutatePairDelegate {
 +        cx,
 +        hir_id_low: bound_id_start,
 +        hir_id_high: bound_id_end,
 +        span_low: None,
 +        span_high: None,
 +    };
 +    let infcx = cx.tcx.infer_ctxt().build();
 +    ExprUseVisitor::new(
 +        &mut delegate,
 +        &infcx,
 +        body.hir_id.owner.def_id,
 +        cx.param_env,
 +        cx.typeck_results(),
 +    )
 +    .walk_expr(body);
 +
 +    delegate.mutation_span()
 +}
 +
 +struct MutatePairDelegate<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    hir_id_low: Option<HirId>,
 +    hir_id_high: Option<HirId>,
 +    span_low: Option<Span>,
 +    span_high: Option<Span>,
 +}
 +
 +impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
 +        if bk == ty::BorrowKind::MutBorrow {
 +            if let PlaceBase::Local(id) = cmt.place.base {
 +                if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                    self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
 +                }
 +                if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                    self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
 +                }
 +            }
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
 +        if let PlaceBase::Local(id) = cmt.place.base {
 +            if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
 +            }
 +            if Some(id) == self.hir_id_high && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
 +                self.span_high = Some(self.cx.tcx.hir().span(diag_expr_id));
 +            }
 +        }
 +    }
 +
++    fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +impl MutatePairDelegate<'_, '_> {
 +    fn mutation_span(&self) -> (Option<Span>, Option<Span>) {
 +        (self.span_low, self.span_high)
 +    }
 +}
 +
 +struct BreakAfterExprVisitor {
 +    hir_id: HirId,
 +    past_expr: bool,
 +    past_candidate: bool,
 +    break_after_expr: bool,
 +}
 +
 +impl BreakAfterExprVisitor {
 +    pub fn is_found(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +        let mut visitor = BreakAfterExprVisitor {
 +            hir_id,
 +            past_expr: false,
 +            past_candidate: false,
 +            break_after_expr: false,
 +        };
 +
 +        get_enclosing_block(cx, hir_id).map_or(false, |block| {
 +            visitor.visit_block(block);
 +            visitor.break_after_expr
 +        })
 +    }
 +}
 +
 +impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if self.past_candidate {
 +            return;
 +        }
 +
 +        if expr.hir_id == self.hir_id {
 +            self.past_expr = true;
 +        } else if self.past_expr {
 +            if matches!(&expr.kind, ExprKind::Break(..)) {
 +                self.break_after_expr = true;
 +            }
 +
 +            self.past_candidate = true;
 +        } else {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +}
index 16b00ad663787cd4c495d5acc9b48917b41be5a9,0000000000000000000000000000000000000000..14f161f51026526a1cfe63bd0aaae6b6e915c23c
mode 100644,000000..100644
--- /dev/null
@@@ -1,226 -1,0 +1,254 @@@
- use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
 +use super::utils::make_iterator_snippet;
 +use super::NEVER_LOOP;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::ForLoop;
 +use clippy_utils::source::snippet;
 +use rustc_errors::Applicability;
-     match never_loop_block(block, loop_id) {
++use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
 +use rustc_lint::LateContext;
 +use rustc_span::Span;
 +use std::iter::{once, Iterator};
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    block: &Block<'_>,
 +    loop_id: HirId,
 +    span: Span,
 +    for_loop: Option<&ForLoop<'_>>,
 +) {
- fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
-     let mut iter = block
++    match never_loop_block(block, &mut Vec::new(), loop_id) {
 +        NeverLoopResult::AlwaysBreak => {
 +            span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
 +                if let Some(ForLoop {
 +                    arg: iterator,
 +                    pat,
 +                    span: for_span,
 +                    ..
 +                }) = for_loop
 +                {
 +                    // Suggests using an `if let` instead. This is `Unspecified` because the
 +                    // loop may (probably) contain `break` statements which would be invalid
 +                    // in an `if let`.
 +                    diag.span_suggestion_verbose(
 +                        for_span.with_hi(iterator.span.hi()),
 +                        "if you need the first element of the iterator, try writing",
 +                        for_to_if_let_sugg(cx, iterator, pat),
 +                        Applicability::Unspecified,
 +                    );
 +                }
 +            });
 +        },
 +        NeverLoopResult::MayContinueMainLoop | NeverLoopResult::Otherwise => (),
 +    }
 +}
 +
 +#[derive(Copy, Clone)]
 +enum NeverLoopResult {
 +    // A break/return always get triggered but not necessarily for the main loop.
 +    AlwaysBreak,
 +    // A continue may occur for the main loop.
 +    MayContinueMainLoop,
 +    Otherwise,
 +}
 +
 +#[must_use]
 +fn absorb_break(arg: NeverLoopResult) -> NeverLoopResult {
 +    match arg {
 +        NeverLoopResult::AlwaysBreak | NeverLoopResult::Otherwise => NeverLoopResult::Otherwise,
 +        NeverLoopResult::MayContinueMainLoop => NeverLoopResult::MayContinueMainLoop,
 +    }
 +}
 +
 +// Combine two results for parts that are called in order.
 +#[must_use]
 +fn combine_seq(first: NeverLoopResult, second: NeverLoopResult) -> NeverLoopResult {
 +    match first {
 +        NeverLoopResult::AlwaysBreak | NeverLoopResult::MayContinueMainLoop => first,
 +        NeverLoopResult::Otherwise => second,
 +    }
 +}
 +
 +// Combine two results where both parts are called but not necessarily in order.
 +#[must_use]
 +fn combine_both(left: NeverLoopResult, right: NeverLoopResult) -> NeverLoopResult {
 +    match (left, right) {
 +        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
 +            NeverLoopResult::MayContinueMainLoop
 +        },
 +        (NeverLoopResult::AlwaysBreak, _) | (_, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
 +        (NeverLoopResult::Otherwise, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +// Combine two results where only one of the part may have been executed.
 +#[must_use]
 +fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult {
 +    match (b1, b2) {
 +        (NeverLoopResult::AlwaysBreak, NeverLoopResult::AlwaysBreak) => NeverLoopResult::AlwaysBreak,
 +        (NeverLoopResult::MayContinueMainLoop, _) | (_, NeverLoopResult::MayContinueMainLoop) => {
 +            NeverLoopResult::MayContinueMainLoop
 +        },
 +        (NeverLoopResult::Otherwise, _) | (_, NeverLoopResult::Otherwise) => NeverLoopResult::Otherwise,
 +    }
 +}
 +
-     never_loop_expr_seq(&mut iter, main_loop_id)
- }
++fn never_loop_block(block: &Block<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
++    let iter = block
 +        .stmts
 +        .iter()
 +        .filter_map(stmt_to_expr)
 +        .chain(block.expr.map(|expr| (expr, None)));
- fn never_loop_expr_seq<'a, T: Iterator<Item = (&'a Expr<'a>, Option<&'a Block<'a>>)>>(
-     es: &mut T,
-     main_loop_id: HirId,
- ) -> NeverLoopResult {
-     es.map(|(e, els)| {
-         let e = never_loop_expr(e, main_loop_id);
-         els.map_or(e, |els| combine_branches(e, never_loop_block(els, main_loop_id)))
 +
-         StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some((e, None)),
++    iter.map(|(e, els)| {
++        let e = never_loop_expr(e, ignore_ids, main_loop_id);
++        // els is an else block in a let...else binding
++        els.map_or(e, |els| {
++            combine_branches(e, never_loop_block(els, ignore_ids, main_loop_id))
++        })
 +    })
 +    .fold(NeverLoopResult::Otherwise, combine_seq)
 +}
 +
 +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<(&'tcx Expr<'tcx>, Option<&'tcx Block<'tcx>>)> {
 +    match stmt.kind {
- fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
++        StmtKind::Semi(e) | StmtKind::Expr(e) => Some((e, None)),
++        // add the let...else expression (if present)
 +        StmtKind::Local(local) => local.init.map(|init| (init, local.els)),
 +        StmtKind::Item(..) => None,
 +    }
 +}
 +
-         | ExprKind::DropTemps(e) => never_loop_expr(e, main_loop_id),
-         ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, main_loop_id),
-         ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), main_loop_id),
-         ExprKind::MethodCall(_, receiver, es, _) => {
-             never_loop_expr_all(&mut std::iter::once(receiver).chain(es.iter()), main_loop_id)
-         },
++#[allow(clippy::too_many_lines)]
++fn never_loop_expr(expr: &Expr<'_>, ignore_ids: &mut Vec<HirId>, main_loop_id: HirId) -> NeverLoopResult {
 +    match expr.kind {
 +        ExprKind::Box(e)
 +        | ExprKind::Unary(_, e)
 +        | ExprKind::Cast(e, _)
 +        | ExprKind::Type(e, _)
 +        | ExprKind::Field(e, _)
 +        | ExprKind::AddrOf(_, _, e)
 +        | ExprKind::Repeat(e, _)
-             let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), main_loop_id);
++        | ExprKind::DropTemps(e) => never_loop_expr(e, ignore_ids, main_loop_id),
++        ExprKind::Let(let_expr) => never_loop_expr(let_expr.init, ignore_ids, main_loop_id),
++        ExprKind::Array(es) | ExprKind::Tup(es) => never_loop_expr_all(&mut es.iter(), ignore_ids, main_loop_id),
++        ExprKind::MethodCall(_, receiver, es, _) => never_loop_expr_all(
++            &mut std::iter::once(receiver).chain(es.iter()),
++            ignore_ids,
++            main_loop_id,
++        ),
 +        ExprKind::Struct(_, fields, base) => {
-                 combine_both(fields, never_loop_expr(base, main_loop_id))
++            let fields = never_loop_expr_all(&mut fields.iter().map(|f| f.expr), ignore_ids, main_loop_id);
 +            if let Some(base) = base {
-         ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
++                combine_both(fields, never_loop_expr(base, ignore_ids, main_loop_id))
 +            } else {
 +                fields
 +            }
 +        },
-         | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
++        ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), ignore_ids, main_loop_id),
 +        ExprKind::Binary(_, e1, e2)
 +        | ExprKind::Assign(e1, e2, _)
 +        | ExprKind::AssignOp(_, e1, e2)
-             absorb_break(never_loop_block(b, main_loop_id))
++        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), ignore_ids, main_loop_id),
 +        ExprKind::Loop(b, _, _, _) => {
 +            // Break can come from the inner loop so remove them.
-             let e1 = never_loop_expr(e, main_loop_id);
-             let e2 = never_loop_expr(e2, main_loop_id);
-             let e3 = e3
-                 .as_ref()
-                 .map_or(NeverLoopResult::Otherwise, |e| never_loop_expr(e, main_loop_id));
++            absorb_break(never_loop_block(b, ignore_ids, main_loop_id))
 +        },
 +        ExprKind::If(e, e2, e3) => {
-             let e = never_loop_expr(e, main_loop_id);
++            let e1 = never_loop_expr(e, ignore_ids, main_loop_id);
++            let e2 = never_loop_expr(e2, ignore_ids, main_loop_id);
++            let e3 = e3.as_ref().map_or(NeverLoopResult::Otherwise, |e| {
++                never_loop_expr(e, ignore_ids, main_loop_id)
++            });
 +            combine_seq(e1, combine_branches(e2, e3))
 +        },
 +        ExprKind::Match(e, arms, _) => {
-                 let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id);
++            let e = never_loop_expr(e, ignore_ids, main_loop_id);
 +            if arms.is_empty() {
 +                e
 +            } else {
-         ExprKind::Block(b, _) => never_loop_block(b, main_loop_id),
++                let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), ignore_ids, main_loop_id);
 +                combine_seq(e, arms)
 +            }
 +        },
-             combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
++        ExprKind::Block(b, l) => {
++            if l.is_some() {
++                ignore_ids.push(b.hir_id);
++            }
++            let ret = never_loop_block(b, ignore_ids, main_loop_id);
++            ignore_ids.pop();
++            ret
++        },
 +        ExprKind::Continue(d) => {
 +            let id = d
 +                .target_id
 +                .expect("target ID can only be missing in the presence of compilation errors");
 +            if id == main_loop_id {
 +                NeverLoopResult::MayContinueMainLoop
 +            } else {
 +                NeverLoopResult::AlwaysBreak
 +            }
 +        },
++        // checks if break targets a block instead of a loop
++        ExprKind::Break(Destination { target_id: Ok(t), .. }, e) if ignore_ids.contains(&t) => e
++            .map_or(NeverLoopResult::Otherwise, |e| {
++                combine_seq(never_loop_expr(e, ignore_ids, main_loop_id), NeverLoopResult::Otherwise)
++            }),
 +        ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
-                     never_loop_expr(expr, main_loop_id)
++            combine_seq(
++                never_loop_expr(e, ignore_ids, main_loop_id),
++                NeverLoopResult::AlwaysBreak,
++            )
 +        }),
 +        ExprKind::InlineAsm(asm) => asm
 +            .operands
 +            .iter()
 +            .map(|(o, _)| match o {
 +                InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
-                 InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter().copied(), main_loop_id),
-                 InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
-                     never_loop_expr_all(&mut once(*in_expr).chain(out_expr.iter().copied()), main_loop_id)
++                    never_loop_expr(expr, ignore_ids, main_loop_id)
 +                },
- fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_loop_id: HirId) -> NeverLoopResult {
-     es.map(|e| never_loop_expr(e, main_loop_id))
++                InlineAsmOperand::Out { expr, .. } => {
++                    never_loop_expr_all(&mut expr.iter().copied(), ignore_ids, main_loop_id)
 +                },
++                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => never_loop_expr_all(
++                    &mut once(*in_expr).chain(out_expr.iter().copied()),
++                    ignore_ids,
++                    main_loop_id,
++                ),
 +                InlineAsmOperand::Const { .. }
 +                | InlineAsmOperand::SymFn { .. }
 +                | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
 +            })
 +            .fold(NeverLoopResult::Otherwise, combine_both),
 +        ExprKind::Yield(_, _)
 +        | ExprKind::Closure { .. }
 +        | ExprKind::Path(_)
 +        | ExprKind::ConstBlock(_)
 +        | ExprKind::Lit(_)
 +        | ExprKind::Err => NeverLoopResult::Otherwise,
 +    }
 +}
 +
- fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(e: &mut T, main_loop_id: HirId) -> NeverLoopResult {
-     e.map(|e| never_loop_expr(e, main_loop_id))
++fn never_loop_expr_all<'a, T: Iterator<Item = &'a Expr<'a>>>(
++    es: &mut T,
++    ignore_ids: &mut Vec<HirId>,
++    main_loop_id: HirId,
++) -> NeverLoopResult {
++    es.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
 +        .fold(NeverLoopResult::Otherwise, combine_both)
 +}
 +
++fn never_loop_expr_branch<'a, T: Iterator<Item = &'a Expr<'a>>>(
++    e: &mut T,
++    ignore_ids: &mut Vec<HirId>,
++    main_loop_id: HirId,
++) -> NeverLoopResult {
++    e.map(|e| never_loop_expr(e, ignore_ids, main_loop_id))
 +        .fold(NeverLoopResult::AlwaysBreak, combine_branches)
 +}
 +
 +fn for_to_if_let_sugg(cx: &LateContext<'_>, iterator: &Expr<'_>, pat: &Pat<'_>) -> String {
 +    let pat_snippet = snippet(cx, pat.span, "_");
 +    let iter_snippet = make_iterator_snippet(cx, iterator, &mut Applicability::Unspecified);
 +
 +    format!("if let Some({pat_snippet}) = {iter_snippet}.next()")
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bb8c142f8e4680e3b1ccea794cedc942f903d5e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,158 @@@
++use rustc_ast::LitKind::{Byte, Char};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_semver::RustcVersion;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{def_id::DefId, sym};
++
++use clippy_utils::{
++    diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet,
++};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Suggests to use dedicated built-in methods,
++    /// `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 {
++    msrv: Option<RustcVersion>,
++}
++
++impl ManualIsAsciiCheck {
++    #[must_use]
++    pub fn new(msrv: Option<RustcVersion>) -> 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 !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) {
++            return;
++        }
++
++        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, 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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1846596fa4c8e259ab6f0ffb580807a47de5381b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,297 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::higher::IfLetOrMatch;
++use clippy_utils::source::snippet_opt;
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::visitors::{for_each_expr, Descend};
++use clippy_utils::{meets_msrv, msrvs, peel_blocks};
++use if_chain::if_chain;
++use rustc_data_structures::fx::FxHashSet;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_semver::RustcVersion;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::symbol::sym;
++use rustc_span::Span;
++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 {
++    msrv: Option<RustcVersion>,
++    matches_behaviour: MatchLintBehaviour,
++}
++
++impl ManualLetElse {
++    #[must_use]
++    pub fn new(msrv: Option<RustcVersion>, 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! {
++            if meets_msrv(self.msrv, 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.
++            let app = Applicability::HasPlaceholders;
++
++            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);
++            }
++        },
++    );
++}
++
++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 32da37a862d8ca02f30001805b013a8fc2f89a0d,0000000000000000000000000000000000000000..59195d1ae4e0a5854865b83243de49155df97f83
mode 100644,000000..100644
--- /dev/null
@@@ -1,270 -1,0 +1,270 @@@
- fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<Span> {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{iter_input_pats, method_chain_args};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, Ty};
 +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 usage of `option.map(f)` where f is a function
 +    /// or closure that returns the unit type `()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more clearly with
 +    /// an if let statement
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff() -> Option<String> { Some(String::new()) }
 +    /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
 +    /// # fn format_msg(foo: String) -> String { String::new() }
 +    /// let x: Option<String> = do_stuff();
 +    /// x.map(log_err_msg);
 +    /// # let x: Option<String> = do_stuff();
 +    /// x.map(|msg| log_err_msg(format_msg(msg)));
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn do_stuff() -> Option<String> { Some(String::new()) }
 +    /// # fn log_err_msg(foo: String) -> Option<String> { Some(foo) }
 +    /// # fn format_msg(foo: String) -> String { String::new() }
 +    /// let x: Option<String> = do_stuff();
 +    /// if let Some(msg) = x {
 +    ///     log_err_msg(msg);
 +    /// }
 +    ///
 +    /// # let x: Option<String> = do_stuff();
 +    /// if let Some(msg) = x {
 +    ///     log_err_msg(format_msg(msg));
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_MAP_UNIT_FN,
 +    complexity,
 +    "using `option.map(f)`, where `f` is a function or closure that returns `()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `result.map(f)` where f is a function
 +    /// or closure that returns the unit type `()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more clearly with
 +    /// an if let statement
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
 +    /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
 +    /// # fn format_msg(foo: String) -> String { String::new() }
 +    /// let x: Result<String, String> = do_stuff();
 +    /// x.map(log_err_msg);
 +    /// # let x: Result<String, String> = do_stuff();
 +    /// x.map(|msg| log_err_msg(format_msg(msg)));
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn do_stuff() -> Result<String, String> { Ok(String::new()) }
 +    /// # fn log_err_msg(foo: String) -> Result<String, String> { Ok(foo) }
 +    /// # fn format_msg(foo: String) -> String { String::new() }
 +    /// let x: Result<String, String> = do_stuff();
 +    /// if let Ok(msg) = x {
 +    ///     log_err_msg(msg);
 +    /// };
 +    /// # let x: Result<String, String> = do_stuff();
 +    /// if let Ok(msg) = x {
 +    ///     log_err_msg(format_msg(msg));
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RESULT_MAP_UNIT_FN,
 +    complexity,
 +    "using `result.map(f)`, where `f` is a function or closure that returns `()`"
 +}
 +
 +declare_lint_pass!(MapUnit => [OPTION_MAP_UNIT_FN, RESULT_MAP_UNIT_FN]);
 +
 +fn is_unit_type(ty: Ty<'_>) -> bool {
 +    ty.is_unit() || ty.is_never()
 +}
 +
 +fn is_unit_function(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +
 +    if let ty::FnDef(id, _) = *ty.kind() {
 +        if let Some(fn_type) = cx.tcx.fn_sig(id).no_bound_vars() {
 +            return is_unit_type(fn_type.output());
 +        }
 +    }
 +    false
 +}
 +
 +fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
 +    is_unit_type(cx.typeck_results().expr_ty(expr))
 +}
 +
 +/// The expression inside a closure may or may not have surrounding braces and
 +/// semicolons, which causes problems when generating a suggestion. Given an
 +/// expression that evaluates to '()' or '!', recursively remove useless braces
 +/// and semi-colons until is suitable for including in the suggestion template
++fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Span> {
 +    if !is_unit_expression(cx, expr) {
 +        return None;
 +    }
 +
 +    match expr.kind {
 +        hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(..) => {
 +            // Calls can't be reduced any more
 +            Some(expr.span)
 +        },
 +        hir::ExprKind::Block(block, _) => {
 +            match (block.stmts, block.expr.as_ref()) {
 +                ([], Some(inner_expr)) => {
 +                    // If block only contains an expression,
 +                    // reduce `{ X }` to `X`
 +                    reduce_unit_expression(cx, inner_expr)
 +                },
 +                ([inner_stmt], None) => {
 +                    // If block only contains statements,
 +                    // reduce `{ X; }` to `X` or `X;`
 +                    match inner_stmt.kind {
 +                        hir::StmtKind::Local(local) => Some(local.span),
 +                        hir::StmtKind::Expr(e) => Some(e.span),
 +                        hir::StmtKind::Semi(..) => Some(inner_stmt.span),
 +                        hir::StmtKind::Item(..) => None,
 +                    }
 +                },
 +                _ => {
 +                    // For closures that contain multiple statements
 +                    // it's difficult to get a correct suggestion span
 +                    // for all cases (multi-line closures specifically)
 +                    //
 +                    // We do not attempt to build a suggestion for those right now.
 +                    None
 +                },
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn unit_closure<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
 +    if_chain! {
 +        if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind;
 +        let body = cx.tcx.hir().body(body);
 +        let body_expr = &body.value;
 +        if fn_decl.inputs.len() == 1;
 +        if is_unit_expression(cx, body_expr);
 +        if let Some(binding) = iter_input_pats(fn_decl, body).next();
 +        then {
 +            return Some((binding, body_expr));
 +        }
 +    }
 +    None
 +}
 +
 +/// Builds a name for the let binding variable (`var_arg`)
 +///
 +/// `x.field` => `x_field`
 +/// `y` => `_y`
 +///
 +/// Anything else will return `a`.
 +fn let_binding_name(cx: &LateContext<'_>, var_arg: &hir::Expr<'_>) -> String {
 +    match &var_arg.kind {
 +        hir::ExprKind::Field(_, _) => snippet(cx, var_arg.span, "_").replace('.', "_"),
 +        hir::ExprKind::Path(_) => format!("_{}", snippet(cx, var_arg.span, "")),
 +        _ => "a".to_string(),
 +    }
 +}
 +
 +#[must_use]
 +fn suggestion_msg(function_type: &str, map_type: &str) -> String {
 +    format!("called `map(f)` on an `{map_type}` value where `f` is a {function_type} that returns the unit type `()`")
 +}
 +
 +fn lint_map_unit_fn(
 +    cx: &LateContext<'_>,
 +    stmt: &hir::Stmt<'_>,
 +    expr: &hir::Expr<'_>,
 +    map_args: (&hir::Expr<'_>, &[hir::Expr<'_>]),
 +) {
 +    let var_arg = &map_args.0;
 +
 +    let (map_type, variant, lint) = if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Option) {
 +        ("Option", "Some", OPTION_MAP_UNIT_FN)
 +    } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(var_arg), sym::Result) {
 +        ("Result", "Ok", RESULT_MAP_UNIT_FN)
 +    } else {
 +        return;
 +    };
 +    let fn_arg = &map_args.1[0];
 +
 +    if is_unit_function(cx, fn_arg) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        let msg = suggestion_msg("function", map_type);
 +        let suggestion = format!(
 +            "if let {0}({binding}) = {1} {{ {2}({binding}) }}",
 +            variant,
 +            snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
 +            snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability),
 +            binding = let_binding_name(cx, var_arg)
 +        );
 +
 +        span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
 +            diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
 +        });
 +    } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
 +        let msg = suggestion_msg("closure", map_type);
 +
 +        span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
 +            if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let suggestion = format!(
 +                    "if let {0}({1}) = {2} {{ {3} }}",
 +                    variant,
 +                    snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability),
 +                    snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
 +                    snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0,
 +                );
 +                diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
 +            } else {
 +                let suggestion = format!(
 +                    "if let {0}({1}) = {2} {{ ... }}",
 +                    variant,
 +                    snippet(cx, binding.pat.span, "_"),
 +                    snippet(cx, var_arg.span, "_"),
 +                );
 +                diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::HasPlaceholders);
 +            }
 +        });
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MapUnit {
 +    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
 +        if stmt.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let hir::StmtKind::Semi(expr) = stmt.kind {
 +            if let Some(arglists) = method_chain_args(expr, &["map"]) {
 +                lint_map_unit_fn(cx, stmt, expr, arglists[0]);
 +            }
 +        }
 +    }
 +}
index 2472acb6f6e8b6f974fcf898010e45140bc021ff,0000000000000000000000000000000000000000..d18c92caba2a6f0b3eaa65b5dcee86cde686d021
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,45 @@@
- use rustc_hir::{ExprKind, Local, MatchSource, PatKind, QPath};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{path_to_local_id, peel_blocks, strip_pat_refs};
 +use rustc_errors::Applicability;
-         if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
++use rustc_hir::{ByRef, ExprKind, Local, MatchSource, PatKind, QPath};
 +use rustc_lint::LateContext;
 +
 +use super::INFALLIBLE_DESTRUCTURING_MATCH;
 +
 +pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
 +    if_chain! {
 +        if !local.span.from_expansion();
 +        if let Some(expr) = local.init;
 +        if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
 +        if arms.len() == 1 && arms[0].guard.is_none();
 +        if let PatKind::TupleStruct(
 +            QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
 +        if args.len() == 1;
-                     "let {}({}) = {};",
++        if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind;
 +        let body = peel_blocks(arms[0].body);
 +        if path_to_local_id(body, arg);
 +
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                INFALLIBLE_DESTRUCTURING_MATCH,
 +                local.span,
 +                "you seem to be trying to use `match` to destructure a single infallible pattern. \
 +                Consider using `let`",
 +                "try this",
 +                format!(
++                    "let {}({}{}) = {};",
 +                    snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
++                    if binding.0 == ByRef::Yes { "ref " } else { "" },
 +                    snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
 +                    snippet_with_applicability(cx, target.span, "..", &mut applicability),
 +                ),
 +                applicability,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
index 66ba1f6f9c55007771d75d415ff517bea32bdbe5,0000000000000000000000000000000000000000..d521a529e0d6463f12486033c3351d9218a67817
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,153 @@@
- fn is_some_expr<'tcx>(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &'tcx Expr<'_>) -> bool {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::visitors::contains_unsafe_block;
 +use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
 +
 +use rustc_hir::LangItem::OptionSome;
 +use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_span::{sym, SyntaxContext};
 +
 +use super::manual_utils::{check_with, SomeExpr};
 +use super::MANUAL_FILTER;
 +
 +// Function called on the <expr> of `[&+]Some((ref | ref mut) x) => <expr>`
 +// Need to check if it's of the form `<expr>=if <cond> {<then_expr>} else {<else_expr>}`
 +// AND that only one `then/else_expr` resolves to `Some(x)` while the other resolves to `None`
 +// return the `cond` expression if so.
 +fn get_cond_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &Pat<'_>,
 +    expr: &'tcx Expr<'_>,
 +    ctxt: SyntaxContext,
 +) -> Option<SomeExpr<'tcx>> {
 +    if_chain! {
 +        if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr);
 +        if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind;
 +        if let PatKind::Binding(_,target, ..) = pat.kind;
 +        if let (then_visitor, else_visitor)
 +            = (is_some_expr(cx, target, ctxt, then_expr),
 +                is_some_expr(cx, target, ctxt, else_expr));
 +        if then_visitor != else_visitor; // check that one expr resolves to `Some(x)`, the other to `None`
 +        then {
 +            return Some(SomeExpr {
 +                    expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()),
 +                    needs_unsafe_block: contains_unsafe_block(cx, expr),
 +                    needs_negated: !then_visitor // if the `then_expr` resolves to `None`, need to negate the cond
 +                })
 +            }
 +    };
 +    None
 +}
 +
 +fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
 +    // we don't want to use `peel_blocks` here because we don't care if the block is unsafe, it's
 +    // checked by `contains_unsafe_block`
 +    if let ExprKind::Block(block, None) = expr.kind {
 +        if block.stmts.is_empty() {
 +            return block.expr;
 +        }
 +    };
 +    None
 +}
 +
 +fn peels_blocks_incl_unsafe<'a>(expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    peels_blocks_incl_unsafe_opt(expr).unwrap_or(expr)
 +}
 +
 +// function called for each <expr> expression:
 +// Some(x) => if <cond> {
 +//    <expr>
 +// } else {
 +//    <expr>
 +// }
 +// Returns true if <expr> resolves to `Some(x)`, `false` otherwise
++fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: &Expr<'_>) -> bool {
 +    if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) {
 +        // there can be not statements in the block as they would be removed when switching to `.filter`
 +        if let ExprKind::Call(callee, [arg]) = inner_expr.kind {
 +            return ctxt == expr.span.ctxt()
 +                && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome)
 +                && path_to_local_id(arg, target);
 +        }
 +    };
 +    false
 +}
 +
 +// given the closure: `|<pattern>| <expr>`
 +// returns `|&<pattern>| <expr>`
 +fn add_ampersand_if_copy(body_str: String, has_copy_trait: bool) -> String {
 +    if has_copy_trait {
 +        let mut with_ampersand = body_str;
 +        with_ampersand.insert(1, '&');
 +        with_ampersand
 +    } else {
 +        body_str
 +    }
 +}
 +
 +pub(super) fn check_match<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'_>],
 +    expr: &'tcx Expr<'_>,
 +) {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    if is_type_diagnostic_item(cx, ty, sym::Option)
 +    && let [first_arm, second_arm] = arms
 +    && first_arm.guard.is_none()
 +    && second_arm.guard.is_none()
 +         {
 +            check(cx, expr, scrutinee, first_arm.pat, first_arm.body, Some(second_arm.pat), second_arm.body);
 +        }
 +}
 +
 +pub(super) fn check_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &'tcx Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +    then_expr: &'tcx Expr<'_>,
 +    else_expr: &'tcx Expr<'_>,
 +) {
 +    check(cx, expr, let_expr, let_pat, then_expr, None, else_expr);
 +}
 +
 +fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    scrutinee: &'tcx Expr<'_>,
 +    then_pat: &'tcx Pat<'_>,
 +    then_body: &'tcx Expr<'_>,
 +    else_pat: Option<&'tcx Pat<'_>>,
 +    else_body: &'tcx Expr<'_>,
 +) {
 +    if let Some(sugg_info) = check_with(
 +        cx,
 +        expr,
 +        scrutinee,
 +        then_pat,
 +        then_body,
 +        else_pat,
 +        else_body,
 +        get_cond_expr,
 +    ) {
 +        let body_str = add_ampersand_if_copy(sugg_info.body_str, sugg_info.scrutinee_impl_copy);
 +        span_lint_and_sugg(
 +            cx,
 +            MANUAL_FILTER,
 +            expr.span,
 +            "manual implementation of `Option::filter`",
 +            "try this",
 +            if sugg_info.needs_brackets {
 +                format!(
 +                    "{{ {}{}.filter({body_str}) }}",
 +                    sugg_info.scrutinee_str, sugg_info.as_ref_str
 +                )
 +            } else {
 +                format!("{}{}.filter({body_str})", sugg_info.scrutinee_str, sugg_info.as_ref_str)
 +            },
 +            sugg_info.app,
 +        );
 +    }
 +}
index 85269e533a066861ad3454d82d0a67185ee3277c,0000000000000000000000000000000000000000..f587c69f7302d41ab7f10d3040e8d37ef9209dfd
mode 100644,000000..100644
--- /dev/null
@@@ -1,399 -1,0 +1,399 @@@
- fn has_significant_drop_in_scrutinee<'tcx, 'a>(
-     cx: &'a LateContext<'tcx>,
 +use crate::FxHashSet;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::{indent_of, snippet};
 +use clippy_utils::{get_attr, is_lint_allowed};
 +use rustc_errors::{Applicability, Diagnostic};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{Ty, TypeAndMut};
 +use rustc_span::Span;
 +
 +use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'_>],
 +    source: MatchSource,
 +) {
 +    if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
 +        return;
 +    }
 +
 +    if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
 +        for found in suggestions {
 +            span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
 +                set_diagnostic(diag, cx, expr, found);
 +                let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
 +                diag.span_label(s, "temporary lives until here");
 +                for span in has_significant_drop_in_arms(cx, arms) {
 +                    diag.span_label(span, "another value with significant `Drop` created here");
 +                }
 +                diag.note("this might lead to deadlocks or other unexpected behavior");
 +            });
 +        }
 +    }
 +}
 +
 +fn set_diagnostic<'tcx>(diag: &mut Diagnostic, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
 +    if found.lint_suggestion == LintSuggestion::MoveAndClone {
 +        // If our suggestion is to move and clone, then we want to leave it to the user to
 +        // decide how to address this lint, since it may be that cloning is inappropriate.
 +        // Therefore, we won't to emit a suggestion.
 +        return;
 +    }
 +
 +    let original = snippet(cx, found.found_span, "..");
 +    let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
 +
 +    let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy {
 +        format!("let value = *{original};\n{trailing_indent}")
 +    } else if found.is_unit_return_val {
 +        // If the return value of the expression to be moved is unit, then we don't need to
 +        // capture the result in a temporary -- we can just replace it completely with `()`.
 +        format!("{original};\n{trailing_indent}")
 +    } else {
 +        format!("let value = {original};\n{trailing_indent}")
 +    };
 +
 +    let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly {
 +        "try moving the temporary above the match"
 +    } else {
 +        "try moving the temporary above the match and create a copy"
 +    };
 +
 +    let scrutinee_replacement = if found.is_unit_return_val {
 +        "()".to_owned()
 +    } else {
 +        "value".to_owned()
 +    };
 +
 +    diag.multipart_suggestion(
 +        suggestion_message,
 +        vec![
 +            (expr.span.shrink_to_lo(), replacement),
 +            (found.found_span, scrutinee_replacement),
 +        ],
 +        Applicability::MaybeIncorrect,
 +    );
 +}
 +
 +/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
 +/// may have a surprising lifetime.
-     /// However, if we are at the the end of the chain, we want to accept whatever is there. (The
++fn has_significant_drop_in_scrutinee<'tcx>(
++    cx: &LateContext<'tcx>,
 +    scrutinee: &'tcx Expr<'tcx>,
 +    source: MatchSource,
 +) -> Option<(Vec<FoundSigDrop>, &'static str)> {
 +    let mut helper = SigDropHelper::new(cx);
 +    let scrutinee = match (source, &scrutinee.kind) {
 +        (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
 +        _ => scrutinee,
 +    };
 +    helper.find_sig_drop(scrutinee).map(|drops| {
 +        let message = if source == MatchSource::Normal {
 +            "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
 +        } else {
 +            "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
 +        };
 +        (drops, message)
 +    })
 +}
 +
 +struct SigDropChecker<'a, 'tcx> {
 +    seen_types: FxHashSet<Ty<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> SigDropChecker<'a, 'tcx> {
 +        SigDropChecker {
 +            seen_types: FxHashSet::default(),
 +            cx,
 +        }
 +    }
 +
 +    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
 +        self.cx.typeck_results().expr_ty(ex)
 +    }
 +
 +    fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
 +        !self.seen_types.insert(ty)
 +    }
 +
 +    fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +        if let Some(adt) = ty.ty_adt_def() {
 +            if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
 +                return true;
 +            }
 +        }
 +
 +        match ty.kind() {
 +            rustc_middle::ty::Adt(a, b) => {
 +                for f in a.all_fields() {
 +                    let ty = f.ty(cx.tcx, b);
 +                    if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
 +                        return true;
 +                    }
 +                }
 +
 +                for generic_arg in b.iter() {
 +                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
 +                        if self.has_sig_drop_attr(cx, ty) {
 +                            return true;
 +                        }
 +                    }
 +                }
 +                false
 +            },
 +            rustc_middle::ty::Array(ty, _)
 +            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
 +            | rustc_middle::ty::Ref(_, ty, _)
 +            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +struct SigDropHelper<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    is_chain_end: bool,
 +    has_significant_drop: bool,
 +    current_sig_drop: Option<FoundSigDrop>,
 +    sig_drop_spans: Option<Vec<FoundSigDrop>>,
 +    special_handling_for_binary_op: bool,
 +    sig_drop_checker: SigDropChecker<'a, 'tcx>,
 +}
 +
 +#[expect(clippy::enum_variant_names)]
 +#[derive(Debug, PartialEq, Eq, Clone, Copy)]
 +enum LintSuggestion {
 +    MoveOnly,
 +    MoveAndDerefToCopy,
 +    MoveAndClone,
 +}
 +
 +#[derive(Clone, Copy)]
 +struct FoundSigDrop {
 +    found_span: Span,
 +    is_unit_return_val: bool,
 +    lint_suggestion: LintSuggestion,
 +}
 +
 +impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> {
 +        SigDropHelper {
 +            cx,
 +            is_chain_end: true,
 +            has_significant_drop: false,
 +            current_sig_drop: None,
 +            sig_drop_spans: None,
 +            special_handling_for_binary_op: false,
 +            sig_drop_checker: SigDropChecker::new(cx),
 +        }
 +    }
 +
 +    fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option<Vec<FoundSigDrop>> {
 +        self.visit_expr(match_expr);
 +
 +        // If sig drop spans is empty but we found a significant drop, it means that we didn't find
 +        // a type that was trivially copyable as we moved up the chain after finding a significant
 +        // drop, so move the entire scrutinee.
 +        if self.has_significant_drop && self.sig_drop_spans.is_none() {
 +            self.try_setting_current_suggestion(match_expr, true);
 +            self.move_current_suggestion();
 +        }
 +
 +        self.sig_drop_spans.take()
 +    }
 +
 +    fn replace_current_sig_drop(
 +        &mut self,
 +        found_span: Span,
 +        is_unit_return_val: bool,
 +        lint_suggestion: LintSuggestion,
 +    ) {
 +        self.current_sig_drop.replace(FoundSigDrop {
 +            found_span,
 +            is_unit_return_val,
 +            lint_suggestion,
 +        });
 +    }
 +
 +    /// This will try to set the current suggestion (so it can be moved into the suggestions vec
 +    /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
 +    /// an opportunity to look for another type in the chain that will be trivially copyable.
- fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
++    /// However, if we are at the end of the chain, we want to accept whatever is there. (The
 +    /// suggestion won't actually be output, but the diagnostic message will be output, so the user
 +    /// can determine the best way to handle the lint.)
 +    fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) {
 +        if self.current_sig_drop.is_some() {
 +            return;
 +        }
 +        let ty = self.sig_drop_checker.get_type(expr);
 +        if ty.is_ref() {
 +            // We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
 +            // but let's avoid any chance of an ICE
 +            if let Some(TypeAndMut { ty, .. }) = ty.builtin_deref(true) {
 +                if ty.is_trivially_pure_clone_copy() {
 +                    self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
 +                } else if allow_move_and_clone {
 +                    self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
 +                }
 +            }
 +        } else if ty.is_trivially_pure_clone_copy() {
 +            self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
 +        } else if allow_move_and_clone {
 +            self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
 +        }
 +    }
 +
 +    fn move_current_suggestion(&mut self) {
 +        if let Some(current) = self.current_sig_drop.take() {
 +            self.sig_drop_spans.get_or_insert_with(Vec::new).push(current);
 +        }
 +    }
 +
 +    fn visit_exprs_for_binary_ops(
 +        &mut self,
 +        left: &'tcx Expr<'_>,
 +        right: &'tcx Expr<'_>,
 +        is_unit_return_val: bool,
 +        span: Span,
 +    ) {
 +        self.special_handling_for_binary_op = true;
 +        self.visit_expr(left);
 +        self.visit_expr(right);
 +
 +        // If either side had a significant drop, suggest moving the entire scrutinee to avoid
 +        // unnecessary copies and to simplify cases where both sides have significant drops.
 +        if self.has_significant_drop {
 +            self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
 +        }
 +
 +        self.special_handling_for_binary_op = false;
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
 +        if !self.is_chain_end
 +            && self
 +                .sig_drop_checker
 +                .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
 +        {
 +            self.has_significant_drop = true;
 +            return;
 +        }
 +        self.is_chain_end = false;
 +
 +        match ex.kind {
 +            ExprKind::MethodCall(_, expr, ..) => {
 +                self.visit_expr(expr);
 +            }
 +            ExprKind::Binary(_, left, right) => {
 +                self.visit_exprs_for_binary_ops(left, right, false, ex.span);
 +            }
 +            ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => {
 +                self.visit_exprs_for_binary_ops(left, right, true, ex.span);
 +            }
 +            ExprKind::Tup(exprs) => {
 +                for expr in exprs {
 +                    self.visit_expr(expr);
 +                    if self.has_significant_drop {
 +                        // We may have not have set current_sig_drop if all the suggestions were
 +                        // MoveAndClone, so add this tuple item's full expression in that case.
 +                        if self.current_sig_drop.is_none() {
 +                            self.try_setting_current_suggestion(expr, true);
 +                        }
 +
 +                        // Now we are guaranteed to have something, so add it to the final vec.
 +                        self.move_current_suggestion();
 +                    }
 +                    // Reset `has_significant_drop` after each tuple expression so we can look for
 +                    // additional cases.
 +                    self.has_significant_drop = false;
 +                }
 +                if self.sig_drop_spans.is_some() {
 +                    self.has_significant_drop = true;
 +                }
 +            }
 +            ExprKind::Box(..) |
 +            ExprKind::Array(..) |
 +            ExprKind::Call(..) |
 +            ExprKind::Unary(..) |
 +            ExprKind::If(..) |
 +            ExprKind::Match(..) |
 +            ExprKind::Field(..) |
 +            ExprKind::Index(..) |
 +            ExprKind::Ret(..) |
 +            ExprKind::Repeat(..) |
 +            ExprKind::Yield(..) => walk_expr(self, ex),
 +            ExprKind::AddrOf(_, _, _) |
 +            ExprKind::Block(_, _) |
 +            ExprKind::Break(_, _) |
 +            ExprKind::Cast(_, _) |
 +            // Don't want to check the closure itself, only invocation, which is covered by MethodCall
 +            ExprKind::Closure { .. } |
 +            ExprKind::ConstBlock(_) |
 +            ExprKind::Continue(_) |
 +            ExprKind::DropTemps(_) |
 +            ExprKind::Err |
 +            ExprKind::InlineAsm(_) |
 +            ExprKind::Let(_) |
 +            ExprKind::Lit(_) |
 +            ExprKind::Loop(_, _, _, _) |
 +            ExprKind::Path(_) |
 +            ExprKind::Struct(_, _, _) |
 +            ExprKind::Type(_, _) => {
 +                return;
 +            }
 +        }
 +
 +        // Once a significant temporary has been found, we need to go back up at least 1 level to
 +        // find the span to extract for replacement, so the temporary gets dropped. However, for
 +        // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to
 +        // simplify cases where both sides have significant drops.
 +        if self.has_significant_drop && !self.special_handling_for_binary_op {
 +            self.try_setting_current_suggestion(ex, false);
 +        }
 +    }
 +}
 +
 +struct ArmSigDropHelper<'a, 'tcx> {
 +    sig_drop_checker: SigDropChecker<'a, 'tcx>,
 +    found_sig_drop_spans: FxHashSet<Span>,
 +}
 +
 +impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
 +        ArmSigDropHelper {
 +            sig_drop_checker: SigDropChecker::new(cx),
 +            found_sig_drop_spans: FxHashSet::<Span>::default(),
 +        }
 +    }
 +}
 +
++fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
 +    let mut helper = ArmSigDropHelper::new(cx);
 +    for arm in arms {
 +        helper.visit_expr(arm.body);
 +    }
 +    helper.found_sig_drop_spans
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
 +        if self
 +            .sig_drop_checker
 +            .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
 +        {
 +            self.found_sig_drop_spans.insert(ex.span);
 +            return;
 +        }
 +        walk_expr(self, ex);
 +    }
 +}
index e5a15b2e1a1d2b454a8a5f488f6dfa1f7aa654fb,0000000000000000000000000000000000000000..19b49c44d5704d316e09239e40ea91141c906b61
mode 100644,000000..100644
--- /dev/null
@@@ -1,243 -1,0 +1,243 @@@
- fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{expr_block, snippet};
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
 +use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
 +use core::cmp::max;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::sym;
 +
 +use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
 +
 +#[rustfmt::skip]
 +pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        if expr.span.from_expansion() {
 +            // Don't lint match expressions present in
 +            // macro_rules! block
 +            return;
 +        }
 +        if let PatKind::Or(..) = arms[0].pat.kind {
 +            // don't lint for or patterns for now, this makes
 +            // the lint noisy in unnecessary situations
 +            return;
 +        }
 +        let els = arms[1].body;
 +        let els = if is_unit_expr(peel_blocks(els)) {
 +            None
 +        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
 +            if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
 +                // single statement/expr "else" block, don't lint
 +                return;
 +            }
 +            // block with 2+ statements or 1 expr and 1+ statement
 +            Some(els)
 +        } else {
 +            // not a block, don't lint
 +            return;
 +        };
 +
 +        let ty = cx.typeck_results().expr_ty(ex);
 +        if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
 +            check_single_pattern(cx, ex, arms, expr, els);
 +            check_opt_like(cx, ex, arms, expr, ty, els);
 +        }
 +    }
 +}
 +
 +fn check_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    if is_wild(arms[1].pat) {
 +        report_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +fn report_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
 +    let els_str = els.map_or(String::new(), |els| {
 +        format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
 +    });
 +
 +    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
 +    let (msg, sugg) = if_chain! {
 +        if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
 +        let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
 +        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
 +        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
 +        if ty.is_integral() || ty.is_char() || ty.is_str()
 +            || (implements_trait(cx, ty, spe_trait_id, &[])
 +                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
 +        then {
 +            // scrutinee derives PartialEq and the pattern is a constant.
 +            let pat_ref_count = match pat.kind {
 +                // string literals are already a reference.
 +                PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
 +                _ => pat_ref_count,
 +            };
 +            // References are only implicitly added to the pattern, so no overflow here.
 +            // e.g. will work: match &Some(_) { Some(_) => () }
 +            // will not: match Some(_) { &Some(_) => () }
 +            let ref_count_diff = ty_ref_count - pat_ref_count;
 +
 +            // Try to remove address of expressions first.
 +            let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
 +            let ref_count_diff = ref_count_diff - removed;
 +
 +            let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
 +            let sugg = format!(
 +                "if {} == {}{} {}{els_str}",
 +                snippet(cx, ex.span, ".."),
 +                // PartialEq for different reference counts may not exist.
 +                "&".repeat(ref_count_diff),
 +                snippet(cx, arms[0].pat.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +            );
 +            (msg, sugg)
 +        } else {
 +            let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
 +            let sugg = format!(
 +                "if let {} = {} {}{els_str}",
 +                snippet(cx, arms[0].pat.span, ".."),
 +                snippet(cx, ex.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +            );
 +            (msg, sugg)
 +        }
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        lint,
 +        expr.span,
 +        msg,
 +        "try this",
 +        sugg,
 +        Applicability::HasPlaceholders,
 +    );
 +}
 +
 +fn check_opt_like<'a>(
 +    cx: &LateContext<'a>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    ty: Ty<'a>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    // We don't want to lint if the second arm contains an enum which could
 +    // have more variants in the future.
 +    if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
 +        report_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +/// Returns `true` if all of the types in the pattern are enums which we know
 +/// won't be expanded in the future
 +fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
 +    let mut paths_and_types = Vec::new();
 +    collect_pat_paths(&mut paths_and_types, cx, pat, ty);
 +    paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
 +}
 +
 +/// Returns `true` if the given type is an enum we know won't be expanded in the future
++fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    // list of candidate `Enum`s we know will never get any more members
 +    let candidates = [sym::Cow, sym::Option, sym::Result];
 +
 +    for candidate_ty in candidates {
 +        if is_type_diagnostic_item(cx, ty, candidate_ty) {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Collects types from the given pattern
 +fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
 +    match pat.kind {
 +        PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
 +            let p_ty = cx.typeck_results().pat_ty(p);
 +            collect_pat_paths(acc, cx, p, p_ty);
 +        }),
 +        PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::NONE, .., None) | PatKind::Path(_) => {
 +            acc.push(ty);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +/// Returns true if the given arm of pattern matching contains wildcard patterns.
 +fn contains_only_wilds(pat: &Pat<'_>) -> bool {
 +    match pat.kind {
 +        PatKind::Wild => true,
 +        PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds),
 +        _ => false,
 +    }
 +}
 +
 +/// Returns true if the given patterns forms only exhaustive matches that don't contain enum
 +/// patterns without a wildcard.
 +fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +    match (&left.kind, &right.kind) {
 +        (PatKind::Wild, _) | (_, PatKind::Wild) => true,
 +        (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
 +            // We don't actually know the position and the presence of the `..` (dotdot) operator
 +            // in the arms, so we need to evaluate the correct offsets here in order to iterate in
 +            // both arms at the same time.
 +            let left_pos = left_pos.as_opt_usize();
 +            let right_pos = right_pos.as_opt_usize();
 +            let len = max(
 +                left_in.len() + usize::from(left_pos.is_some()),
 +                right_in.len() + usize::from(right_pos.is_some()),
 +            );
 +            let mut left_pos = left_pos.unwrap_or(usize::MAX);
 +            let mut right_pos = right_pos.unwrap_or(usize::MAX);
 +            let mut left_dot_space = 0;
 +            let mut right_dot_space = 0;
 +            for i in 0..len {
 +                let mut found_dotdot = false;
 +                if i == left_pos {
 +                    left_dot_space += 1;
 +                    if left_dot_space < len - left_in.len() {
 +                        left_pos += 1;
 +                    }
 +                    found_dotdot = true;
 +                }
 +                if i == right_pos {
 +                    right_dot_space += 1;
 +                    if right_dot_space < len - right_in.len() {
 +                        right_pos += 1;
 +                    }
 +                    found_dotdot = true;
 +                }
 +                if found_dotdot {
 +                    continue;
 +                }
 +                if !contains_only_wilds(&left_in[i - left_dot_space])
 +                    && !contains_only_wilds(&right_in[i - right_dot_space])
 +                {
 +                    return false;
 +                }
 +            }
 +            true
 +        },
 +        (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
 +        (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
 +            pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
 +        },
 +        _ => false,
 +    }
 +}
index 7e808760663a1a4b370bd33daeebedca134fc1f9,0000000000000000000000000000000000000000..27a05337a290f2c6cce4ee0635e44672ef71e5df
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,43 @@@
- pub(super) fn check<'tcx>(
-     cx: &LateContext<'tcx>,
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::method_chain_args;
 +use clippy_utils::source::snippet_with_applicability;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_lint::Lint;
 +
 +/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
++pub(super) fn check(
++    cx: &LateContext<'_>,
 +    info: &crate::methods::BinaryExprInfo<'_>,
 +    chain_methods: &[&str],
 +    lint: &'static Lint,
 +    suggest: &str,
 +) -> bool {
 +    if_chain! {
 +        if let Some(args) = method_chain_args(info.chain, chain_methods);
 +        if let hir::ExprKind::Lit(ref lit) = info.other.kind;
 +        if let ast::LitKind::Char(c) = lit.node;
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                lint,
 +                info.expr.span,
 +                &format!("you should use the `{suggest}` method"),
 +                "like this",
 +                format!("{}{}.{suggest}('{}')",
 +                        if info.eq { "" } else { "!" },
 +                        snippet_with_applicability(cx, args[0].0.span, "..", &mut applicability),
 +                        c.escape_default()),
 +                applicability,
 +            );
 +
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +}
index 07bbc5ca1bf46cdf843966ea89ce87b3378164e9,0000000000000000000000000000000000000000..2efff4c3c5497b9d361b3b375b04c29e1ecdc331
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +use crate::methods::chars_cmp;
 +use rustc_lint::LateContext;
 +
 +use super::CHARS_LAST_CMP;
 +
 +/// Checks for the `CHARS_LAST_CMP` lint.
++pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +    if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
 +        true
 +    } else {
 +        chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
 +    }
 +}
index c29ee0ec8c8ca5720430947f701cdc6ed6c2e7e9,0000000000000000000000000000000000000000..5b8713f7d79035d92803413ae6b509c98d5f4fe9
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +use crate::methods::chars_cmp_with_unwrap;
 +use rustc_lint::LateContext;
 +
 +use super::CHARS_LAST_CMP;
 +
 +/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
++pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +    if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
 +        true
 +    } else {
 +        chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
 +    }
 +}
index a6701d8830e77d099f17bcc7192f6f6cac566190,0000000000000000000000000000000000000000..b631fecab9729e4de4c6ed853aa9928ed1eb175a
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,8 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +use rustc_lint::LateContext;
 +
 +use super::CHARS_NEXT_CMP;
 +
 +/// Checks for the `CHARS_NEXT_CMP` lint.
++pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +    crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
 +}
index 28ede28e9358bee56a41817d121950bf3e0cd2d6,0000000000000000000000000000000000000000..caf21d3ff3bcb8a055ed5072be1c6fba88ca579a
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,8 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +use rustc_lint::LateContext;
 +
 +use super::CHARS_NEXT_CMP;
 +
 +/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
++pub(super) fn check(cx: &LateContext<'_>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
 +    crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
 +}
index 501646863fe156c4fab56cc8452f2e23cf7e65f9,0000000000000000000000000000000000000000..ac61b4377885b8cfd2a179c901ee844be0cd8aef
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,96 @@@
-             && let Some(("replace", _, [current_from, current_to], _)) = method_call(parent)
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::visitors::for_each_expr;
 +use clippy_utils::{eq_expr_value, get_parent_expr};
 +use core::ops::ControlFlow;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use std::collections::VecDeque;
 +
 +use super::method_call;
 +use super::COLLAPSIBLE_STR_REPLACE;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'tcx>,
 +    from: &'tcx hir::Expr<'tcx>,
 +    to: &'tcx hir::Expr<'tcx>,
 +) {
 +    let replace_methods = collect_replace_calls(cx, expr, to);
 +    if replace_methods.methods.len() > 1 {
 +        let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
 +        // If the parent node's `to` argument is the same as the `to` argument
 +        // of the last replace call in the current chain, don't lint as it was already linted
 +        if let Some(parent) = get_parent_expr(cx, expr)
-         if let Some(("replace", _, [from, to], _)) = method_call(e) {
++            && let Some(("replace", _, [current_from, current_to], _, _)) = method_call(parent)
 +            && eq_expr_value(cx, to, current_to)
 +            && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
 +        {
 +            return;
 +        }
 +
 +        check_consecutive_replace_calls(cx, expr, &replace_methods, to);
 +    }
 +}
 +
 +struct ReplaceMethods<'tcx> {
 +    methods: VecDeque<&'tcx hir::Expr<'tcx>>,
 +    from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
 +}
 +
 +fn collect_replace_calls<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'tcx>,
 +    to_arg: &'tcx hir::Expr<'tcx>,
 +) -> ReplaceMethods<'tcx> {
 +    let mut methods = VecDeque::new();
 +    let mut from_args = VecDeque::new();
 +
 +    let _: Option<()> = for_each_expr(expr, |e| {
-     if let Some((_, _, [..], span_lo)) = method_call(earliest_replace_call) {
++        if let Some(("replace", _, [from, to], _, _)) = method_call(e) {
 +            if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
 +                methods.push_front(e);
 +                from_args.push_front(from);
 +                ControlFlow::Continue(())
 +            } else {
 +                ControlFlow::BREAK
 +            }
 +        } else {
 +            ControlFlow::Continue(())
 +        }
 +    });
 +
 +    ReplaceMethods { methods, from_args }
 +}
 +
 +/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
 +fn check_consecutive_replace_calls<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'tcx>,
 +    replace_methods: &ReplaceMethods<'tcx>,
 +    to_arg: &'tcx hir::Expr<'tcx>,
 +) {
 +    let from_args = &replace_methods.from_args;
 +    let from_arg_reprs: Vec<String> = from_args
 +        .iter()
 +        .map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
 +        .collect();
 +    let app = Applicability::MachineApplicable;
 +    let earliest_replace_call = replace_methods.methods.front().unwrap();
++    if let Some((_, _, [..], span_lo, _)) = method_call(earliest_replace_call) {
 +        span_lint_and_sugg(
 +            cx,
 +            COLLAPSIBLE_STR_REPLACE,
 +            expr.span.with_lo(span_lo.lo()),
 +            "used consecutive `str::replace` call",
 +            "replace with",
 +            format!(
 +                "replace([{}], {})",
 +                from_arg_reprs.join(", "),
 +                snippet(cx, to_arg.span, ".."),
 +            ),
 +            app,
 +        );
 +    }
 +}
index d59fefa1ddc0ebf68104498532f8da1f1c8c0e26,0000000000000000000000000000000000000000..cce8f797e98c6a1c4e948caa3c31d26137f75c5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,44 @@@
- use clippy_utils::is_in_test_function;
 +use clippy_utils::diagnostics::span_lint_and_help;
-         Some((EXPECT_USED, "an Option", "None", ""))
++use clippy_utils::is_in_cfg_test;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::EXPECT_USED;
 +
 +/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    recv: &hir::Expr<'_>,
 +    is_err: bool,
 +    allow_expect_in_tests: bool,
 +) {
 +    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 +
 +    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
-         Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
++        Some((EXPECT_USED, "an `Option`", "None", ""))
 +    } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-     if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
++        Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
 +    } else {
 +        None
 +    };
 +
 +    let method = if is_err { "expect_err" } else { "expect" };
 +
-             &format!("used `{method}()` on `{kind}` value"),
++    if allow_expect_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) {
 +        return;
 +    }
 +
 +    if let Some((lint, kind, none_value, none_prefix)) = mess {
 +        span_lint_and_help(
 +            cx,
 +            lint,
 +            expr.span,
++            &format!("used `{method}()` on {kind} value"),
 +            None,
 +            &format!("if this value is {none_prefix}`{none_value}`, it will panic"),
 +        );
 +    }
 +}
index 9719b2f1c5125ff78f97934aeed8078405aa8878,0000000000000000000000000000000000000000..f888c58a72de93a605f3193e5dbfc5907d86ef24
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,197 @@@
- fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{indent_of, reindent_multiline, snippet};
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_trait_method, path_to_local_id, peel_blocks, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::adjustment::Adjust;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{sym, Symbol};
 +use std::borrow::Cow;
 +
 +use super::MANUAL_FILTER_MAP;
 +use super::MANUAL_FIND_MAP;
 +use super::OPTION_FILTER_MAP;
 +
- fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool {
++fn is_method(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol) -> bool {
 +    match &expr.kind {
 +        hir::ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
 +        hir::ExprKind::Path(QPath::Resolved(_, segments)) => {
 +            segments.segments.last().unwrap().ident.name == method_name
 +        },
 +        hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
 +            let body = cx.tcx.hir().body(body);
 +            let closure_expr = peel_blocks(body.value);
 +            let arg_id = body.params[0].pat.hir_id;
 +            match closure_expr.kind {
 +                hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {
 +                    if_chain! {
 +                    if ident.name == method_name;
 +                    if let hir::ExprKind::Path(path) = &receiver.kind;
 +                    if let Res::Local(ref local) = cx.qpath_res(path, receiver.hir_id);
 +                    then {
 +                        return arg_id == *local
 +                    }
 +                    }
 +                    false
 +                },
 +                _ => false,
 +            }
 +        },
 +        _ => false,
 +    }
 +}
 +
- pub(super) fn check<'tcx>(
-     cx: &LateContext<'tcx>,
++fn is_option_filter_map(cx: &LateContext<'_>, filter_arg: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) -> bool {
 +    is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
 +}
 +
 +/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
 +fn is_filter_some_map_unwrap(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    filter_recv: &hir::Expr<'_>,
 +    filter_arg: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +) -> bool {
 +    let iterator = is_trait_method(cx, expr, sym::Iterator);
 +    let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option);
 +
 +    (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg)
 +}
 +
 +/// lint use of `filter().map()` or `find().map()` for `Iterators`
 +#[allow(clippy::too_many_arguments)]
++pub(super) fn check(
++    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    filter_recv: &hir::Expr<'_>,
 +    filter_arg: &hir::Expr<'_>,
 +    filter_span: Span,
 +    map_recv: &hir::Expr<'_>,
 +    map_arg: &hir::Expr<'_>,
 +    map_span: Span,
 +    is_find: bool,
 +) {
 +    if is_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, map_arg) {
 +        span_lint_and_sugg(
 +            cx,
 +            OPTION_FILTER_MAP,
 +            filter_span.with_hi(expr.span.hi()),
 +            "`filter` for `Some` followed by `unwrap`",
 +            "consider using `flatten` instead",
 +            reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
 +            Applicability::MachineApplicable,
 +        );
 +
 +        return;
 +    }
 +
 +    if_chain! {
 +            if is_trait_method(cx, map_recv, sym::Iterator);
 +
 +            // filter(|x| ...is_some())...
 +            if let ExprKind::Closure(&Closure { body: filter_body_id, .. }) = filter_arg.kind;
 +            let filter_body = cx.tcx.hir().body(filter_body_id);
 +            if let [filter_param] = filter_body.params;
 +            // optional ref pattern: `filter(|&x| ..)`
 +            let (filter_pat, is_filter_param_ref) = if let PatKind::Ref(ref_pat, _) = filter_param.pat.kind {
 +                (ref_pat, true)
 +            } else {
 +                (filter_param.pat, false)
 +            };
 +            // closure ends with is_some() or is_ok()
 +            if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
 +            if let ExprKind::MethodCall(path, filter_arg, [], _) = filter_body.value.kind;
 +            if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
 +            if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
 +                Some(false)
 +            } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            if path.ident.name.as_str() == if is_result { "is_ok" } else { "is_some" };
 +
 +            // ...map(|x| ...unwrap())
 +            if let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind;
 +            let map_body = cx.tcx.hir().body(map_body_id);
 +            if let [map_param] = map_body.params;
 +            if let PatKind::Binding(_, map_param_id, map_param_ident, None) = map_param.pat.kind;
 +            // closure ends with expect() or unwrap()
 +            if let ExprKind::MethodCall(seg, map_arg, ..) = map_body.value.kind;
 +            if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
 +
 +            // .filter(..).map(|y| f(y).copied().unwrap())
 +            //                     ~~~~
 +            let map_arg_peeled = match map_arg.kind {
 +                ExprKind::MethodCall(method, original_arg, [], _) if acceptable_methods(method) => {
 +                    original_arg
 +                },
 +                _ => map_arg,
 +            };
 +
 +            // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
 +            let simple_equal = path_to_local_id(filter_arg, filter_param_id)
 +                && path_to_local_id(map_arg_peeled, map_param_id);
 +
 +            let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
 +                // in `filter(|x| ..)`, replace `*x` with `x`
 +                let a_path = if_chain! {
 +                    if !is_filter_param_ref;
 +                    if let ExprKind::Unary(UnOp::Deref, expr_path) = a.kind;
 +                    then { expr_path } else { a }
 +                };
 +                // let the filter closure arg and the map closure arg be equal
 +                path_to_local_id(a_path, filter_param_id)
 +                    && path_to_local_id(b, map_param_id)
 +                    && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
 +            };
 +
 +            if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
 +            then {
 +                let span = filter_span.with_hi(expr.span.hi());
 +                let (filter_name, lint) = if is_find {
 +                    ("find", MANUAL_FIND_MAP)
 +                } else {
 +                    ("filter", MANUAL_FILTER_MAP)
 +                };
 +                let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
 +                let (to_opt, deref) = if is_result {
 +                    (".ok()", String::new())
 +                } else {
 +                    let derefs = cx.typeck_results()
 +                        .expr_adjustments(map_arg)
 +                        .iter()
 +                        .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
 +                        .count();
 +
 +                    ("", "*".repeat(derefs))
 +                };
 +                let sugg = format!(
 +                    "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
 +                    snippet(cx, map_arg.span, ".."),
 +                );
 +                span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
 +            }
 +    }
 +}
 +
 +fn acceptable_methods(method: &PathSegment<'_>) -> bool {
 +    let methods: [Symbol; 8] = [
 +        sym::clone,
 +        sym::as_ref,
 +        sym!(copied),
 +        sym!(cloned),
 +        sym!(as_deref),
 +        sym!(as_mut),
 +        sym!(as_deref_mut),
 +        sym!(to_owned),
 +    ];
 +
 +    methods.contains(&method.ident.name)
 +}
index 4f4f543e8a912abd079ddaf9b74724100376ce00,0000000000000000000000000000000000000000..d8c821bc9eeee1cfb6eadf374a8c6e718668f472
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -1,0 +1,72 @@@
- pub fn check<'tcx>(
-     cx: &LateContext<'tcx>,
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth};
 +use clippy_utils::{match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::{Symbol, sym};
 +
 +use super::INEFFICIENT_TO_STRING;
 +
 +/// Checks for the `INEFFICIENT_TO_STRING` lint
++pub fn check(
++    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &hir::Expr<'_>,
 +    args: &[hir::Expr<'_>],
 +) {
 +    if_chain! {
 +        if args.is_empty() && method_name == sym::to_string;
 +        if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
 +        if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
 +        let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver);
 +        let self_ty = substs.type_at(0);
 +        let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
 +        if deref_count >= 1;
 +        if specializes_tostring(cx, deref_self_ty);
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                INEFFICIENT_TO_STRING,
 +                expr.span,
 +                &format!("calling `to_string` on `{arg_ty}`"),
 +                |diag| {
 +                    diag.help(&format!(
 +                        "`{self_ty}` implements `ToString` through a slower blanket impl, but `{deref_self_ty}` has a fast specialization of `ToString`"
 +                    ));
 +                    let mut applicability = Applicability::MachineApplicable;
 +                    let arg_snippet = snippet_with_applicability(cx, receiver.span, "..", &mut applicability);
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "try dereferencing the receiver",
 +                        format!("({}{arg_snippet}).to_string()", "*".repeat(deref_count)),
 +                        applicability,
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +/// Returns whether `ty` specializes `ToString`.
 +/// Currently, these are `str`, `String`, and `Cow<'_, str>`.
 +fn specializes_tostring(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    if let ty::Str = ty.kind() {
 +        return true;
 +    }
 +
 +    if is_type_lang_item(cx, ty, hir::LangItem::String) {
 +        return true;
 +    }
 +
 +    if let ty::Adt(adt, substs) = ty.kind() {
 +        cx.tcx.is_diagnostic_item(sym::Cow, adt.did()) && substs.type_at(1).is_str()
 +    } else {
 +        false
 +    }
 +}
index 68d906c3ea3999823796a7d89d8c894e85703d0b,0000000000000000000000000000000000000000..c830958d5c80e58865bd1c93b9091fdbd5e5f239
mode 100644,000000..100644
--- /dev/null
@@@ -1,30 -1,0 +1,30 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_trait_method;
 +use clippy_utils::source::snippet_with_applicability;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::ITER_NTH_ZERO;
 +
++pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
 +    if_chain! {
 +        if is_trait_method(cx, expr, sym::Iterator);
 +        if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), arg);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                ITER_NTH_ZERO,
 +                expr.span,
 +                "called `.nth(0)` on a `std::iter::Iterator`, when `.next()` is equivalent",
 +                "try calling `.next()` instead of `.nth(0)`",
 +                format!("{}.next()", snippet_with_applicability(cx, recv.span, "..", &mut applicability)),
 +                applicability,
 +            );
 +        }
 +    }
 +}
index 4f73b3ec42247e56bacf4f6d9df9a1506095c6e7,0000000000000000000000000000000000000000..70abe4891d9857e8ccee90b8abfc8065a744b941
mode 100644,000000..100644
--- /dev/null
@@@ -1,92 -1,0 +1,92 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{get_expr_use_or_unification_node, is_no_std_crate, is_res_lang_ctor, path_res};
 +
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_hir::{Expr, ExprKind, Node};
 +use rustc_lint::LateContext;
 +
 +use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
 +
 +enum IterType {
 +    Iter,
 +    IterMut,
 +    IntoIter,
 +}
 +
 +impl IterType {
 +    fn ref_prefix(&self) -> &'static str {
 +        match self {
 +            Self::Iter => "&",
 +            Self::IterMut => "&mut ",
 +            Self::IntoIter => "",
 +        }
 +    }
 +}
 +
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
 +    let item = match recv.kind {
 +        ExprKind::Array([]) => None,
 +        ExprKind::Array([e]) => Some(e),
 +        ExprKind::Path(ref p) if is_res_lang_ctor(cx, cx.qpath_res(p, recv.hir_id), OptionNone) => None,
 +        ExprKind::Call(f, [arg]) if is_res_lang_ctor(cx, path_res(cx, f), OptionSome) => Some(arg),
 +        _ => return,
 +    };
 +    let iter_type = match method_name {
 +        "iter" => IterType::Iter,
 +        "iter_mut" => IterType::IterMut,
 +        "into_iter" => IterType::IntoIter,
 +        _ => return,
 +    };
 +
 +    let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
 +        Some((Node::Expr(parent), child_id)) => match parent.kind {
 +            ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
 +            ExprKind::If(_, _, _)
 +            | ExprKind::Match(_, _, _)
 +            | ExprKind::Closure(_)
 +            | ExprKind::Ret(_)
 +            | ExprKind::Break(_, _) => true,
 +            _ => false,
 +        },
 +        Some((Node::Stmt(_) | Node::Local(_), _)) => false,
 +        _ => true,
 +    };
 +
 +    if is_unified {
 +        return;
 +    }
 +
 +    if let Some(i) = item {
 +        let sugg = format!(
 +            "{}::iter::once({}{})",
 +            if is_no_std_crate(cx) { "core" } else { "std" },
 +            iter_type.ref_prefix(),
 +            snippet(cx, i.span, "...")
 +        );
 +        span_lint_and_sugg(
 +            cx,
 +            ITER_ON_SINGLE_ITEMS,
 +            expr.span,
 +            &format!("`{method_name}` call on a collection with only one item"),
 +            "try",
 +            sugg,
 +            Applicability::MaybeIncorrect,
 +        );
 +    } else {
 +        span_lint_and_sugg(
 +            cx,
 +            ITER_ON_EMPTY_COLLECTIONS,
 +            expr.span,
 +            &format!("`{method_name}` call on an empty collection"),
 +            "try",
 +            if is_no_std_crate(cx) {
 +                "core::iter::empty()".to_string()
 +            } else {
 +                "std::iter::empty()".to_string()
 +            },
 +            Applicability::MaybeIncorrect,
 +        );
 +    }
 +}
index b80541b86479a804227b1152718881ad7d2daf58,0000000000000000000000000000000000000000..a7284c64497766b2b404b2377c5d0317b074d5d0
mode 100644,000000..100644
--- /dev/null
@@@ -1,157 -1,0 +1,157 @@@
- fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<MinMax> {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{match_def_path, path_def_id};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::layout::LayoutOf;
 +
 +pub fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    arith_lhs: &hir::Expr<'_>,
 +    arith_rhs: &hir::Expr<'_>,
 +    unwrap_arg: &hir::Expr<'_>,
 +    arith: &str,
 +) {
 +    let ty = cx.typeck_results().expr_ty(arith_lhs);
 +    if !ty.is_integral() {
 +        return;
 +    }
 +
 +    let Some(mm) = is_min_or_max(cx, unwrap_arg) else { return };
 +
 +    if ty.is_signed() {
 +        use self::{
 +            MinMax::{Max, Min},
 +            Sign::{Neg, Pos},
 +        };
 +
 +        let Some(sign) = lit_sign(arith_rhs) else {
 +            return;
 +        };
 +
 +        match (arith, sign, mm) {
 +            ("add", Pos, Max) | ("add", Neg, Min) | ("sub", Neg, Max) | ("sub", Pos, Min) => (),
 +            // "mul" is omitted because lhs can be negative.
 +            _ => return,
 +        }
 +    } else {
 +        match (mm, arith) {
 +            (MinMax::Max, "add" | "mul") | (MinMax::Min, "sub") => (),
 +            _ => return,
 +        }
 +    }
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +    span_lint_and_sugg(
 +        cx,
 +        super::MANUAL_SATURATING_ARITHMETIC,
 +        expr.span,
 +        "manual saturating arithmetic",
 +        &format!("try using `saturating_{arith}`"),
 +        format!(
 +            "{}.saturating_{arith}({})",
 +            snippet_with_applicability(cx, arith_lhs.span, "..", &mut applicability),
 +            snippet_with_applicability(cx, arith_rhs.span, "..", &mut applicability),
 +        ),
 +        applicability,
 +    );
 +}
 +
 +#[derive(PartialEq, Eq)]
 +enum MinMax {
 +    Min,
 +    Max,
 +}
 +
++fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
 +    // `T::max_value()` `T::min_value()` inherent methods
 +    if_chain! {
 +        if let hir::ExprKind::Call(func, args) = &expr.kind;
 +        if args.is_empty();
 +        if let hir::ExprKind::Path(hir::QPath::TypeRelative(_, segment)) = &func.kind;
 +        then {
 +            match segment.ident.as_str() {
 +                "max_value" => return Some(MinMax::Max),
 +                "min_value" => return Some(MinMax::Min),
 +                _ => {}
 +            }
 +        }
 +    }
 +
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    let ty_str = ty.to_string();
 +
 +    // `std::T::MAX` `std::T::MIN` constants
 +    if let Some(id) = path_def_id(cx, expr) {
 +        if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
 +            return Some(MinMax::Max);
 +        }
 +
 +        if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
 +            return Some(MinMax::Min);
 +        }
 +    }
 +
 +    // Literals
 +    let bits = cx.layout_of(ty).unwrap().size.bits();
 +    let (minval, maxval): (u128, u128) = if ty.is_signed() {
 +        let minval = 1 << (bits - 1);
 +        let mut maxval = !(1 << (bits - 1));
 +        if bits != 128 {
 +            maxval &= (1 << bits) - 1;
 +        }
 +        (minval, maxval)
 +    } else {
 +        (0, if bits == 128 { !0 } else { (1 << bits) - 1 })
 +    };
 +
 +    let check_lit = |expr: &hir::Expr<'_>, check_min: bool| {
 +        if let hir::ExprKind::Lit(lit) = &expr.kind {
 +            if let ast::LitKind::Int(value, _) = lit.node {
 +                if value == maxval {
 +                    return Some(MinMax::Max);
 +                }
 +
 +                if check_min && value == minval {
 +                    return Some(MinMax::Min);
 +                }
 +            }
 +        }
 +
 +        None
 +    };
 +
 +    if let r @ Some(_) = check_lit(expr, !ty.is_signed()) {
 +        return r;
 +    }
 +
 +    if ty.is_signed() {
 +        if let hir::ExprKind::Unary(hir::UnOp::Neg, val) = &expr.kind {
 +            return check_lit(val, true);
 +        }
 +    }
 +
 +    None
 +}
 +
 +#[derive(PartialEq, Eq)]
 +enum Sign {
 +    Pos,
 +    Neg,
 +}
 +
 +fn lit_sign(expr: &hir::Expr<'_>) -> Option<Sign> {
 +    if let hir::ExprKind::Unary(hir::UnOp::Neg, inner) = &expr.kind {
 +        if let hir::ExprKind::Lit(..) = &inner.kind {
 +            return Some(Sign::Neg);
 +        }
 +    } else if let hir::ExprKind::Lit(..) = &expr.kind {
 +        return Some(Sign::Pos);
 +    }
 +
 +    None
 +}
index 13c47c03a80dd583d6a006315dc2ce3fdd3f30f5,0000000000000000000000000000000000000000..a08f7254053fe1a499b74a22250f06a53297c4dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,97 @@@
-         if let Some(collect_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_path_diagnostic_item;
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 +use if_chain::if_chain;
 +use rustc_ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, LangItem};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::sym;
 +use std::borrow::Cow;
 +
 +use super::MANUAL_STR_REPEAT;
 +
 +enum RepeatKind {
 +    String,
 +    Char(char),
 +}
 +
 +fn get_ty_param(ty: Ty<'_>) -> Option<Ty<'_>> {
 +    if let ty::Adt(_, subs) = ty.kind() {
 +        subs.types().next()
 +    } else {
 +        None
 +    }
 +}
 +
 +fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
 +    if let ExprKind::Lit(lit) = &e.kind {
 +        match lit.node {
 +            LitKind::Str(..) => Some(RepeatKind::String),
 +            LitKind::Char(c) => Some(RepeatKind::Char(c)),
 +            _ => None,
 +        }
 +    } else {
 +        let ty = cx.typeck_results().expr_ty(e);
 +        if is_type_lang_item(cx, ty, LangItem::String)
 +            || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
 +            || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str))
 +        {
 +            Some(RepeatKind::String)
 +        } else {
 +            let ty = ty.peel_refs();
 +            (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)).then_some(RepeatKind::String)
 +        }
 +    }
 +}
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    collect_expr: &Expr<'_>,
 +    take_expr: &Expr<'_>,
 +    take_self_arg: &Expr<'_>,
 +    take_arg: &Expr<'_>,
 +) {
 +    if_chain! {
 +        if let ExprKind::Call(repeat_fn, [repeat_arg]) = take_self_arg.kind;
 +        if is_path_diagnostic_item(cx, repeat_fn, sym::iter_repeat);
 +        if is_type_lang_item(cx, cx.typeck_results().expr_ty(collect_expr), LangItem::String);
-         if cx.tcx.trait_of_item(collect_id) == Some(iter_trait_id);
 +        if let Some(take_id) = cx.typeck_results().type_dependent_def_id(take_expr.hir_id);
 +        if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        if cx.tcx.trait_of_item(take_id) == Some(iter_trait_id);
 +        if let Some(repeat_kind) = parse_repeat_arg(cx, repeat_arg);
 +        let ctxt = collect_expr.span.ctxt();
 +        if ctxt == take_expr.span.ctxt();
 +        if ctxt == take_self_arg.span.ctxt();
 +        then {
 +            let mut app = Applicability::MachineApplicable;
 +            let count_snip = snippet_with_context(cx, take_arg.span, ctxt, "..", &mut app).0;
 +
 +            let val_str = match repeat_kind {
 +                RepeatKind::Char(_) if repeat_arg.span.ctxt() != ctxt => return,
 +                RepeatKind::Char('\'') => r#""'""#.into(),
 +                RepeatKind::Char('"') => r#""\"""#.into(),
 +                RepeatKind::Char(_) =>
 +                    match snippet_with_applicability(cx, repeat_arg.span, "..", &mut app) {
 +                        Cow::Owned(s) => Cow::Owned(format!("\"{}\"", &s[1..s.len() - 1])),
 +                        s @ Cow::Borrowed(_) => s,
 +                    },
 +                RepeatKind::String =>
 +                    Sugg::hir_with_context(cx, repeat_arg, ctxt, "..", &mut app).maybe_par().to_string().into(),
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_STR_REPEAT,
 +                collect_expr.span,
 +                "manual implementation of `str::repeat` using iterators",
 +                "try this",
 +                format!("{val_str}.repeat({count_snip})"),
 +                app
 +            )
 +        }
 +    }
 +}
index 7ce14ec080b15fa42177d583cb2802526a414adf,0000000000000000000000000000000000000000..6bc783c6d505af108a508067f772a9fefcda5103
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,121 @@@
- pub(super) fn check<'tcx>(
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
 +use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
 +use 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;
 +use rustc_semver::RustcVersion;
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span};
 +
 +use super::MAP_CLONE;
 +
-     arg: &'tcx hir::Expr<'_>,
++pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    e: &hir::Expr<'_>,
 +    recv: &hir::Expr<'_>,
++    arg: &hir::Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) {
 +    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,
 +    );
 +}
 +
 +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
 +    let mut applicability = Applicability::MachineApplicable;
 +
 +    let (message, sugg_method) = if is_copy && meets_msrv(msrv, 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 d420f144eea1ff197dfe7439719a9c6a934e83fd,0000000000000000000000000000000000000000..a0300d278709d7e82ab20a5928d6ec74e4d6c8e0
mode 100644,000000..100644
--- /dev/null
@@@ -1,47 -1,0 +1,38 @@@
- use clippy_utils::is_trait_method;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
- pub(super) fn check(
-     cx: &LateContext<'_>,
-     expr: &hir::Expr<'_>,
-     iter: &hir::Expr<'_>,
-     map_fn: &hir::Expr<'_>,
-     collect_recv: &hir::Expr<'_>,
- ) {
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::symbol::sym;
 +
 +use super::MAP_COLLECT_RESULT_UNIT;
 +
-         // called on Iterator
-         if is_trait_method(cx, collect_recv, sym::Iterator);
-         // return of collect `Result<(),_>`
-         let collect_ret_ty = cx.typeck_results().expr_ty(expr);
++pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, iter: &hir::Expr<'_>, map_fn: &hir::Expr<'_>) {
++    // return of collect `Result<(),_>`
++    let collect_ret_ty = cx.typeck_results().expr_ty(expr);
 +    if_chain! {
 +        if is_type_diagnostic_item(cx, collect_ret_ty, sym::Result);
 +        if let ty::Adt(_, substs) = collect_ret_ty.kind();
 +        if let Some(result_t) = substs.types().next();
 +        if result_t.is_unit();
 +        // get parts for snippet
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                MAP_COLLECT_RESULT_UNIT,
 +                expr.span,
 +                "`.map().collect()` can be replaced with `.try_for_each()`",
 +                "try this",
 +                format!(
 +                    "{}.try_for_each({})",
 +                    snippet(cx, iter.span, ".."),
 +                    snippet(cx, map_fn.span, "..")
 +                ),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
index 1fb6617145e718bb0a9f116f955a3c5a73a30f09,0000000000000000000000000000000000000000..b773b3e423f407c78e8e1c68b959a205c0d87b41
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,34 @@@
- pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::MAP_ERR_IGNORE;
 +
++pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) {
 +    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
 +        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
 +        && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
 +        && let ExprKind::Closure(&Closure {
 +            capture_clause: CaptureBy::Ref,
 +            body,
 +            fn_decl_span,
 +            ..
 +        }) = arg.kind
 +        && let closure_body = cx.tcx.hir().body(body)
 +        && let [param] = closure_body.params
 +        && let PatKind::Wild = param.pat.kind
 +    {
 +        // span the area of the closure capture and warn that the
 +        // original error will be thrown away
 +        span_lint_and_help(
 +            cx,
 +            MAP_ERR_IGNORE,
 +            fn_decl_span,
 +            "`map_err(|_|...` wildcard pattern discards the original error",
 +            None,
 +            "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
 +        );
 +    }
 +}
index 8a76ba0b064b547b9d0579f8efc6ccfcb549311c,0000000000000000000000000000000000000000..38165ab4fb26f4053a256c012a933307538b8452
mode 100644,000000..100644
--- /dev/null
@@@ -1,3921 -1,0 +1,3996 @@@
- use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
- use clippy_utils::{contains_return, 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 rustc_hir::def::Res;
- use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
++use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
++use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
-     /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
++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};
 +use rustc_semver::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
-     #[clippy::version = "1.64.0"]
++    /// let hello = "hesuo worpd".replace(['s', 'u', 'p'], "l");
 +    /// ```
-     /// The function will always be called and potentially
-     /// allocate an object acting as the default.
++    #[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?
-     /// If the function has side-effects, not calling it will
-     /// change the semantic of the program, but you shouldn't rely on that anyway.
++    /// The function will always be called. This is only bad if it allocates or
++    /// does some non-trivial amount of work.
 +    ///
 +    /// ### Known problems
-     /// foo.unwrap_or(String::new());
++    /// 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_else(String::new);
-     ///
-     /// // or
-     ///
-     /// # let foo = Some(String::new());
-     /// foo.unwrap_or_default();
++    /// foo.unwrap_or(String::from("empty"));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
-     perf,
++    /// foo.unwrap_or_else(|| String::from("empty"));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OR_FUN_CALL,
-     /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
++    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
-     /// let data = cow.into_owned();
-     /// assert!(matches!(data, String))
++    /// 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);
 +    ///
-     #[clippy::version = "1.64.0"]
++    /// 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.64.0"]
++    #[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
- ) -> Option<(&'tcx str, &'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>], Span)> {
-     if let ExprKind::MethodCall(path, receiver, args, _) = recv.kind {
++    #[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>,
 +    allow_expect_in_tests: bool,
 +    allow_unwrap_in_tests: bool,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(
 +        avoid_breaking_exported_api: bool,
 +        msrv: Option<RustcVersion>,
 +        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>,
-             return Some((name, receiver, args, path.ident.span));
++) -> 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();
-             // walk the return type and check for Self (this does not check associated types)
-             if let Some(self_adt) = self_ty.ty_adt_def() {
-                 if contains_adt_constructor(ret_ty, self_adt) {
-                     return;
-                 }
-             } else if ret_ty.contains(self_ty) {
++            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);
 +                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 return type is impl trait, check the associated types
-             if let ty::Opaque(def_id, _) = *ret_ty.kind() {
-                 // one of the associated types must be Self
-                 for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
-                     if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
-                         let assoc_ty = match projection_predicate.term.unpack() {
-                             ty::TermKind::Ty(ty) => ty,
-                             ty::TermKind::Const(_c) => continue,
-                         };
-                         // walk the associated type and check for Self
-                         if let Some(self_adt) = self_ty.ty_adt_def() {
-                             if contains_adt_constructor(assoc_ty, self_adt) {
-                                 return;
-                             }
-                         } else if assoc_ty.contains(self_ty) {
-                             return;
-                         }
-                     }
-                 }
-             }
++            if contains_ty_adt_constructor_opaque(cx, ret_ty, self_ty) {
 +                return;
 +            }
 +
-         if let Some((name, recv, args, span)) = method_call(expr) {
 +            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<'_>) {
-                 ("collect", []) => 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, recv);
-                     },
-                     Some(("take", take_self_arg, [take_arg], _)) => {
-                         if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
-                             manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
-                         }
-                     },
-                     _ => {},
++        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),
 +                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
-                     Some(("cloned", recv2, [], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
-                     Some((name2 @ ("into_iter" | "iter" | "iter_mut"), recv2, [], _)) => {
++                ("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], _, _)) => {
++                            if meets_msrv(self.msrv, 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(("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),
++                    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(("ok", recv, [], _)) => ok_expect::check(cx, expr, recv),
-                     Some(("err", recv, [], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
++                    Some(("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(("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),
++                    Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv),
++                    Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
 +                    _ => 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) {
-                     if let Some(("inspect", _, [_], span2)) = 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(("collect", _, _, span)) = method_call(recv) {
++                    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),
 +                ("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((name2, recv2, args2, _span2)) = method_call(recv) {
++                    if let Some(("collect", _, _, span, _)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", []) | ("skip", [_]) => {
-                         if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _)) = method_call(recv) {
++                    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" {
 +                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
-                     if let Some((name, recv2, args, span2)) = method_call(recv) {
++                        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((name2, recv2, args2, _)) = method_call(recv) {
++                    if let Some((name, recv2, args, span2,_)) = method_call(recv) {
 +                        match (name, args) {
 +                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, 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", []) => {
-                     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),
++                    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),
 +                            ("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) {
-                     if let Some((name2, recv2, args2, _span2)) = 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_FROM_CURRENT) {
++                        seek_from_current::check(cx, expr, recv, arg);
++                    }
++                    if meets_msrv(self.msrv, 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);
 +                        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]) => {
-                         Some(("get", recv, [get_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 !meets_msrv(self.msrv, 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_mut", recv, [get_arg], _)) => {
++                        Some(("get", recv, [get_arg], _, _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
-                         Some(("or", recv, [or_arg], or_span)) => {
++                        Some(("get_mut", recv, [get_arg], _, _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
-                     Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _)) => {
++                        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(("map", m_recv, [m_arg], span)) => {
++                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), lhs, [rhs], _, _)) => {
 +                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                    },
-                     Some(("then_some", t_recv, [t_arg], _)) => {
++                    Some(("map", m_recv, [m_arg], span, _)) => {
 +                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                    },
-                     Some(("map", recv, [map_arg], _))
++                    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) {
-     if let Some((name @ ("find" | "position" | "rposition"), f_recv, [arg], span)) = 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) {
- fn is_bool(ty: &hir::Ty<'_>) -> bool {
-     if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
-         matches!(path.res, Res::PrimTy(PrimTy::Bool))
-     } else {
-         false
-     }
- }
++    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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b088e642e0e9acd258e5e422ce1984d08b06d226
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,456 @@@
++use super::NEEDLESS_COLLECT;
++use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
++use clippy_utils::higher;
++use clippy_utils::source::{snippet, snippet_with_applicability};
++use clippy_utils::sugg::Sugg;
++use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
++use clippy_utils::{
++    can_move_expr_to_closure, get_enclosing_block, get_parent_node, is_trait_method, path_to_local, path_to_local_id,
++    CaptureKind,
++};
++use rustc_data_structures::fx::FxHashMap;
++use rustc_errors::{Applicability, MultiSpan};
++use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
++use rustc_hir::{
++    BindingAnnotation, Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind,
++};
++use rustc_lint::LateContext;
++use rustc_middle::hir::nested_filter;
++use rustc_middle::ty::{self, AssocKind, EarlyBinder, GenericArg, GenericArgKind, Ty};
++use rustc_span::symbol::Ident;
++use rustc_span::{sym, Span, Symbol};
++
++const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    name_span: Span,
++    collect_expr: &'tcx Expr<'_>,
++    iter_expr: &'tcx Expr<'tcx>,
++    call_span: Span,
++) {
++    if let Some(parent) = get_parent_node(cx.tcx, collect_expr.hir_id) {
++        match parent {
++            Node::Expr(parent) => {
++                if let ExprKind::MethodCall(name, _, args @ ([] | [_]), _) = parent.kind {
++                    let mut app = Applicability::MachineApplicable;
++                    let name = name.ident.as_str();
++                    let collect_ty = cx.typeck_results().expr_ty(collect_expr);
++
++                    let sugg: String = match name {
++                        "len" => {
++                            if let Some(adt) = collect_ty.ty_adt_def()
++                                && matches!(
++                                    cx.tcx.get_diagnostic_name(adt.did()),
++                                    Some(sym::Vec | sym::VecDeque | sym::LinkedList | sym::BinaryHeap)
++                                )
++                            {
++                                "count()".into()
++                            } else {
++                                return;
++                            }
++                        },
++                        "is_empty"
++                            if is_is_empty_sig(cx, parent.hir_id)
++                                && iterates_same_ty(cx, cx.typeck_results().expr_ty(iter_expr), collect_ty) =>
++                        {
++                            "next().is_none()".into()
++                        },
++                        "contains" => {
++                            if is_contains_sig(cx, parent.hir_id, iter_expr)
++                                && let Some(arg) = args.first()
++                            {
++                                let (span, prefix) = if let ExprKind::AddrOf(_, _, arg) = arg.kind {
++                                    (arg.span, "")
++                                } else {
++                                    (arg.span, "*")
++                                };
++                                let snip = snippet_with_applicability(cx, span, "??", &mut app);
++                                format!("any(|x| x == {prefix}{snip})")
++                            } else {
++                                return;
++                            }
++                        },
++                        _ => return,
++                    };
++
++                    span_lint_and_sugg(
++                        cx,
++                        NEEDLESS_COLLECT,
++                        call_span.with_hi(parent.span.hi()),
++                        NEEDLESS_COLLECT_MSG,
++                        "replace with",
++                        sugg,
++                        app,
++                    );
++                }
++            },
++            Node::Local(l) => {
++                if let PatKind::Binding(BindingAnnotation::NONE | BindingAnnotation::MUT, id, _, None)
++                    = l.pat.kind
++                    && let ty = cx.typeck_results().expr_ty(collect_expr)
++                    && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList].into_iter()
++                        .any(|item| is_type_diagnostic_item(cx, ty, item))
++                    && let iter_ty = cx.typeck_results().expr_ty(iter_expr)
++                    && let Some(block) = get_enclosing_block(cx, l.hir_id)
++                    && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
++                    && let [iter_call] = &*iter_calls
++                {
++                    let mut used_count_visitor = UsedCountVisitor {
++                        cx,
++                        id,
++                        count: 0,
++                    };
++                    walk_block(&mut used_count_visitor, block);
++                    if used_count_visitor.count > 1 {
++                        return;
++                    }
++
++                    // Suggest replacing iter_call with iter_replacement, and removing stmt
++                    let mut span = MultiSpan::from_span(name_span);
++                    span.push_span_label(iter_call.span, "the iterator could be used here instead");
++                    span_lint_hir_and_then(
++                        cx,
++                        super::NEEDLESS_COLLECT,
++                        collect_expr.hir_id,
++                        span,
++                        NEEDLESS_COLLECT_MSG,
++                        |diag| {
++                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_expr, ".."), iter_call.get_iter_method(cx));
++                            diag.multipart_suggestion(
++                                iter_call.get_suggestion_text(),
++                                vec![
++                                    (l.span, String::new()),
++                                    (iter_call.span, iter_replacement)
++                                ],
++                                Applicability::MaybeIncorrect,
++                            );
++                        },
++                    );
++                }
++            },
++            _ => (),
++        }
++    }
++}
++
++/// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
++fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
++    cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
++        let sig = cx.tcx.fn_sig(id).skip_binder();
++        sig.inputs().len() == 1 && sig.output().is_bool()
++    })
++}
++
++/// Checks if `<iter_ty as Iterator>::Item` is the same as `<collect_ty as IntoIter>::Item`
++fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
++    let item = Symbol::intern("Item");
++    if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
++        && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
++        && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, item, [iter_ty])
++        && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, item, [collect_ty])
++        && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
++            cx.param_env,
++            cx.tcx.mk_projection(into_iter_item_proj.item_def_id, into_iter_item_proj.substs)
++        )
++    {
++        iter_item_ty == into_iter_item_ty
++    } else {
++        false
++    }
++}
++
++/// Checks if the given method call matches the expected signature of
++/// `([&[mut]] self, &<iter_ty as Iterator>::Item) -> bool`
++fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -> bool {
++    let typeck = cx.typeck_results();
++    if let Some(id) = typeck.type_dependent_def_id(call_id)
++        && let sig = cx.tcx.fn_sig(id)
++        && sig.skip_binder().output().is_bool()
++        && let [_, search_ty] = *sig.skip_binder().inputs()
++        && let ty::Ref(_, search_ty, Mutability::Not) = *cx.tcx.erase_late_bound_regions(sig.rebind(search_ty)).kind()
++        && let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
++        && let Some(iter_item) = cx.tcx
++            .associated_items(iter_trait)
++            .find_by_name_and_kind(cx.tcx, Ident::with_dummy_span(Symbol::intern("Item")), AssocKind::Type, iter_trait)
++        && let substs = cx.tcx.mk_substs([GenericArg::from(typeck.expr_ty_adjusted(iter_expr))].into_iter())
++        && let proj_ty = cx.tcx.mk_projection(iter_item.def_id, substs)
++        && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
++    {
++        item_ty == EarlyBinder(search_ty).subst(cx.tcx, cx.typeck_results().node_substs(call_id))
++    } else {
++        false
++    }
++}
++
++struct IterFunction {
++    func: IterFunctionKind,
++    span: Span,
++}
++impl IterFunction {
++    fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
++        match &self.func {
++            IterFunctionKind::IntoIter => String::new(),
++            IterFunctionKind::Len => String::from(".count()"),
++            IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
++            IterFunctionKind::Contains(span) => {
++                let s = snippet(cx, *span, "..");
++                if let Some(stripped) = s.strip_prefix('&') {
++                    format!(".any(|x| x == {stripped})")
++                } else {
++                    format!(".any(|x| x == *{s})")
++                }
++            },
++        }
++    }
++    fn get_suggestion_text(&self) -> &'static str {
++        match &self.func {
++            IterFunctionKind::IntoIter => {
++                "use the original Iterator instead of collecting it and then producing a new one"
++            },
++            IterFunctionKind::Len => {
++                "take the original Iterator's count instead of collecting it and finding the length"
++            },
++            IterFunctionKind::IsEmpty => {
++                "check if the original Iterator has anything instead of collecting it and seeing if it's empty"
++            },
++            IterFunctionKind::Contains(_) => {
++                "check if the original Iterator contains an element instead of collecting then checking"
++            },
++        }
++    }
++}
++enum IterFunctionKind {
++    IntoIter,
++    Len,
++    IsEmpty,
++    Contains(Span),
++}
++
++struct IterFunctionVisitor<'a, 'tcx> {
++    illegal_mutable_capture_ids: HirIdSet,
++    current_mutably_captured_ids: HirIdSet,
++    cx: &'a LateContext<'tcx>,
++    uses: Vec<Option<IterFunction>>,
++    hir_id_uses_map: FxHashMap<HirId, usize>,
++    current_statement_hir_id: Option<HirId>,
++    seen_other: bool,
++    target: HirId,
++}
++impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
++    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
++        for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
++            if check_loop_kind(expr).is_some() {
++                continue;
++            }
++            self.visit_block_expr(expr, hir_id);
++        }
++        if let Some(expr) = block.expr {
++            if let Some(loop_kind) = check_loop_kind(expr) {
++                if let LoopKind::Conditional(block_expr) = loop_kind {
++                    self.visit_block_expr(block_expr, None);
++                }
++            } else {
++                self.visit_block_expr(expr, None);
++            }
++        }
++    }
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
++        // Check function calls on our collection
++        if let ExprKind::MethodCall(method_name, recv, [args @ ..], _) = &expr.kind {
++            if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
++                self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
++                self.visit_expr(recv);
++                return;
++            }
++
++            if path_to_local_id(recv, self.target) {
++                if self
++                    .illegal_mutable_capture_ids
++                    .intersection(&self.current_mutably_captured_ids)
++                    .next()
++                    .is_none()
++                {
++                    if let Some(hir_id) = self.current_statement_hir_id {
++                        self.hir_id_uses_map.insert(hir_id, self.uses.len());
++                    }
++                    match method_name.ident.name.as_str() {
++                        "into_iter" => self.uses.push(Some(IterFunction {
++                            func: IterFunctionKind::IntoIter,
++                            span: expr.span,
++                        })),
++                        "len" => self.uses.push(Some(IterFunction {
++                            func: IterFunctionKind::Len,
++                            span: expr.span,
++                        })),
++                        "is_empty" => self.uses.push(Some(IterFunction {
++                            func: IterFunctionKind::IsEmpty,
++                            span: expr.span,
++                        })),
++                        "contains" => self.uses.push(Some(IterFunction {
++                            func: IterFunctionKind::Contains(args[0].span),
++                            span: expr.span,
++                        })),
++                        _ => {
++                            self.seen_other = true;
++                            if let Some(hir_id) = self.current_statement_hir_id {
++                                self.hir_id_uses_map.remove(&hir_id);
++                            }
++                        },
++                    }
++                }
++                return;
++            }
++
++            if let Some(hir_id) = path_to_local(recv) {
++                if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
++                    if self
++                        .illegal_mutable_capture_ids
++                        .intersection(&self.current_mutably_captured_ids)
++                        .next()
++                        .is_none()
++                    {
++                        if let Some(hir_id) = self.current_statement_hir_id {
++                            self.hir_id_uses_map.insert(hir_id, index);
++                        }
++                    } else {
++                        self.uses[index] = None;
++                    }
++                }
++            }
++        }
++        // Check if the collection is used for anything else
++        if path_to_local_id(expr, self.target) {
++            self.seen_other = true;
++        } else {
++            walk_expr(self, expr);
++        }
++    }
++}
++
++enum LoopKind<'tcx> {
++    Conditional(&'tcx Expr<'tcx>),
++    Loop,
++}
++
++fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
++    if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
++        return Some(LoopKind::Conditional(let_expr));
++    }
++    if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
++        return Some(LoopKind::Conditional(condition));
++    }
++    if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
++        return Some(LoopKind::Conditional(arg));
++    }
++    if let ExprKind::Loop { .. } = expr.kind {
++        return Some(LoopKind::Loop);
++    }
++
++    None
++}
++
++impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
++    fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
++        self.current_statement_hir_id = hir_id;
++        self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(expr));
++        self.visit_expr(expr);
++    }
++}
++
++fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>, Option<HirId>)> {
++    match stmt.kind {
++        StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
++        StmtKind::Item(..) => None,
++        StmtKind::Local(Local { init, pat, .. }) => {
++            if let PatKind::Binding(_, hir_id, ..) = pat.kind {
++                init.map(|init_expr| (init_expr, Some(hir_id)))
++            } else {
++                init.map(|init_expr| (init_expr, None))
++            }
++        },
++    }
++}
++
++struct UsedCountVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'tcx>,
++    id: HirId,
++    count: usize,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
++    type NestedFilter = nested_filter::OnlyBodies;
++
++    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if path_to_local_id(expr, self.id) {
++            self.count += 1;
++        } else {
++            walk_expr(self, expr);
++        }
++    }
++
++    fn nested_visit_map(&mut self) -> Self::Map {
++        self.cx.tcx.hir()
++    }
++}
++
++/// Detect the occurrences of calls to `iter` or `into_iter` for the
++/// given identifier
++fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
++    block: &'tcx Block<'tcx>,
++    id: HirId,
++    cx: &'a LateContext<'tcx>,
++    captured_ids: HirIdSet,
++) -> Option<Vec<IterFunction>> {
++    let mut visitor = IterFunctionVisitor {
++        uses: Vec::new(),
++        target: id,
++        seen_other: false,
++        cx,
++        current_mutably_captured_ids: HirIdSet::default(),
++        illegal_mutable_capture_ids: captured_ids,
++        hir_id_uses_map: FxHashMap::default(),
++        current_statement_hir_id: None,
++    };
++    visitor.visit_block(block);
++    if visitor.seen_other {
++        None
++    } else {
++        Some(visitor.uses.into_iter().flatten().collect())
++    }
++}
++
++fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet {
++    fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: Ty<'_>, set: &mut HirIdSet) {
++        match ty.kind() {
++            ty::Adt(_, generics) => {
++                for generic in *generics {
++                    if let GenericArgKind::Type(ty) = generic.unpack() {
++                        get_captured_ids_recursive(cx, ty, set);
++                    }
++                }
++            },
++            ty::Closure(def_id, _) => {
++                let closure_hir_node = cx.tcx.hir().get_if_local(*def_id).unwrap();
++                if let Node::Expr(closure_expr) = closure_hir_node {
++                    can_move_expr_to_closure(cx, closure_expr)
++                        .unwrap()
++                        .into_iter()
++                        .for_each(|(hir_id, capture_kind)| {
++                            if matches!(capture_kind, CaptureKind::Ref(Mutability::Mut)) {
++                                set.insert(hir_id);
++                            }
++                        });
++                }
++            },
++            _ => (),
++        }
++    }
++
++    let mut set = HirIdSet::default();
++
++    get_captured_ids_recursive(cx, ty, &mut set);
++
++    set
++}
index 742483e6b2e5545274adf08081f1db58953eb8c5,0000000000000000000000000000000000000000..e6eb64bcbde64ad0abd70e444e806a30ee51777d
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,120 @@@
- pub(super) fn check<'tcx>(
-     cx: &LateContext<'tcx>,
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_span::sym;
 +
 +use super::OPTION_AS_REF_DEREF;
 +
 +/// 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,
 +    msrv: Option<RustcVersion>,
 +) {
 +    if !meets_msrv(msrv, 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 991d3dd538bf86a7ea572c011cd419fb1aa22999,0000000000000000000000000000000000000000..4460f38fcc18f8ff5743eda8ecdbb1347617ce88
mode 100644,000000..100644
--- /dev/null
@@@ -1,180 -1,0 +1,205 @@@
-                 let macro_expanded_snipped;
-                 let sugg: Cow<'_, str> = {
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
 +use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{contains_return, is_trait_item, last_path_segment};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::{kw, sym, Symbol};
 +use std::borrow::Cow;
 +
 +use super::OR_FUN_CALL;
 +
 +/// Checks for the `OR_FUN_CALL` lint.
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &hir::Expr<'_>,
 +    method_span: Span,
 +    name: &str,
 +    receiver: &'tcx hir::Expr<'_>,
 +    args: &'tcx [hir::Expr<'_>],
 +) {
 +    /// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
 +    /// `or_insert(T::new())` or `or_insert(T::default())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_unwrap_or_default(
 +        cx: &LateContext<'_>,
 +        name: &str,
 +        fun: &hir::Expr<'_>,
 +        arg: &hir::Expr<'_>,
 +        or_has_args: bool,
 +        span: Span,
 +        method_span: Span,
 +    ) -> bool {
 +        let is_default_default = || is_trait_item(cx, fun, sym::Default);
 +
 +        let implements_default = |arg, default_trait_id| {
 +            let arg_ty = cx.typeck_results().expr_ty(arg);
 +            implements_trait(cx, arg_ty, default_trait_id, &[])
 +        };
 +
 +        if_chain! {
 +            if !or_has_args;
 +            if let Some(sugg) = match name {
 +                "unwrap_or" => Some("unwrap_or_default"),
 +                "or_insert" => Some("or_default"),
 +                _ => None,
 +            };
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
 +            let path = last_path_segment(qpath).ident.name;
 +            // needs to target Default::default in particular or be *::new and have a Default impl
 +            // available
 +            if (matches!(path, kw::Default) && is_default_default())
 +                || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
 +
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    method_span.with_hi(span.hi()),
 +                    &format!("use of `{name}` followed by a call to `{path}`"),
 +                    "try this",
 +                    format!("{sugg}()"),
 +                    Applicability::MachineApplicable,
 +                );
 +
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +
 +    /// Checks for `*or(foo())`.
 +    #[allow(clippy::too_many_arguments)]
 +    fn check_general_case<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        name: &str,
 +        method_span: Span,
 +        self_expr: &hir::Expr<'_>,
 +        arg: &'tcx hir::Expr<'_>,
++        // `Some` if fn has second argument
++        second_arg: Option<&hir::Expr<'_>>,
 +        span: Span,
 +        // None if lambda is required
 +        fun_span: Option<Span>,
 +    ) {
 +        // (path, fn_has_argument, methods, suffix)
 +        const KNOW_TYPES: [(Symbol, bool, &[&str], &str); 4] = [
 +            (sym::BTreeEntry, false, &["or_insert"], "with"),
 +            (sym::HashMapEntry, false, &["or_insert"], "with"),
 +            (sym::Option, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),
 +            (sym::Result, true, &["or", "unwrap_or"], "else"),
 +        ];
 +
 +        if_chain! {
 +            if KNOW_TYPES.iter().any(|k| k.2.contains(&name));
 +
 +            if switch_to_lazy_eval(cx, arg);
 +            if !contains_return(arg);
 +
 +            let self_ty = cx.typeck_results().expr_ty(self_expr);
 +
 +            if let Some(&(_, fn_has_arguments, poss, suffix)) =
 +                KNOW_TYPES.iter().find(|&&i| is_type_diagnostic_item(cx, self_ty, i.0));
 +
 +            if poss.contains(&name);
 +
 +            then {
-                     let snippet = {
-                         let not_macro_argument_snippet = snippet_with_macro_callsite(cx, snippet_span, "..");
-                         if not_macro_argument_snippet == "vec![]" {
-                             macro_expanded_snipped = snippet(cx, snippet_span, "..");
++                let sugg = {
 +                    let (snippet_span, use_lambda) = match (fn_has_arguments, fun_span) {
 +                        (false, Some(fun_span)) => (fun_span, false),
 +                        _ => (arg.span, true),
 +                    };
-                                 Some(stripped) => Cow::from(stripped),
++
++                    let format_span = |span: Span| {
++                        let not_macro_argument_snippet = snippet_with_macro_callsite(cx, span, "..");
++                        let snip = if not_macro_argument_snippet == "vec![]" {
++                            let macro_expanded_snipped = snippet(cx, snippet_span, "..");
 +                            match macro_expanded_snipped.strip_prefix("$crate::vec::") {
-                         }
++                                Some(stripped) => Cow::Owned(stripped.to_owned()),
 +                                None => macro_expanded_snipped,
 +                            }
 +                        } else {
 +                            not_macro_argument_snippet
-                     if use_lambda {
++                        };
++
++                        snip.to_string()
 +                    };
 +
-                         format!("|{l_arg}| {snippet}").into()
++                    let snip = format_span(snippet_span);
++                    let snip = if use_lambda {
 +                        let l_arg = if fn_has_arguments { "_" } else { "" };
-                         snippet
++                        format!("|{l_arg}| {snip}")
 +                    } else {
-     if let [arg] = args {
-         let inner_arg = if let hir::ExprKind::Block(
++                        snip
++                    };
++
++                    if let Some(f) = second_arg {
++                        let f = format_span(f.span);
++                        format!("{snip}, {f}")
++                    } else {
++                        snip
 +                    }
 +                };
 +                let span_replace_word = method_span.with_hi(span.hi());
 +                span_lint_and_sugg(
 +                    cx,
 +                    OR_FUN_CALL,
 +                    span_replace_word,
 +                    &format!("use of `{name}` followed by a function call"),
 +                    "try this",
 +                    format!("{name}_{suffix}({sugg})"),
 +                    Applicability::HasPlaceholders,
 +                );
 +            }
 +        }
 +    }
 +
-         };
++    let extract_inner_arg = |arg: &'tcx hir::Expr<'_>| {
++        if let hir::ExprKind::Block(
 +            hir::Block {
 +                stmts: [],
 +                expr: Some(expr),
 +                ..
 +            },
 +            _,
 +        ) = arg.kind
 +        {
 +            expr
 +        } else {
 +            arg
-                     check_general_case(cx, name, method_span, receiver, arg, expr.span, fun_span);
++        }
++    };
++
++    if let [arg] = args {
++        let inner_arg = extract_inner_arg(arg);
 +        match inner_arg.kind {
 +            hir::ExprKind::Call(fun, or_args) => {
 +                let or_has_args = !or_args.is_empty();
 +                if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
 +                    let fun_span = if or_has_args { None } else { Some(fun.span) };
-                 check_general_case(cx, name, method_span, receiver, arg, expr.span, None);
++                    check_general_case(cx, name, method_span, receiver, arg, None, expr.span, fun_span);
 +                }
 +            },
 +            hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
++                check_general_case(cx, name, method_span, receiver, arg, None, expr.span, None);
 +            },
 +            _ => (),
 +        }
 +    }
++
++    // `map_or` takes two arguments
++    if let [arg, lambda] = args {
++        let inner_arg = extract_inner_arg(arg);
++        if let hir::ExprKind::Call(fun, or_args) = inner_arg.kind {
++            let fun_span = if or_args.is_empty() { Some(fun.span) } else { None };
++            check_general_case(cx, name, method_span, receiver, arg, Some(lambda), expr.span, fun_span);
++        }
++    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..361a3082f949a12baec89f13955c8701ebb8aaad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,48 @@@
++use rustc_ast::ast::{LitIntType, LitKind};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++
++use clippy_utils::{
++    diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability,
++    ty::implements_trait,
++};
++
++use super::SEEK_FROM_CURRENT;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
++    let ty = cx.typeck_results().expr_ty(recv);
++
++    if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) {
++        if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
++            let mut applicability = Applicability::MachineApplicable;
++            let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
++
++            span_lint_and_sugg(
++                cx,
++                SEEK_FROM_CURRENT,
++                expr.span,
++                "using `SeekFrom::Current` to start from current position",
++                "replace with",
++                format!("{snip}.stream_position()"),
++                applicability,
++            );
++        }
++    }
++}
++
++fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
++    if let ExprKind::Call(f, args) = expr.kind &&
++        let ExprKind::Path(ref path) = f.kind &&
++        let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() &&
++        match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
++        // check if argument of `SeekFrom::Current` is `0`
++        if args.len() == 1 &&
++            let ExprKind::Lit(ref lit) = args[0].kind &&
++            let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
++            return true
++        }
++    }
++
++    false
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7e3bed1e41a9457d9ca485d17ac4c0b92cf52dc7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::ty::implements_trait;
++use clippy_utils::{get_trait_def_id, match_def_path, paths};
++use rustc_ast::ast::{LitIntType, LitKind};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_span::Span;
++
++use super::SEEK_TO_START_INSTEAD_OF_REWIND;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    recv: &'tcx Expr<'_>,
++    arg: &'tcx Expr<'_>,
++    name_span: Span,
++) {
++    // Get receiver type
++    let ty = cx.typeck_results().expr_ty(recv).peel_refs();
++
++    if let Some(seek_trait_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) &&
++        implements_trait(cx, ty, seek_trait_id, &[]) &&
++        let ExprKind::Call(func, args1) = arg.kind &&
++        let ExprKind::Path(ref path) = func.kind &&
++        let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id() &&
++        match_def_path(cx, def_id, &paths::STD_IO_SEEKFROM_START) &&
++        args1.len() == 1 &&
++        let ExprKind::Lit(ref lit) = args1[0].kind &&
++        let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node
++    {
++        let method_call_span = expr.span.with_lo(name_span.lo());
++        span_lint_and_then(
++            cx,
++            SEEK_TO_START_INSTEAD_OF_REWIND,
++            method_call_span,
++            "used `seek` to go to the start of the stream",
++            |diag| {
++                let app = Applicability::MachineApplicable;
++
++                diag.span_suggestion(method_call_span, "replace with", "rewind()", app);
++            },
++        );
++    }
++}
index 6f4cec546e969693a0ec9b5260dd98f3ea58f519,0000000000000000000000000000000000000000..f35d81cee8e97f969bb8b16c04ff39b494270704
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,47 @@@
-             ""
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::method_chain_args;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_lang_item;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +
 +use super::STRING_EXTEND_CHARS;
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
 +    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 +    if !is_type_lang_item(cx, obj_ty, hir::LangItem::String) {
 +        return;
 +    }
 +    if let Some(arglists) = method_chain_args(arg, &["chars"]) {
 +        let target = &arglists[0].0;
 +        let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
 +        let ref_str = if *self_ty.kind() == ty::Str {
++            if matches!(target.kind, hir::ExprKind::Index(..)) {
++                "&"
++            } else {
++                ""
++            }
 +        } else if is_type_lang_item(cx, self_ty, hir::LangItem::String) {
 +            "&"
 +        } else {
 +            return;
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            STRING_EXTEND_CHARS,
 +            expr.span,
 +            "calling `.extend(_.chars())`",
 +            "try this",
 +            format!(
 +                "{}.push_str({ref_str}{})",
 +                snippet_with_applicability(cx, recv.span, "..", &mut applicability),
 +                snippet_with_applicability(cx, target.span, "..", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
index 851cdf544550f6caf077890c8c3d9f1293058bbd,0000000000000000000000000000000000000000..2ac0786b37b1e73fd431cd3267c30b0992bed0b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,36 @@@
- pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::usage::mutated_variables;
 +use clippy_utils::{expr_or_init, is_trait_method};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::SUSPICIOUS_MAP;
 +
++pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, count_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>) {
 +    if_chain! {
 +        if is_trait_method(cx, count_recv, sym::Iterator);
 +        let closure = expr_or_init(cx, map_arg);
 +        if let Some(def_id) = cx.tcx.hir().opt_local_def_id(closure.hir_id);
 +        if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id);
 +        let closure_body = cx.tcx.hir().body(body_id);
 +        if !cx.typeck_results().expr_ty(closure_body.value).is_unit();
 +        then {
 +            if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
 +                // A variable is used mutably inside of the closure. Suppress the lint.
 +                if !map_mutated_vars.is_empty() {
 +                    return;
 +                }
 +            }
 +            span_lint_and_help(
 +                cx,
 +                SUSPICIOUS_MAP,
 +                expr.span,
 +                "this call to `map()` won't have an effect on the call to `count()`",
 +                None,
 +                "make sure you did not confuse `map` with `filter`, `for_each` or `inspect`",
 +            );
 +        }
 +    }
 +}
index c9b87bc6bf29d8e9df51a8b64291438300d772e1,0000000000000000000000000000000000000000..087e1e4343b707b4e29a87bcd9012ecd38e6de62
mode 100644,000000..100644
--- /dev/null
@@@ -1,41 -1,0 +1,41 @@@
-                 r#"called `.collect<Vec<String>>().join("")` on an iterator"#,
 +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::is_type_lang_item};
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, LangItem};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{Ref, Slice};
 +use rustc_span::Span;
 +
 +use super::UNNECESSARY_JOIN;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    join_self_arg: &'tcx Expr<'tcx>,
 +    join_arg: &'tcx Expr<'tcx>,
 +    span: Span,
 +) {
 +    let applicability = Applicability::MachineApplicable;
 +    let collect_output_adjusted_type = cx.typeck_results().expr_ty_adjusted(join_self_arg);
 +    if_chain! {
 +        // the turbofish for collect is ::<Vec<String>>
 +        if let Ref(_, ref_type, _) = collect_output_adjusted_type.kind();
 +        if let Slice(slice) = ref_type.kind();
 +        if is_type_lang_item(cx, *slice, LangItem::String);
 +        // the argument for join is ""
 +        if let ExprKind::Lit(spanned) = &join_arg.kind;
 +        if let LitKind::Str(symbol, _) = spanned.node;
 +        if symbol.is_empty();
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_JOIN,
 +                span.with_hi(expr.span.hi()),
++                r#"called `.collect::<Vec<String>>().join("")` on an iterator"#,
 +                "try using",
 +                "collect::<String>()".to_owned(),
 +                applicability,
 +            );
 +        }
 +    }
 +}
index ee17f2d7889ee9f93b6f619bf0100d982a62ec3e,0000000000000000000000000000000000000000..90983f249cd59eae7dcb6af4f4b1eb4b5762ce6c
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,53 @@@
- use clippy_utils::{is_in_test_function, is_lint_allowed};
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_type_diagnostic_item;
-         Some((UNWRAP_USED, "an Option", "None", ""))
++use clippy_utils::{is_in_cfg_test, is_lint_allowed};
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::{EXPECT_USED, UNWRAP_USED};
 +
 +/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    recv: &hir::Expr<'_>,
 +    is_err: bool,
 +    allow_unwrap_in_tests: bool,
 +) {
 +    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 +
 +    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
-         Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
++        Some((UNWRAP_USED, "an `Option`", "None", ""))
 +    } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-     if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
++        Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an "))
 +    } else {
 +        None
 +    };
 +
 +    let method_suffix = if is_err { "_err" } else { "" };
 +
-             &format!("used `unwrap{method_suffix}()` on `{kind}` value"),
++    if allow_unwrap_in_tests && is_in_cfg_test(cx.tcx, expr.hir_id) {
 +        return;
 +    }
 +
 +    if let Some((lint, kind, none_value, none_prefix)) = mess {
 +        let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
 +            format!(
 +                "if you don't want to handle the `{none_value}` case gracefully, consider \
 +                using `expect{method_suffix}()` to provide a better panic message"
 +            )
 +        } else {
 +            format!("if this value is {none_prefix}`{none_value}`, it will panic")
 +        };
 +
 +        span_lint_and_help(
 +            cx,
 +            lint,
 +            expr.span,
++            &format!("used `unwrap{method_suffix}()` on {kind} value"),
 +            None,
 +            &help,
 +        );
 +    }
 +}
index 872679f25ab5a777364c7c3ee8a1dfbe1a4d015d,0000000000000000000000000000000000000000..4712846939e6a0fcaae2a242895f7d9709988314
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,101 @@@
-             if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs, None) {
 +use clippy_utils::{diagnostics::span_lint_and_sugg, source::snippet_opt};
 +
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::{def::Res, def_id::DefId, Item, ItemKind, UseKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Symbol;
 +
 +use crate::utils::conf::Rename;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for imports that do not rename the item as specified
 +    /// in the `enforce-import-renames` config option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency is important, if a project has defined import
 +    /// renames they should be followed. More practically, some item names are too
 +    /// vague outside of their defining scope this can enforce a more meaningful naming.
 +    ///
 +    /// ### Example
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
 +    /// enforced-import-renames = [ { path = "serde_json::Value", rename = "JsonValue" }]
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// use serde_json::Value;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// use serde_json::Value as JsonValue;
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub MISSING_ENFORCED_IMPORT_RENAMES,
 +    restriction,
 +    "enforce import renames"
 +}
 +
 +pub struct ImportRename {
 +    conf_renames: Vec<Rename>,
 +    renames: FxHashMap<DefId, Symbol>,
 +}
 +
 +impl ImportRename {
 +    pub fn new(conf_renames: Vec<Rename>) -> Self {
 +        Self {
 +            conf_renames,
 +            renames: FxHashMap::default(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]);
 +
 +impl LateLintPass<'_> for ImportRename {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        for Rename { path, rename } in &self.conf_renames {
 +            let segs = path.split("::").collect::<Vec<_>>();
++            for id in clippy_utils::def_path_def_ids(cx, &segs) {
 +                self.renames.insert(id, Symbol::intern(rename));
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        if_chain! {
 +            if let ItemKind::Use(path, UseKind::Single) = &item.kind;
 +            if let Res::Def(_, id) = path.res;
 +            if let Some(name) = self.renames.get(&id);
 +            // Remove semicolon since it is not present for nested imports
 +            let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';');
 +            if let Some(snip) = snippet_opt(cx, span_without_semi);
 +            if let Some(import) = match snip.split_once(" as ") {
 +                None => Some(snip.as_str()),
 +                Some((import, rename)) => {
 +                    if rename.trim() == name.as_str() {
 +                        None
 +                    } else {
 +                        Some(import.trim())
 +                    }
 +                },
 +            };
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MISSING_ENFORCED_IMPORT_RENAMES,
 +                    span_without_semi,
 +                    "this import should be renamed",
 +                    "try",
 +                    format!(
 +                        "{import} as {name}",
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index 6752976348f6045ed45e98ad774cf722d43d14ea,0000000000000000000000000000000000000000..321fa4b7f9996681000b59321216a3474766df11
mode 100644,000000..100644
--- /dev/null
@@@ -1,347 -1,0 +1,347 @@@
- fn check_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
 +use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a read and a write to the same variable where
 +    /// whether the read occurs before or after the write depends on the evaluation
 +    /// order of sub-expressions.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
 +    /// the operands of these expressions are evaluated before applying the effects of the expression.
 +    ///
 +    /// ### Known problems
 +    /// Code which intentionally depends on the evaluation
 +    /// order, or which is correct for any evaluation order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut x = 0;
 +    ///
 +    /// let a = {
 +    ///     x = 1;
 +    ///     1
 +    /// } + x;
 +    /// // Unclear whether a is 1 or 2.
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut x = 0;
 +    /// let tmp = {
 +    ///     x = 1;
 +    ///     1
 +    /// };
 +    /// let a = tmp + x;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MIXED_READ_WRITE_IN_EXPRESSION,
 +    restriction,
 +    "whether a variable read occurs before a write depends on sub-expression evaluation order"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for diverging calls that are not match arms or
 +    /// statements.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is often confusing to read. In addition, the
 +    /// sub-expression evaluation order for Rust is not well documented.
 +    ///
 +    /// ### Known problems
 +    /// Someone might want to use `some_bool || panic!()` as a
 +    /// shorthand.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # fn b() -> bool { true }
 +    /// # fn c() -> bool { true }
 +    /// let a = b() || panic!() || c();
 +    /// // `c()` is dead, `panic!()` is only called if `b()` returns `false`
 +    /// let x = (a, b, c, panic!());
 +    /// // can simply be replaced by `panic!()`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DIVERGING_SUB_EXPRESSION,
 +    complexity,
 +    "whether an expression contains a diverging sub expression"
 +}
 +
 +declare_lint_pass!(EvalOrderDependence => [MIXED_READ_WRITE_IN_EXPRESSION, DIVERGING_SUB_EXPRESSION]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Find a write to a local variable.
 +        let var = if_chain! {
 +            if let ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) = expr.kind;
 +            if let Some(var) = path_to_local(lhs);
 +            if expr.span.desugaring_kind().is_none();
 +            then { var } else { return; }
 +        };
 +        let mut visitor = ReadVisitor {
 +            cx,
 +            var,
 +            write_expr: expr,
 +            last_expr: expr,
 +        };
 +        check_for_unsequenced_reads(&mut visitor);
 +    }
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Local(local) => {
 +                if let Local { init: Some(e), .. } = local {
 +                    DivergenceVisitor { cx }.visit_expr(e);
 +                }
 +            },
 +            StmtKind::Expr(e) | StmtKind::Semi(e) => DivergenceVisitor { cx }.maybe_walk_expr(e),
 +            StmtKind::Item(..) => {},
 +        }
 +    }
 +}
 +
 +struct DivergenceVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
 +    fn maybe_walk_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Closure { .. } => {},
 +            ExprKind::Match(e, arms, _) => {
 +                self.visit_expr(e);
 +                for arm in arms {
 +                    if let Some(Guard::If(if_expr)) = arm.guard {
 +                        self.visit_expr(if_expr);
 +                    }
 +                    // make sure top level arm expressions aren't linted
 +                    self.maybe_walk_expr(arm.body);
 +                }
 +            },
 +            _ => walk_expr(self, e),
 +        }
 +    }
 +    fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
 +        span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges");
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        match e.kind {
 +            ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => self.report_diverging_sub_expr(e),
 +            ExprKind::Call(func, _) => {
 +                let typ = self.cx.typeck_results().expr_ty(func);
 +                match typ.kind() {
 +                    ty::FnDef(..) | ty::FnPtr(_) => {
 +                        let sig = typ.fn_sig(self.cx.tcx);
 +                        if self.cx.tcx.erase_late_bound_regions(sig).output().kind() == &ty::Never {
 +                            self.report_diverging_sub_expr(e);
 +                        }
 +                    },
 +                    _ => {},
 +                }
 +            },
 +            ExprKind::MethodCall(..) => {
 +                let borrowed_table = self.cx.typeck_results();
 +                if borrowed_table.expr_ty(e).is_never() {
 +                    self.report_diverging_sub_expr(e);
 +                }
 +            },
 +            _ => {
 +                // do not lint expressions referencing objects of type `!`, as that required a
 +                // diverging expression
 +                // to begin with
 +            },
 +        }
 +        self.maybe_walk_expr(e);
 +    }
 +    fn visit_block(&mut self, _: &'tcx Block<'_>) {
 +        // don't continue over blocks, LateLintPass already does that
 +    }
 +}
 +
 +/// Walks up the AST from the given write expression (`vis.write_expr`) looking
 +/// for reads to the same variable that are unsequenced relative to the write.
 +///
 +/// This means reads for which there is a common ancestor between the read and
 +/// the write such that
 +///
 +/// * evaluating the ancestor necessarily evaluates both the read and the write (for example, `&x`
 +///   and `|| x = 1` don't necessarily evaluate `x`), and
 +///
 +/// * which one is evaluated first depends on the order of sub-expression evaluation. Blocks, `if`s,
 +///   loops, `match`es, and the short-circuiting logical operators are considered to have a defined
 +///   evaluation order.
 +///
 +/// When such a read is found, the lint is triggered.
 +fn check_for_unsequenced_reads(vis: &mut ReadVisitor<'_, '_>) {
 +    let map = &vis.cx.tcx.hir();
 +    let mut cur_id = vis.write_expr.hir_id;
 +    loop {
 +        let parent_id = map.get_parent_node(cur_id);
 +        if parent_id == cur_id {
 +            break;
 +        }
 +        let Some(parent_node) = map.find(parent_id) else { break };
 +
 +        let stop_early = match parent_node {
 +            Node::Expr(expr) => check_expr(vis, expr),
 +            Node::Stmt(stmt) => check_stmt(vis, stmt),
 +            Node::Item(_) => {
 +                // We reached the top of the function, stop.
 +                break;
 +            },
 +            _ => StopEarly::KeepGoing,
 +        };
 +        match stop_early {
 +            StopEarly::Stop => break,
 +            StopEarly::KeepGoing => {},
 +        }
 +
 +        cur_id = parent_id;
 +    }
 +}
 +
 +/// Whether to stop early for the loop in `check_for_unsequenced_reads`. (If
 +/// `check_expr` weren't an independent function, this would be unnecessary and
 +/// we could just use `break`).
 +enum StopEarly {
 +    KeepGoing,
 +    Stop,
 +}
 +
- fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
++fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> StopEarly {
 +    if expr.hir_id == vis.last_expr.hir_id {
 +        return StopEarly::KeepGoing;
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Array(_)
 +        | ExprKind::Tup(_)
 +        | ExprKind::MethodCall(..)
 +        | ExprKind::Call(_, _)
 +        | ExprKind::Assign(..)
 +        | ExprKind::Index(_, _)
 +        | ExprKind::Repeat(_, _)
 +        | ExprKind::Struct(_, _, _) => {
 +            walk_expr(vis, expr);
 +        },
 +        ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
 +            if op.node == BinOpKind::And || op.node == BinOpKind::Or {
 +                // x && y and x || y always evaluate x first, so these are
 +                // strictly sequenced.
 +            } else {
 +                walk_expr(vis, expr);
 +            }
 +        },
 +        ExprKind::Closure { .. } => {
 +            // Either
 +            //
 +            // * `var` is defined in the closure body, in which case we've reached the top of the enclosing
 +            //   function and can stop, or
 +            //
 +            // * `var` is captured by the closure, in which case, because evaluating a closure does not evaluate
 +            //   its body, we don't necessarily have a write, so we need to stop to avoid generating false
 +            //   positives.
 +            //
 +            // This is also the only place we need to stop early (grrr).
 +            return StopEarly::Stop;
 +        },
 +        // All other expressions either have only one child or strictly
 +        // sequence the evaluation order of their sub-expressions.
 +        _ => {},
 +    }
 +
 +    vis.last_expr = expr;
 +
 +    StopEarly::KeepGoing
 +}
 +
++fn check_stmt<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, stmt: &'tcx Stmt<'_>) -> StopEarly {
 +    match stmt.kind {
 +        StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
 +        // If the declaration is of a local variable, check its initializer
 +        // expression if it has one. Otherwise, keep going.
 +        StmtKind::Local(local) => local
 +            .init
 +            .as_ref()
 +            .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
 +        StmtKind::Item(..) => StopEarly::KeepGoing,
 +    }
 +}
 +
 +/// A visitor that looks for reads from a variable.
 +struct ReadVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    /// The ID of the variable we're looking for.
 +    var: HirId,
 +    /// The expressions where the write to the variable occurred (for reporting
 +    /// in the lint).
 +    write_expr: &'tcx Expr<'tcx>,
 +    /// The last (highest in the AST) expression we've checked, so we know not
 +    /// to recheck it.
 +    last_expr: &'tcx Expr<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if expr.hir_id == self.last_expr.hir_id {
 +            return;
 +        }
 +
 +        if path_to_local_id(expr, self.var) {
 +            // Check that this is a read, not a write.
 +            if !is_in_assignment_position(self.cx, expr) {
 +                span_lint_and_note(
 +                    self.cx,
 +                    MIXED_READ_WRITE_IN_EXPRESSION,
 +                    expr.span,
 +                    &format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
 +                    Some(self.write_expr.span),
 +                    "whether read occurs before this write depends on evaluation order",
 +                );
 +            }
 +        }
 +        match expr.kind {
 +            // We're about to descend a closure. Since we don't know when (or
 +            // if) the closure will be evaluated, any reads in it might not
 +            // occur here (or ever). Like above, bail to avoid false positives.
 +            ExprKind::Closure{..} |
 +
 +            // We want to avoid a false positive when a variable name occurs
 +            // only to have its address taken, so we stop here. Technically,
 +            // this misses some weird cases, eg.
 +            //
 +            // ```rust
 +            // let mut x = 0;
 +            // let a = foo(&{x = 1; x}, x);
 +            // ```
 +            //
 +            // TODO: fix this
 +            ExprKind::AddrOf(_, _, _) => {
 +                return;
 +            }
 +            _ => {}
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
 +
 +/// Returns `true` if `expr` is the LHS of an assignment, like `expr = ...`.
 +fn is_in_assignment_position(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::Assign(lhs, ..) = parent.kind {
 +            return lhs.hir_id == expr.hir_id;
 +        }
 +    }
 +    false
 +}
index 4b62dcdffe2fcf68a2481cac8bdc6af55b19c817,0000000000000000000000000000000000000000..a651020ca6566341d086bb723d4a85af68c1187e
mode 100644,000000..100644
--- /dev/null
@@@ -1,177 -1,0 +1,204 @@@
- use clippy_utils::trait_ref_of_method;
 +use clippy_utils::diagnostics::span_lint;
- use rustc_session::{declare_lint_pass, declare_tool_lint};
++use clippy_utils::{def_path_def_ids, trait_ref_of_method};
++use rustc_data_structures::fx::FxHashSet;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::TypeVisitable;
 +use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
- declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
++use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::sym;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for sets/maps with mutable key types.
 +    ///
 +    /// ### Why is this bad?
 +    /// All of `HashMap`, `HashSet`, `BTreeMap` and
 +    /// `BtreeSet` rely on either the hash or the order of keys be unchanging,
 +    /// so having types with interior mutability is a bad idea.
 +    ///
 +    /// ### Known problems
 +    ///
 +    /// #### False Positives
 +    /// It's correct to use a struct that contains interior mutability as a key, when its
 +    /// implementation of `Hash` or `Ord` doesn't access any of the interior mutable types.
 +    /// However, this lint is unable to recognize this, so it will often cause false positives in
 +    /// theses cases.  The `bytes` crate is a great example of this.
 +    ///
 +    /// #### False Negatives
 +    /// For custom `struct`s/`enum`s, this lint is unable to check for interior mutability behind
 +    /// indirection.  For example, `struct BadKey<'a>(&'a Cell<usize>)` will be seen as immutable
 +    /// and cause a false negative if its implementation of `Hash`/`Ord` accesses the `Cell`.
 +    ///
 +    /// This lint does check a few cases for indirection.  Firstly, using some standard library
 +    /// types (`Option`, `Result`, `Box`, `Rc`, `Arc`, `Vec`, `VecDeque`, `BTreeMap` and
 +    /// `BTreeSet`) directly as keys (e.g. in `HashMap<Box<Cell<usize>>, ()>`) **will** trigger the
 +    /// lint, because the impls of `Hash`/`Ord` for these types directly call `Hash`/`Ord` on their
 +    /// contained type.
 +    ///
 +    /// Secondly, the implementations of `Hash` and `Ord` for raw pointers (`*const T` or `*mut T`)
 +    /// apply only to the **address** of the contained value.  Therefore, interior mutability
 +    /// behind raw pointers (e.g. in `HashSet<*mut Cell<usize>>`) can't impact the value of `Hash`
 +    /// or `Ord`, and therefore will not trigger this link.  For more info, see issue
 +    /// [#6745](https://github.com/rust-lang/rust-clippy/issues/6745).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::cmp::{PartialEq, Eq};
 +    /// use std::collections::HashSet;
 +    /// use std::hash::{Hash, Hasher};
 +    /// use std::sync::atomic::AtomicUsize;
 +    ///# #[allow(unused)]
 +    ///
 +    /// struct Bad(AtomicUsize);
 +    /// impl PartialEq for Bad {
 +    ///     fn eq(&self, rhs: &Self) -> bool {
 +    ///          ..
 +    /// ; unimplemented!();
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Eq for Bad {}
 +    ///
 +    /// impl Hash for Bad {
 +    ///     fn hash<H: Hasher>(&self, h: &mut H) {
 +    ///         ..
 +    /// ; unimplemented!();
 +    ///     }
 +    /// }
 +    ///
 +    /// fn main() {
 +    ///     let _: HashSet<Bad> = HashSet::new();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub MUTABLE_KEY_TYPE,
 +    suspicious,
 +    "Check for mutable `Map`/`Set` key type"
 +}
 +
-             check_sig(cx, item.hir_id(), sig.decl);
++#[derive(Clone)]
++pub struct MutableKeyType {
++    ignore_interior_mutability: Vec<String>,
++    ignore_mut_def_ids: FxHashSet<hir::def_id::DefId>,
++}
++
++impl_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
++    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
++        self.ignore_mut_def_ids.clear();
++        let mut path = Vec::new();
++        for ty in &self.ignore_interior_mutability {
++            path.extend(ty.split("::"));
++            for id in def_path_def_ids(cx, &path[..]) {
++                self.ignore_mut_def_ids.insert(id);
++            }
++            path.clear();
++        }
++    }
++
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
 +        if let hir::ItemKind::Fn(ref sig, ..) = item.kind {
-                 check_sig(cx, item.hir_id(), sig.decl);
++            self.check_sig(cx, item.hir_id(), sig.decl);
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'tcx>) {
 +        if let hir::ImplItemKind::Fn(ref sig, ..) = item.kind {
 +            if trait_ref_of_method(cx, item.owner_id.def_id).is_none() {
-             check_sig(cx, item.hir_id(), sig.decl);
++                self.check_sig(cx, item.hir_id(), sig.decl);
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'tcx>) {
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
-         check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat));
++            self.check_sig(cx, item.hir_id(), sig.decl);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
 +        if let hir::PatKind::Wild = local.pat.kind {
 +            return;
 +        }
- fn check_sig<'tcx>(cx: &LateContext<'tcx>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
-     let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
-     let fn_sig = cx.tcx.fn_sig(fn_def_id);
-     for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
-         check_ty(cx, hir_ty.span, *ty);
++        self.check_ty_(cx, local.span, cx.typeck_results().pat_ty(local.pat));
 +    }
 +}
 +
-     check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
- }
++impl MutableKeyType {
++    pub fn new(ignore_interior_mutability: Vec<String>) -> Self {
++        Self {
++            ignore_interior_mutability,
++            ignore_mut_def_ids: FxHashSet::default(),
++        }
 +    }
- // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
- // generics (because the compiler cannot ensure immutability for unknown types).
- fn check_ty<'tcx>(cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
-     let ty = ty.peel_refs();
-     if let Adt(def, substs) = ty.kind() {
-         let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
-             .iter()
-             .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
-         if is_keyed_type && is_interior_mutable_type(cx, substs.type_at(0), span) {
-             span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
 +
- }
++    fn check_sig(&self, cx: &LateContext<'_>, item_hir_id: hir::HirId, decl: &hir::FnDecl<'_>) {
++        let fn_def_id = cx.tcx.hir().local_def_id(item_hir_id);
++        let fn_sig = cx.tcx.fn_sig(fn_def_id);
++        for (hir_ty, ty) in iter::zip(decl.inputs, fn_sig.inputs().skip_binder()) {
++            self.check_ty_(cx, hir_ty.span, *ty);
 +        }
++        self.check_ty_(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
 +    }
- /// Determines if a type contains interior mutability which would affect its implementation of
- /// [`Hash`] or [`Ord`].
- fn is_interior_mutable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span) -> bool {
-     match *ty.kind() {
-         Ref(_, inner_ty, mutbl) => {
-             mutbl == hir::Mutability::Mut || is_interior_mutable_type(cx, inner_ty, span)
-         }
-         Slice(inner_ty) => is_interior_mutable_type(cx, inner_ty, span),
-         Array(inner_ty, size) => {
-             size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
-                 && is_interior_mutable_type(cx, inner_ty, span)
-         }
-         Tuple(fields) => fields.iter().any(|ty| is_interior_mutable_type(cx, ty, span)),
-         Adt(def, substs) => {
-             // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
-             // that of their type parameters.  Note: we don't include `HashSet` and `HashMap`
-             // because they have no impl for `Hash` or `Ord`.
-             let is_std_collection = [
-                 sym::Option,
-                 sym::Result,
-                 sym::LinkedList,
-                 sym::Vec,
-                 sym::VecDeque,
-                 sym::BTreeMap,
-                 sym::BTreeSet,
-                 sym::Rc,
-                 sym::Arc,
-             ]
-             .iter()
-             .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
-             let is_box = Some(def.did()) == cx.tcx.lang_items().owned_box();
-             if is_std_collection || is_box {
-                 // The type is mutable if any of its type parameters are
-                 substs.types().any(|ty| is_interior_mutable_type(cx, ty, span))
-             } else {
-                 !ty.has_escaping_bound_vars()
-                     && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
-                     && !ty.is_freeze(cx.tcx, cx.param_env)
 +
-         _ => false,
++    // We want to lint 1. sets or maps with 2. not immutable key types and 3. no unerased
++    // generics (because the compiler cannot ensure immutability for unknown types).
++    fn check_ty_<'tcx>(&self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
++        let ty = ty.peel_refs();
++        if let Adt(def, substs) = ty.kind() {
++            let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
++                .iter()
++                .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
++            if is_keyed_type && self.is_interior_mutable_type(cx, substs.type_at(0)) {
++                span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");
 +            }
 +        }
++    }
++
++    /// Determines if a type contains interior mutability which would affect its implementation of
++    /// [`Hash`] or [`Ord`].
++    fn is_interior_mutable_type<'tcx>(&self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
++        match *ty.kind() {
++            Ref(_, inner_ty, mutbl) => mutbl == hir::Mutability::Mut || self.is_interior_mutable_type(cx, inner_ty),
++            Slice(inner_ty) => self.is_interior_mutable_type(cx, inner_ty),
++            Array(inner_ty, size) => {
++                size.try_eval_usize(cx.tcx, cx.param_env).map_or(true, |u| u != 0)
++                    && self.is_interior_mutable_type(cx, inner_ty)
++            },
++            Tuple(fields) => fields.iter().any(|ty| self.is_interior_mutable_type(cx, ty)),
++            Adt(def, substs) => {
++                // Special case for collections in `std` who's impl of `Hash` or `Ord` delegates to
++                // that of their type parameters.  Note: we don't include `HashSet` and `HashMap`
++                // because they have no impl for `Hash` or `Ord`.
++                let def_id = def.did();
++                let is_std_collection = [
++                    sym::Option,
++                    sym::Result,
++                    sym::LinkedList,
++                    sym::Vec,
++                    sym::VecDeque,
++                    sym::BTreeMap,
++                    sym::BTreeSet,
++                    sym::Rc,
++                    sym::Arc,
++                ]
++                .iter()
++                .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def_id));
++                let is_box = Some(def_id) == cx.tcx.lang_items().owned_box();
++                if is_std_collection || is_box || self.ignore_mut_def_ids.contains(&def_id) {
++                    // The type is mutable if any of its type parameters are
++                    substs.types().any(|ty| self.is_interior_mutable_type(cx, ty))
++                } else {
++                    !ty.has_escaping_bound_vars()
++                        && cx.tcx.layout_of(cx.param_env.and(ty)).is_ok()
++                        && !ty.is_freeze(cx.tcx, cx.param_env)
++                }
++            },
++            _ => false,
++        }
 +    }
 +}
index cb16f00047a394b06b147151fd0244f9bde86274,0000000000000000000000000000000000000000..bc90e131b7f3be65b1ff008599796c50d5da7f7f
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,116 @@@
-             } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
-                 span_lint(
-                     self.cx,
-                     MUT_MUT,
-                     expr.span,
-                     "this expression mutably borrows a mutable reference. Consider reborrowing",
-                 );
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::higher;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `mut mut` references.
 +    ///
 +    /// ### Why is this bad?
 +    /// Multiple `mut`s don't add anything meaningful to the
 +    /// source. This is either a copy'n'paste error, or it shows a fundamental
 +    /// misunderstanding of references.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut y = 1;
 +    /// let x = &mut &mut y;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MUT_MUT,
 +    pedantic,
 +    "usage of double-mut refs, e.g., `&mut &mut ...`"
 +}
 +
 +declare_lint_pass!(MutMut => [MUT_MUT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MutMut {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        intravisit::walk_block(&mut MutVisitor { cx }, block);
 +    }
 +
 +    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) {
 +        use rustc_hir::intravisit::Visitor;
 +
 +        MutVisitor { cx }.visit_ty(ty);
 +    }
 +}
 +
 +pub struct MutVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        if in_external_macro(self.cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let Some(higher::ForLoop { arg, body, .. }) = higher::ForLoop::hir(expr) {
 +            // A `for` loop lowers to:
 +            // ```rust
 +            // match ::std::iter::Iterator::next(&mut iter) {
 +            // //                                ^^^^
 +            // ```
 +            // Let's ignore the generated code.
 +            intravisit::walk_expr(self, arg);
 +            intravisit::walk_expr(self, body);
 +        } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind {
 +            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
 +                span_lint(
 +                    self.cx,
 +                    MUT_MUT,
 +                    expr.span,
 +                    "generally you want to avoid `&mut &mut _` if possible",
 +                );
++            } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
++                if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) {
++                    span_lint(
++                        self.cx,
++                        MUT_MUT,
++                        expr.span,
++                        "this expression mutably borrows a mutable reference. Consider reborrowing",
++                    );
++                }
 +            }
 +        }
 +    }
 +
 +    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
 +        if in_external_macro(self.cx.sess(), ty.span) {
 +            return;
 +        }
 +
 +        if let hir::TyKind::Rptr(
 +            _,
 +            hir::MutTy {
 +                ty: pty,
 +                mutbl: hir::Mutability::Mut,
 +            },
 +        ) = ty.kind
 +        {
 +            if let hir::TyKind::Rptr(
 +                _,
 +                hir::MutTy {
 +                    mutbl: hir::Mutability::Mut,
 +                    ..
 +                },
 +            ) = pty.kind
 +            {
 +                span_lint(
 +                    self.cx,
 +                    MUT_MUT,
 +                    ty.span,
 +                    "generally you want to avoid `&mut &mut _` if possible",
 +                );
 +            }
 +        }
 +
 +        intravisit::walk_ty(self, ty);
 +    }
 +}
index 10c3ff026b6d66100b3bee5a9b27d7c25799c184,0000000000000000000000000000000000000000..498e1408e52a064836a713f5d184f5a5ad1bd64d
mode 100644,000000..100644
--- /dev/null
@@@ -1,124 -1,0 +1,154 @@@
-     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
-         if pat.span.from_expansion() {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that needlessly destructure a reference and borrow the inner
 +    /// value with `&ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This pattern has no effect in almost all cases.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut v = Vec::<String>::new();
 +    /// v.iter_mut().filter(|&ref a| a.is_empty());
 +    ///
 +    /// if let &[ref first, ref second] = v.as_slice() {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut v = Vec::<String>::new();
 +    /// v.iter_mut().filter(|a| a.is_empty());
 +    ///
 +    /// if let [first, second] = v.as_slice() {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_BORROWED_REFERENCE,
 +    complexity,
 +    "destructuring a reference and borrowing the inner value"
 +}
 +
 +declare_lint_pass!(NeedlessBorrowedRef => [NEEDLESS_BORROWED_REFERENCE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowedRef {
-         for (_, node) in cx.tcx.hir().parent_iter(pat.hir_id) {
++    fn check_pat(&mut self, cx: &LateContext<'tcx>, ref_pat: &'tcx Pat<'_>) {
++        if ref_pat.span.from_expansion() {
 +            // OK, simple enough, lints doesn't check in macro.
 +            return;
 +        }
 +
 +        // Do not lint patterns that are part of an OR `|` pattern, the binding mode must match in all arms
-         let PatKind::Ref(sub_pat, Mutability::Not) = pat.kind else { return };
++        for (_, node) in cx.tcx.hir().parent_iter(ref_pat.hir_id) {
 +            let Node::Pat(pat) = node else { break };
 +
 +            if matches!(pat.kind, PatKind::Or(_)) {
 +                return;
 +            }
 +        }
 +
 +        // Only lint immutable refs, because `&mut ref T` may be useful.
-         match sub_pat.kind {
++        let PatKind::Ref(pat, Mutability::Not) = ref_pat.kind else { return };
 +
-                     pat.span,
++        match pat.kind {
 +            // Check sub_pat got a `ref` keyword (excluding `ref mut`).
 +            PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
 +                span_lint_and_then(
 +                    cx,
 +                    NEEDLESS_BORROWED_REFERENCE,
-                         let span = pat.span.until(ident.span);
++                    ref_pat.span,
 +                    "this pattern takes a reference on something that is being dereferenced",
 +                    |diag| {
 +                        // `&ref ident`
 +                        //  ^^^^^
-                 let mut suggestions = Vec::new();
-                 for element_pat in itertools::chain(before, after) {
-                     if let PatKind::Binding(BindingAnnotation::REF, _, ident, None) = element_pat.kind {
-                         // `&[..., ref ident, ...]`
-                         //         ^^^^
-                         let span = element_pat.span.until(ident.span);
-                         suggestions.push((span, String::new()));
-                     } else {
-                         return;
-                     }
-                 }
++                        let span = ref_pat.span.until(ident.span);
 +                        diag.span_suggestion_verbose(
 +                            span,
 +                            "try removing the `&ref` part",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +            // Slices where each element is `ref`: `&[ref a, ref b, ..., ref z]`
 +            PatKind::Slice(
 +                before,
 +                None
 +                | Some(Pat {
 +                    kind: PatKind::Wild, ..
 +                }),
 +                after,
 +            ) => {
-                 if !suggestions.is_empty() {
-                     span_lint_and_then(
-                         cx,
-                         NEEDLESS_BORROWED_REFERENCE,
-                         pat.span,
-                         "dereferencing a slice pattern where every element takes a reference",
-                         |diag| {
-                             // `&[...]`
-                             //  ^
-                             let span = pat.span.until(sub_pat.span);
-                             suggestions.push((span, String::new()));
++                check_subpatterns(
++                    cx,
++                    "dereferencing a slice pattern where every element takes a reference",
++                    ref_pat,
++                    pat,
++                    itertools::chain(before, after),
++                );
++            },
++            PatKind::Tuple(subpatterns, _) | PatKind::TupleStruct(_, subpatterns, _) => {
++                check_subpatterns(
++                    cx,
++                    "dereferencing a tuple pattern where every element takes a reference",
++                    ref_pat,
++                    pat,
++                    subpatterns,
++                );
++            },
++            PatKind::Struct(_, fields, _) => {
++                check_subpatterns(
++                    cx,
++                    "dereferencing a struct pattern where every field's pattern takes a reference",
++                    ref_pat,
++                    pat,
++                    fields.iter().map(|field| field.pat),
++                );
++            },
++            _ => {},
++        }
++    }
++}
 +
-                             diag.multipart_suggestion(
-                                 "try removing the `&` and `ref` parts",
-                                 suggestions,
-                                 Applicability::MachineApplicable,
-                             );
-                         },
-                     );
-                 }
++fn check_subpatterns<'tcx>(
++    cx: &LateContext<'tcx>,
++    message: &str,
++    ref_pat: &Pat<'_>,
++    pat: &Pat<'_>,
++    subpatterns: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
++) {
++    let mut suggestions = Vec::new();
 +
-             _ => {},
++    for subpattern in subpatterns {
++        match subpattern.kind {
++            PatKind::Binding(BindingAnnotation::REF, _, ident, None) => {
++                // `ref ident`
++                //  ^^^^
++                let span = subpattern.span.until(ident.span);
++                suggestions.push((span, String::new()));
 +            },
++            PatKind::Wild => {},
++            _ => return,
 +        }
 +    }
++
++    if !suggestions.is_empty() {
++        span_lint_and_then(cx, NEEDLESS_BORROWED_REFERENCE, ref_pat.span, message, |diag| {
++            // `&pat`
++            //  ^
++            let span = ref_pat.span.until(pat.span);
++            suggestions.push((span, String::new()));
++
++            diag.multipart_suggestion(
++                "try removing the `&` and `ref` parts",
++                suggestions,
++                Applicability::MachineApplicable,
++            );
++        });
++    }
 +}
index 6f0e755466e5a4524099df8f37f6954fddbd8ab3,0000000000000000000000000000000000000000..38a75034cd314857f0c1489a1def0cdaf5db7007
mode 100644,000000..100644
--- /dev/null
@@@ -1,473 -1,0 +1,473 @@@
- fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
 +//! Checks for continue statements in loops that are redundant.
 +//!
 +//! For example, the lint would catch
 +//!
 +//! ```rust
 +//! let mut a = 1;
 +//! let x = true;
 +//!
 +//! while a < 5 {
 +//!     a = 6;
 +//!     if x {
 +//!         // ...
 +//!     } else {
 +//!         continue;
 +//!     }
 +//!     println!("Hello, world");
 +//! }
 +//! ```
 +//!
 +//! And suggest something like this:
 +//!
 +//! ```rust
 +//! let mut a = 1;
 +//! let x = true;
 +//!
 +//! while a < 5 {
 +//!     a = 6;
 +//!     if x {
 +//!         // ...
 +//!         println!("Hello, world");
 +//!     }
 +//! }
 +//! ```
 +//!
 +//! This lint is **warn** by default.
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::source::{indent_of, snippet, snippet_block};
 +use rustc_ast::ast;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// The lint checks for `if`-statements appearing in loops
 +    /// that contain a `continue` statement in either their main blocks or their
 +    /// `else`-blocks, when omitting the `else`-block possibly with some
 +    /// rearrangement of code can make the code easier to understand.
 +    ///
 +    /// ### Why is this bad?
 +    /// Having explicit `else` blocks for `if` statements
 +    /// containing `continue` in their THEN branch adds unnecessary branching and
 +    /// nesting to the code. Having an else block containing just `continue` can
 +    /// also be better written by grouping the statements following the whole `if`
 +    /// statement within the THEN block and omitting the else block completely.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn condition() -> bool { false }
 +    /// # fn update_condition() {}
 +    /// # let x = false;
 +    /// while condition() {
 +    ///     update_condition();
 +    ///     if x {
 +    ///         // ...
 +    ///     } else {
 +    ///         continue;
 +    ///     }
 +    ///     println!("Hello, world");
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be rewritten as
 +    ///
 +    /// ```rust
 +    /// # fn condition() -> bool { false }
 +    /// # fn update_condition() {}
 +    /// # let x = false;
 +    /// while condition() {
 +    ///     update_condition();
 +    ///     if x {
 +    ///         // ...
 +    ///         println!("Hello, world");
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// As another example, the following code
 +    ///
 +    /// ```rust
 +    /// # fn waiting() -> bool { false }
 +    /// loop {
 +    ///     if waiting() {
 +    ///         continue;
 +    ///     } else {
 +    ///         // Do something useful
 +    ///     }
 +    ///     # break;
 +    /// }
 +    /// ```
 +    /// Could be rewritten as
 +    ///
 +    /// ```rust
 +    /// # fn waiting() -> bool { false }
 +    /// loop {
 +    ///     if waiting() {
 +    ///         continue;
 +    ///     }
 +    ///     // Do something useful
 +    ///     # break;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_CONTINUE,
 +    pedantic,
 +    "`continue` statements that can be replaced by a rearrangement of code"
 +}
 +
 +declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);
 +
 +impl EarlyLintPass for NeedlessContinue {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if !expr.span.from_expansion() {
 +            check_and_warn(cx, expr);
 +        }
 +    }
 +}
 +
 +/* This lint has to mainly deal with two cases of needless continue
 + * statements. */
 +// Case 1 [Continue inside else block]:
 +//
 +//     loop {
 +//         // region A
 +//         if cond {
 +//             // region B
 +//         } else {
 +//             continue;
 +//         }
 +//         // region C
 +//     }
 +//
 +// This code can better be written as follows:
 +//
 +//     loop {
 +//         // region A
 +//         if cond {
 +//             // region B
 +//             // region C
 +//         }
 +//     }
 +//
 +// Case 2 [Continue inside then block]:
 +//
 +//     loop {
 +//       // region A
 +//       if cond {
 +//           continue;
 +//           // potentially more code here.
 +//       } else {
 +//           // region B
 +//       }
 +//       // region C
 +//     }
 +//
 +//
 +// This snippet can be refactored to:
 +//
 +//     loop {
 +//       // region A
 +//       if !cond {
 +//           // region B
 +//           // region C
 +//       }
 +//     }
 +//
 +
 +/// Given an expression, returns true if either of the following is true
 +///
 +/// - The expression is a `continue` node.
 +/// - The expression node is a block with the first statement being a
 +/// `continue`.
 +fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
 +    match else_expr.kind {
 +        ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
 +        ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
 +        _ => false,
 +    }
 +}
 +
 +fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
 +    block.stmts.get(0).map_or(false, |stmt| match stmt.kind {
 +        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
 +            if let ast::ExprKind::Continue(ref l) = e.kind {
 +                compare_labels(label, l.as_ref())
 +            } else {
 +                false
 +            }
 +        },
 +        _ => false,
 +    })
 +}
 +
 +/// If the `continue` has a label, check it matches the label of the loop.
 +fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool {
 +    match (loop_label, continue_label) {
 +        // `loop { continue; }` or `'a loop { continue; }`
 +        (_, None) => true,
 +        // `loop { continue 'a; }`
 +        (None, _) => false,
 +        // `'a loop { continue 'a; }` or `'a loop { continue 'b; }`
 +        (Some(x), Some(y)) => x.ident == y.ident,
 +    }
 +}
 +
 +/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
 +/// the AST object representing the loop block of `expr`.
 +fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
 +where
 +    F: FnMut(&ast::Block, Option<&ast::Label>),
 +{
 +    if let ast::ExprKind::While(_, loop_block, label)
 +    | ast::ExprKind::ForLoop(_, _, loop_block, label)
 +    | ast::ExprKind::Loop(loop_block, label, ..) = &expr.kind
 +    {
 +        func(loop_block, label.as_ref());
 +    }
 +}
 +
 +/// If `stmt` is an if expression node with an `else` branch, calls func with
 +/// the
 +/// following:
 +///
 +/// - The `if` expression itself,
 +/// - The `if` condition expression,
 +/// - The `then` block, and
 +/// - The `else` expression.
 +fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
 +where
 +    F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
 +{
 +    match stmt.kind {
 +        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
 +            if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind {
 +                func(e, cond, if_block, else_expr);
 +            }
 +        },
 +        _ => {},
 +    }
 +}
 +
 +/// A type to distinguish between the two distinct cases this lint handles.
 +#[derive(Copy, Clone, Debug)]
 +enum LintType {
 +    ContinueInsideElseBlock,
 +    ContinueInsideThenBlock,
 +}
 +
 +/// Data we pass around for construction of help messages.
 +struct LintData<'a> {
 +    /// The `if` expression encountered in the above loop.
 +    if_expr: &'a ast::Expr,
 +    /// The condition expression for the above `if`.
 +    if_cond: &'a ast::Expr,
 +    /// The `then` block of the `if` statement.
 +    if_block: &'a ast::Block,
 +    /// The `else` block of the `if` statement.
 +    /// Note that we only work with `if` exprs that have an `else` branch.
 +    else_expr: &'a ast::Expr,
 +    /// The 0-based index of the `if` statement in the containing loop block.
 +    stmt_idx: usize,
 +    /// The statements of the loop block.
 +    loop_block: &'a ast::Block,
 +}
 +
 +const MSG_REDUNDANT_CONTINUE_EXPRESSION: &str = "this `continue` expression is redundant";
 +
 +const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";
 +
 +const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
 +                                         expression";
 +
 +const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \
 +                                             follows (in the loop) with the `if` block";
 +
 +const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";
 +
 +const DROP_CONTINUE_EXPRESSION_MSG: &str = "consider dropping the `continue` expression";
 +
- fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
++fn emit_warning(cx: &EarlyContext<'_>, data: &LintData<'_>, header: &str, typ: LintType) {
 +    // snip    is the whole *help* message that appears after the warning.
 +    // message is the warning message.
 +    // expr    is the expression which the lint warning message refers to.
 +    let (snip, message, expr) = match typ {
 +        LintType::ContinueInsideElseBlock => (
 +            suggestion_snippet_for_continue_inside_else(cx, data),
 +            MSG_REDUNDANT_ELSE_BLOCK,
 +            data.else_expr,
 +        ),
 +        LintType::ContinueInsideThenBlock => (
 +            suggestion_snippet_for_continue_inside_if(cx, data),
 +            MSG_ELSE_BLOCK_NOT_NEEDED,
 +            data.if_expr,
 +        ),
 +    };
 +    span_lint_and_help(
 +        cx,
 +        NEEDLESS_CONTINUE,
 +        expr.span,
 +        message,
 +        None,
 +        &format!("{header}\n{snip}"),
 +    );
 +}
 +
- fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
++fn suggestion_snippet_for_continue_inside_if(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String {
 +    let cond_code = snippet(cx, data.if_cond.span, "..");
 +
 +    let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));
 +
 +    let else_code = snippet_block(cx, data.else_expr.span, "..", Some(data.if_expr.span));
 +
 +    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
 +    format!(
 +        "{indent}if {cond_code} {continue_code}\n{indent}{else_code}",
 +        indent = " ".repeat(indent_if),
 +    )
 +}
 +
- fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) {
++fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &LintData<'_>) -> String {
 +    let cond_code = snippet(cx, data.if_cond.span, "..");
 +
 +    // Region B
 +    let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)));
 +
 +    // Region C
 +    // These is the code in the loop block that follows the if/else construction
 +    // we are complaining about. We want to pull all of this code into the
 +    // `then` block of the `if` statement.
 +    let indent = span_of_first_expr_in_block(data.if_block)
 +        .and_then(|span| indent_of(cx, span))
 +        .unwrap_or(0);
 +    let to_annex = data.loop_block.stmts[data.stmt_idx + 1..]
 +        .iter()
 +        .map(|stmt| {
 +            let span = cx.sess().source_map().stmt_span(stmt.span, data.loop_block.span);
 +            let snip = snippet_block(cx, span, "..", None).into_owned();
 +            snip.lines()
 +                .map(|line| format!("{}{line}", " ".repeat(indent)))
 +                .collect::<Vec<_>>()
 +                .join("\n")
 +        })
 +        .collect::<Vec<_>>()
 +        .join("\n");
 +
 +    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
 +    format!(
 +        "{indent_if}if {cond_code} {block_code}\n{indent}// merged code follows:\n{to_annex}\n{indent_if}}}",
 +        indent = " ".repeat(indent),
 +        indent_if = " ".repeat(indent_if),
 +    )
 +}
 +
++fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +    if_chain! {
 +        if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind;
 +        if let Some(last_stmt) = loop_block.stmts.last();
 +        if let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind;
 +        if let ast::ExprKind::Continue(_) = inner_expr.kind;
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                NEEDLESS_CONTINUE,
 +                last_stmt.span,
 +                MSG_REDUNDANT_CONTINUE_EXPRESSION,
 +                None,
 +                DROP_CONTINUE_EXPRESSION_MSG,
 +            );
 +        }
 +    }
 +    with_loop_block(expr, |loop_block, label| {
 +        for (i, stmt) in loop_block.stmts.iter().enumerate() {
 +            with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
 +                let data = &LintData {
 +                    stmt_idx: i,
 +                    if_expr,
 +                    if_cond: cond,
 +                    if_block: then_block,
 +                    else_expr,
 +                    loop_block,
 +                };
 +                if needless_continue_in_else(else_expr, label) {
 +                    emit_warning(
 +                        cx,
 +                        data,
 +                        DROP_ELSE_BLOCK_AND_MERGE_MSG,
 +                        LintType::ContinueInsideElseBlock,
 +                    );
 +                } else if is_first_block_stmt_continue(then_block, label) {
 +                    emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
 +                }
 +            });
 +        }
 +    });
 +}
 +
 +/// Eats at `s` from the end till a closing brace `}` is encountered, and then continues eating
 +/// till a non-whitespace character is found.  e.g., the string. If no closing `}` is present, the
 +/// string will be preserved.
 +///
 +/// ```rust
 +/// {
 +///     let x = 5;
 +/// }
 +/// ```
 +///
 +/// is transformed to
 +///
 +/// ```text
 +///     {
 +///         let x = 5;
 +/// ```
 +#[must_use]
 +fn erode_from_back(s: &str) -> String {
 +    let mut ret = s.to_string();
 +    while ret.pop().map_or(false, |c| c != '}') {}
 +    while let Some(c) = ret.pop() {
 +        if !c.is_whitespace() {
 +            ret.push(c);
 +            break;
 +        }
 +    }
 +    if ret.is_empty() { s.to_string() } else { ret }
 +}
 +
 +fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
 +    block.stmts.get(0).map(|stmt| stmt.span)
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::erode_from_back;
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_erode_from_back() {
 +        let input = "\
 +{
 +    let x = 5;
 +    let y = format!(\"{}\", 42);
 +}";
 +
 +        let expected = "\
 +{
 +    let x = 5;
 +    let y = format!(\"{}\", 42);";
 +
 +        let got = erode_from_back(input);
 +        assert_eq!(expected, got);
 +    }
 +
 +    #[test]
 +    #[rustfmt::skip]
 +    fn test_erode_from_back_no_brace() {
 +        let input = "\
 +let x = 5;
 +let y = something();
 +";
 +        let expected = input;
 +        let got = erode_from_back(input);
 +        assert_eq!(expected, got);
 +    }
 +}
index 79aa15b06ef4d4abf97c94ebeef4b35e9dc15b4f,0000000000000000000000000000000000000000..2ef902965f66d4db276533355a4706eac991baeb
mode 100644,000000..100644
--- /dev/null
@@@ -1,350 -1,0 +1,344 @@@
-     fn fake_read(
-         &mut self,
-         _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
-         _: FakeReadCause,
-         _: HirId,
-     ) {
-     }
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::ptr::get_spans;
 +use clippy_utils::source::{snippet, snippet_opt};
 +use clippy_utils::ty::{implements_trait, 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::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(cx, ty, t, &[]));
 +                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 2a7159764e4637007bd8be353ca33a7d0a2343b7,0000000000000000000000000000000000000000..ae0a41db918a3447067342479161c62939e71e9e
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
-                 check_lit(cx, &token_lit, expr.span, true);
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_ast::ast::{Expr, ExprKind};
 +use rustc_ast::token::{Lit, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +use std::fmt::Write;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `\0` escapes in string and byte literals that look like octal
 +    /// character escapes in C.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// C and other languages support octal character escapes in strings, where
 +    /// a backslash is followed by up to three octal digits. For example, `\033`
 +    /// stands for the ASCII character 27 (ESC). Rust does not support this
 +    /// notation, but has the escape code `\0` which stands for a null
 +    /// byte/character, and any following digits do not form part of the escape
 +    /// sequence. Therefore, `\033` is not a compiler error but the result may
 +    /// be surprising.
 +    ///
 +    /// ### Known problems
 +    /// The actual meaning can be the intended one. `\x00` can be used in these
 +    /// cases to be unambiguous.
 +    ///
 +    /// The lint does not trigger for format strings in `print!()`, `write!()`
 +    /// and friends since the string is already preprocessed when Clippy lints
 +    /// can see it.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
 +    /// let two = "\033\0";                 // \033 intended as null-3-3
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let one = "\x1b[1mWill this be bold?\x1b[0m";
 +    /// let two = "\x0033\x00";
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub OCTAL_ESCAPES,
 +    suspicious,
 +    "string escape sequences looking like octal characters"
 +}
 +
 +declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]);
 +
 +impl EarlyLintPass for OctalEscapes {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Lit(token_lit) = &expr.kind {
 +            if matches!(token_lit.kind, LitKind::Str) {
-                 check_lit(cx, &token_lit, expr.span, false);
++                check_lit(cx, token_lit, expr.span, true);
 +            } else if matches!(token_lit.kind, LitKind::ByteStr) {
++                check_lit(cx, token_lit, expr.span, false);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) {
 +    let contents = lit.symbol.as_str();
 +    let mut iter = contents.char_indices().peekable();
 +    let mut found = vec![];
 +
 +    // go through the string, looking for \0[0-7][0-7]?
 +    while let Some((from, ch)) = iter.next() {
 +        if ch == '\\' {
 +            if let Some((_, '0')) = iter.next() {
 +                // collect up to two further octal digits
 +                if let Some((mut to, '0'..='7')) = iter.next() {
 +                    if let Some((_, '0'..='7')) = iter.peek() {
 +                        to += 1;
 +                    }
 +                    found.push((from, to + 1));
 +                }
 +            }
 +        }
 +    }
 +
 +    if found.is_empty() {
 +        return;
 +    }
 +
 +    // construct two suggestion strings, one with \x escapes with octal meaning
 +    // as in C, and one with \x00 for null bytes.
 +    let mut suggest_1 = if is_string { "\"" } else { "b\"" }.to_string();
 +    let mut suggest_2 = suggest_1.clone();
 +    let mut index = 0;
 +    for (from, to) in found {
 +        suggest_1.push_str(&contents[index..from]);
 +        suggest_2.push_str(&contents[index..from]);
 +
 +        // construct a replacement escape
 +        // the maximum value is \077, or \x3f, so u8 is sufficient here
 +        if let Ok(n) = u8::from_str_radix(&contents[from + 1..to], 8) {
 +            write!(suggest_1, "\\x{n:02x}").unwrap();
 +        }
 +
 +        // append the null byte as \x00 and the following digits literally
 +        suggest_2.push_str("\\x00");
 +        suggest_2.push_str(&contents[from + 2..to]);
 +
 +        index = to;
 +    }
 +    suggest_1.push_str(&contents[index..]);
 +    suggest_1.push('"');
 +    suggest_2.push_str(&contents[index..]);
 +    suggest_2.push('"');
 +
 +    span_lint_and_then(
 +        cx,
 +        OCTAL_ESCAPES,
 +        span,
 +        &format!(
 +            "octal-looking escape in {} literal",
 +            if is_string { "string" } else { "byte string" }
 +        ),
 +        |diag| {
 +            diag.help(&format!(
 +                "octal escapes are not supported, `\\0` is always a null {}",
 +                if is_string { "character" } else { "byte" }
 +            ));
 +            // suggestion 1: equivalent hex escape
 +            diag.span_suggestion(
 +                span,
 +                "if an octal escape was intended, use the hexadecimal representation instead",
 +                suggest_1,
 +                Applicability::MaybeIncorrect,
 +            );
 +            // suggestion 2: unambiguous null byte
 +            diag.span_suggestion(
 +                span,
 +                &format!(
 +                    "if the null {} is intended, disambiguate using",
 +                    if is_string { "character" } else { "byte" }
 +                ),
 +                suggest_2,
 +                Applicability::MaybeIncorrect,
 +            );
 +        },
 +    );
 +}
index 8827daaa3ee7c37760d54ad43ada2dc2d369c83d,0000000000000000000000000000000000000000..20b82d81a2aeb64e57d11994ceb3755a96e4cde4
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,198 @@@
- use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
 +use super::ARITHMETIC_SIDE_EFFECTS;
-     /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a
-     /// non-constant environment that won't overflow.
-     fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool {
-         if let hir::ExprKind::Lit(ref lit) = expr.kind &&
-             let ast::LitKind::Int(value, _) = lit.node
-         {
-             match (&op.node, value) {
-                 (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
-                 (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
-                     | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
-                     | (hir::BinOpKind::Mul, 0 | 1) => true,
-                 _ => false,
-             }
-         } else {
-             false
-         }
-     }
++use clippy_utils::{
++    consts::{constant, constant_simple},
++    diagnostics::span_lint,
++    peel_hir_expr_refs,
++};
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::Ty;
 +use rustc_session::impl_lint_pass;
 +use rustc_span::source_map::{Span, Spanned};
 +
 +const HARD_CODED_ALLOWED: &[&str] = &[
 +    "&str",
 +    "f32",
 +    "f64",
 +    "std::num::Saturating",
 +    "std::num::Wrapping",
 +    "std::string::String",
 +];
 +
 +#[derive(Debug)]
 +pub struct ArithmeticSideEffects {
 +    allowed: FxHashSet<String>,
 +    // Used to check whether expressions are constants, such as in enum discriminants and consts
 +    const_span: Option<Span>,
 +    expr_span: Option<Span>,
 +}
 +
 +impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
 +
 +impl ArithmeticSideEffects {
 +    #[must_use]
 +    pub fn new(mut allowed: FxHashSet<String>) -> Self {
 +        allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
 +        Self {
 +            allowed,
 +            const_span: None,
 +            expr_span: None,
 +        }
 +    }
 +
-     /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`.
-     fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option<LiteralIntegerTy<'expr, 'tcx>> {
-         if matches!(expr.kind, hir::ExprKind::Lit(_)) {
-             return Some(LiteralIntegerTy::Value(expr));
 +    /// Checks if the given `expr` has any of the inner `allowed` elements.
 +    fn is_allowed_ty(&self, ty: Ty<'_>) -> bool {
 +        self.allowed
 +            .contains(ty.to_string().split('<').next().unwrap_or_default())
 +    }
 +
 +    // For example, 8i32 or &i64::MAX.
 +    fn is_integral(ty: Ty<'_>) -> bool {
 +        ty.peel_refs().is_integral()
 +    }
 +
 +    // Common entry-point to avoid code duplication.
 +    fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
 +        let msg = "arithmetic operation that can potentially result in unexpected side-effects";
 +        span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg);
 +        self.expr_span = Some(expr.span);
 +    }
 +
-         if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind {
-             return Some(LiteralIntegerTy::Ref(inn));
++    /// If `expr` is not a literal integer like `1`, returns `None`.
++    fn literal_integer(expr: &hir::Expr<'_>) -> Option<u128> {
++        if let hir::ExprKind::Lit(ref lit) = expr.kind && let ast::LitKind::Int(n, _) = lit.node {
++            Some(n)
 +        }
-         None
++        else {
++            None
 +        }
-             match (Self::literal_integer(lhs), Self::literal_integer(rhs)) {
-                 (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()),
-                 (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true,
-                 (None, None) | (Some(_), Some(_)) => false,
 +    }
 +
 +    /// Manages when the lint should be triggered. Operations in constant environments, hard coded
 +    /// types, custom allowed types and non-constant operations that won't overflow are ignored.
 +    fn manage_bin_ops<'tcx>(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        expr: &hir::Expr<'tcx>,
 +        op: &Spanned<hir::BinOpKind>,
 +        lhs: &hir::Expr<'tcx>,
 +        rhs: &hir::Expr<'tcx>,
 +    ) {
 +        if constant_simple(cx, cx.typeck_results(), expr).is_some() {
 +            return;
 +        }
 +        if !matches!(
 +            op.node,
 +            hir::BinOpKind::Add
 +                | hir::BinOpKind::Sub
 +                | hir::BinOpKind::Mul
 +                | hir::BinOpKind::Div
 +                | hir::BinOpKind::Rem
 +                | hir::BinOpKind::Shl
 +                | hir::BinOpKind::Shr
 +        ) {
 +            return;
 +        };
 +        let lhs_ty = cx.typeck_results().expr_ty(lhs);
 +        let rhs_ty = cx.typeck_results().expr_ty(rhs);
 +        let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty;
 +        if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) {
 +            return;
 +        }
 +        let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
-         if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) {
++            let (actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs);
++            let (actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs);
++            match (Self::literal_integer(actual_lhs), Self::literal_integer(actual_rhs)) {
++                (None, None) => false,
++                (None, Some(n)) | (Some(n), None) => match (&op.node, n) {
++                    (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false,
++                    (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0)
++                    | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _)
++                    | (hir::BinOpKind::Mul, 0 | 1) => true,
++                    _ => false,
++                },
++                (Some(_), Some(_)) => {
++                    matches!((lhs_ref_counter, rhs_ref_counter), (0, 0))
++                },
 +            }
 +        } else {
 +            false
 +        };
 +        if !has_valid_op {
 +            self.issue_lint(cx, expr);
 +        }
 +    }
++
++    fn manage_unary_ops<'tcx>(
++        &mut self,
++        cx: &LateContext<'tcx>,
++        expr: &hir::Expr<'tcx>,
++        un_expr: &hir::Expr<'tcx>,
++        un_op: hir::UnOp,
++    ) {
++        let hir::UnOp::Neg = un_op else { return; };
++        if constant(cx, cx.typeck_results(), un_expr).is_some() {
++            return;
++        }
++        let ty = cx.typeck_results().expr_ty(expr).peel_refs();
++        if self.is_allowed_ty(ty) {
++            return;
++        }
++        let actual_un_expr = peel_hir_expr_refs(un_expr).0;
++        if Self::literal_integer(actual_un_expr).is_some() {
++            return;
++        }
++        self.issue_lint(cx, expr);
++    }
++
++    fn should_skip_expr(&mut self, expr: &hir::Expr<'_>) -> bool {
++        self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span))
++    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) {
-             hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
++        if self.should_skip_expr(expr) {
 +            return;
 +        }
 +        match &expr.kind {
-             hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
-                 if constant_simple(cx, cx.typeck_results(), expr).is_none() {
-                     self.issue_lint(cx, expr);
-                 }
++            hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => {
 +                self.manage_bin_ops(cx, expr, op, lhs, rhs);
 +            },
- /// Tells if an expression is a integer declared by value or by reference.
- ///
- /// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather
- /// than `hirExprKind::Addr`.
- enum LiteralIntegerTy<'expr, 'tcx> {
-     /// For example, `&199`
-     Ref(&'expr hir::Expr<'tcx>),
-     /// For example, `1` or `i32::MAX`
-     Value(&'expr hir::Expr<'tcx>),
- }
- impl<'expr, 'tcx> From<LiteralIntegerTy<'expr, 'tcx>> for &'expr hir::Expr<'tcx> {
-     fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self {
-         match from {
-             LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem,
-         }
-     }
- }
++            hir::ExprKind::Unary(un_op, un_expr) => {
++                self.manage_unary_ops(cx, expr, un_expr, *un_op);
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
 +        let body_owner = cx.tcx.hir().body_owner(body.id());
 +        let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
 +        let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
 +        if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind {
 +            let body_span = cx.tcx.hir().span_with_body(body_owner);
 +            if let Some(span) = self.const_span && span.contains(body_span) {
 +                return;
 +            }
 +            self.const_span = Some(body_span);
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
 +        let body_owner = cx.tcx.hir().body_owner(body.id());
 +        let body_span = cx.tcx.hir().span(body_owner);
 +        if let Some(span) = self.const_span && span.contains(body_span) {
 +            return;
 +        }
 +        self.const_span = None;
 +    }
 +
 +    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if Some(expr.span) == self.expr_span {
 +            self.expr_span = None;
 +        }
 +    }
 +}
index 71b31b5e4a562604efc96a1d30c2e6945f137b2d,0000000000000000000000000000000000000000..d7917e86a861f4fa57f5ada7f497293e51214f65
mode 100644,000000..100644
--- /dev/null
@@@ -1,218 -1,0 +1,218 @@@
- fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::get_enclosing_block;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::{implements_trait, is_copy};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +
 +use super::OP_REF;
 +
 +#[expect(clippy::similar_names, clippy::too_many_lines)]
 +pub(crate) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    op: BinOpKind,
 +    left: &'tcx Expr<'_>,
 +    right: &'tcx Expr<'_>,
 +) {
 +    let (trait_id, requires_ref) = match op {
 +        BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
 +        BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
 +        BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
 +        BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
 +        BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
 +        // don't lint short circuiting ops
 +        BinOpKind::And | BinOpKind::Or => return,
 +        BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
 +        BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
 +        BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
 +        BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
 +        BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
 +        BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
 +        BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
 +            (cx.tcx.lang_items().partial_ord_trait(), true)
 +        },
 +    };
 +    if let Some(trait_id) = trait_id {
 +        match (&left.kind, &right.kind) {
 +            // do not suggest to dereference literals
 +            (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
 +            // &foo == &bar
 +            (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
 +                let lty = cx.typeck_results().expr_ty(l);
 +                let rty = cx.typeck_results().expr_ty(r);
 +                let lcpy = is_copy(cx, lty);
 +                let rcpy = is_copy(cx, rty);
 +                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
 +                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
 +                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
 +                    {
 +                        return; // Don't lint
 +                    }
 +                }
 +                // either operator autorefs or both args are copyable
 +                if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
 +                    span_lint_and_then(
 +                        cx,
 +                        OP_REF,
 +                        e.span,
 +                        "needlessly taken reference of both operands",
 +                        |diag| {
 +                            let lsnip = snippet(cx, l.span, "...").to_string();
 +                            let rsnip = snippet(cx, r.span, "...").to_string();
 +                            multispan_sugg(
 +                                diag,
 +                                "use the values directly",
 +                                vec![(left.span, lsnip), (right.span, rsnip)],
 +                            );
 +                        },
 +                    );
 +                } else if lcpy
 +                    && !rcpy
 +                    && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
 +                {
 +                    span_lint_and_then(
 +                        cx,
 +                        OP_REF,
 +                        e.span,
 +                        "needlessly taken reference of left operand",
 +                        |diag| {
 +                            let lsnip = snippet(cx, l.span, "...").to_string();
 +                            diag.span_suggestion(
 +                                left.span,
 +                                "use the left value directly",
 +                                lsnip,
 +                                Applicability::MaybeIncorrect, // FIXME #2597
 +                            );
 +                        },
 +                    );
 +                } else if !lcpy
 +                    && rcpy
 +                    && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
 +                {
 +                    span_lint_and_then(
 +                        cx,
 +                        OP_REF,
 +                        e.span,
 +                        "needlessly taken reference of right operand",
 +                        |diag| {
 +                            let rsnip = snippet(cx, r.span, "...").to_string();
 +                            diag.span_suggestion(
 +                                right.span,
 +                                "use the right value directly",
 +                                rsnip,
 +                                Applicability::MaybeIncorrect, // FIXME #2597
 +                            );
 +                        },
 +                    );
 +                }
 +            },
 +            // &foo == bar
 +            (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
 +                let lty = cx.typeck_results().expr_ty(l);
 +                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
 +                    let rty = cx.typeck_results().expr_ty(right);
 +                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
 +                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
 +                    {
 +                        return; // Don't lint
 +                    }
 +                }
 +                let lcpy = is_copy(cx, lty);
 +                if (requires_ref || lcpy)
 +                    && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
 +                {
 +                    span_lint_and_then(
 +                        cx,
 +                        OP_REF,
 +                        e.span,
 +                        "needlessly taken reference of left operand",
 +                        |diag| {
 +                            let lsnip = snippet(cx, l.span, "...").to_string();
 +                            diag.span_suggestion(
 +                                left.span,
 +                                "use the left value directly",
 +                                lsnip,
 +                                Applicability::MaybeIncorrect, // FIXME #2597
 +                            );
 +                        },
 +                    );
 +                }
 +            },
 +            // foo == &bar
 +            (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
 +                let rty = cx.typeck_results().expr_ty(r);
 +                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
 +                    let lty = cx.typeck_results().expr_ty(left);
 +                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
 +                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
 +                    {
 +                        return; // Don't lint
 +                    }
 +                }
 +                let rcpy = is_copy(cx, rty);
 +                if (requires_ref || rcpy)
 +                    && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
 +                {
 +                    span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
 +                        let rsnip = snippet(cx, r.span, "...").to_string();
 +                        diag.span_suggestion(
 +                            right.span,
 +                            "use the right value directly",
 +                            rsnip,
 +                            Applicability::MaybeIncorrect, // FIXME #2597
 +                        );
 +                    });
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn in_impl<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    bin_op: DefId,
 +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
 +    if_chain! {
 +        if let Some(block) = get_enclosing_block(cx, e.hir_id);
 +        if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
 +        let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
 +        if let ItemKind::Impl(item) = &item.kind;
 +        if let Some(of_trait) = &item.of_trait;
 +        if let Some(seg) = of_trait.path.segments.last();
 +        if let Res::Def(_, trait_id) = seg.res;
 +        if trait_id == bin_op;
 +        if let Some(generic_args) = seg.args;
 +        if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
 +
 +        then {
 +            Some((item.self_ty, other_ty))
 +        }
 +        else {
 +            None
 +        }
 +    }
 +}
 +
++fn are_equal(cx: &LateContext<'_>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
 +    if_chain! {
 +        if let ty::Adt(adt_def, _) = middle_ty.kind();
 +        if let Some(local_did) = adt_def.did().as_local();
 +        let item = cx.tcx.hir().expect_item(local_did);
 +        let middle_ty_id = item.owner_id.to_def_id();
 +        if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
 +        if let Res::Def(_, hir_ty_id) = path.res;
 +
 +        then {
 +            hir_ty_id == middle_ty_id
 +        }
 +        else {
 +            false
 +        }
 +    }
 +}
index 4eb42da1fed02a1bdabb2dab559c36498e31b310,0000000000000000000000000000000000000000..472f52380bbf479ac0c22b9218b0637abcc3c4ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,263 -1,0 +1,266 @@@
-     if arms.len() == 2 {
-         return if is_none_or_err_arm(cx, &arms[1]) {
-             Some((arms[0].pat, arms[0].body, arms[1].body))
-         } else if is_none_or_err_arm(cx, &arms[0]) {
-             Some((arms[1].pat, arms[1].body, arms[0].body))
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{
 +    can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks,
 +    peel_hir_expr_while, CaptureKind,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lints usage of `if let Some(v) = ... { y } else { x }` and
 +    /// `match .. { Some(v) => y, None/_ => x }` which are more
 +    /// idiomatically done with `Option::map_or` (if the else bit is a pure
 +    /// expression) or `Option::map_or_else` (if the else bit is an impure
 +    /// expression).
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the dedicated functions of the `Option` type is clearer and
 +    /// more concise than an `if let` expression.
 +    ///
 +    /// ### Known problems
 +    /// This lint uses a deliberately conservative metric for checking
 +    /// if the inside of either body contains breaks or continues which will
 +    /// cause it to not suggest a fix if either block contains a loop with
 +    /// continues or breaks contained within the loop.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let optional: Option<u32> = Some(0);
 +    /// # fn do_complicated_function() -> u32 { 5 };
 +    /// let _ = if let Some(foo) = optional {
 +    ///     foo
 +    /// } else {
 +    ///     5
 +    /// };
 +    /// let _ = match optional {
 +    ///     Some(val) => val + 1,
 +    ///     None => 5
 +    /// };
 +    /// let _ = if let Some(foo) = optional {
 +    ///     foo
 +    /// } else {
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// };
 +    /// ```
 +    ///
 +    /// should be
 +    ///
 +    /// ```rust
 +    /// # let optional: Option<u32> = Some(0);
 +    /// # fn do_complicated_function() -> u32 { 5 };
 +    /// let _ = optional.map_or(5, |foo| foo);
 +    /// let _ = optional.map_or(5, |val| val + 1);
 +    /// let _ = optional.map_or_else(||{
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// }, |foo| foo);
 +    /// ```
 +    // FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also
 +    // covers matches and `Result`.
 +    #[clippy::version = "1.47.0"]
 +    pub OPTION_IF_LET_ELSE,
 +    nursery,
 +    "reimplementation of Option::map_or"
 +}
 +
 +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 +
 +/// A struct containing information about occurrences of construct that this lint detects
 +///
 +/// Such as:
 +///
 +/// ```ignore
 +/// if let Some(..) = {..} else {..}
 +/// ```
 +/// or
 +/// ```ignore
 +/// match x {
 +///     Some(..) => {..},
 +///     None/_ => {..}
 +/// }
 +/// ```
 +struct OptionOccurrence {
 +    option: String,
 +    method_sugg: String,
 +    some_expr: String,
 +    none_expr: String,
 +}
 +
 +fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
 +    format!(
 +        "{}{}",
 +        Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
 +        if as_mut {
 +            ".as_mut()"
 +        } else if as_ref {
 +            ".as_ref()"
 +        } else {
 +            ""
 +        }
 +    )
 +}
 +
 +fn try_get_option_occurrence<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &Pat<'tcx>,
 +    expr: &Expr<'_>,
 +    if_then: &'tcx Expr<'_>,
 +    if_else: &'tcx Expr<'_>,
 +) -> Option<OptionOccurrence> {
 +    let cond_expr = match expr.kind {
 +        ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr,
 +        _ => expr,
 +    };
 +    let inner_pat = try_get_inner_pat(cx, pat)?;
 +    if_chain! {
 +        if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
 +        if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
 +        if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
 +        if some_captures
 +            .iter()
 +            .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
 +            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
 +        then {
 +            let capture_mut = if bind_annotation == BindingAnnotation::MUT { "mut " } else { "" };
 +            let some_body = peel_blocks(if_then);
 +            let none_body = peel_blocks(if_else);
 +            let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
 +            let capture_name = id.name.to_ident_string();
 +            let (as_ref, as_mut) = match &expr.kind {
 +                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
 +                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
 +                _ => (bind_annotation == BindingAnnotation::REF, bind_annotation == BindingAnnotation::REF_MUT),
 +            };
 +
 +            // Check if captures the closure will need conflict with borrows made in the scrutinee.
 +            // TODO: check all the references made in the scrutinee expression. This will require interacting
 +            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
 +            if as_ref || as_mut {
 +                let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
 +                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
 +                    _ => None,
 +                });
 +                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
 +                    match some_captures.get(local_id)
 +                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
 +                    {
 +                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
 +                        Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
 +                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
 +                    }
 +                }
 +            }
 +
 +            return Some(OptionOccurrence {
 +                option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
 +                method_sugg: method_sugg.to_string(),
 +                some_expr: format!("|{capture_mut}{capture_name}| {}", Sugg::hir_with_macro_callsite(cx, some_body, "..")),
 +                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
 +            });
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
 +    if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
 +        let res = cx.qpath_res(qpath, pat.hir_id);
 +        if is_res_lang_ctor(cx, res, OptionSome) || is_res_lang_ctor(cx, res, ResultOk) {
 +            return Some(inner_pat);
 +        }
 +    }
 +    None
 +}
 +
 +/// If this expression is the option if let/else construct we're detecting, then
 +/// this function returns an `OptionOccurrence` struct with details if
 +/// this construct is found, or None if this construct is not found.
 +fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> {
 +    if let Some(higher::IfLet {
 +        let_pat,
 +        let_expr,
 +        if_then,
 +        if_else: Some(if_else),
 +    }) = higher::IfLet::hir(cx, expr)
 +    {
 +        if !is_else_clause(cx.tcx, expr) {
 +            return try_get_option_occurrence(cx, let_pat, let_expr, if_then, if_else);
 +        }
 +    }
 +    None
 +}
 +
 +fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurrence> {
 +    if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
 +        if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
 +            return try_get_option_occurrence(cx, let_pat, ex, if_then, if_else);
 +        }
 +    }
 +    None
 +}
 +
 +fn try_convert_match<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    arms: &[Arm<'tcx>],
 +) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
++    if let [first_arm, second_arm] = arms
++        && first_arm.guard.is_none()
++        && second_arm.guard.is_none()
++        {
++        return if is_none_or_err_arm(cx, second_arm) {
++            Some((first_arm.pat, first_arm.body, second_arm.body))
++        } else if is_none_or_err_arm(cx, first_arm) {
++            Some((second_arm.pat, second_arm.body, first_arm.body))
 +        } else {
 +            None
 +        };
 +    }
 +    None
 +}
 +
 +fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +    match arm.pat.kind {
 +        PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone),
 +        PatKind::TupleStruct(ref qpath, [first_pat], _) => {
 +            is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr)
 +                && matches!(first_pat.kind, PatKind::Wild)
 +        },
 +        PatKind::Wild => true,
 +        _ => false,
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +        // Don't lint macros and constants
 +        if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
 +            return;
 +        }
 +
 +        let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr));
 +        if let Some(det) = detection {
 +            span_lint_and_sugg(
 +                cx,
 +                OPTION_IF_LET_ELSE,
 +                expr.span,
 +                format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(),
 +                "try",
 +                format!(
 +                    "{}.{}({}, {})",
 +                    det.option, det.method_sugg, det.none_expr, det.some_expr
 +                ),
 +                Applicability::MaybeIncorrect,
 +            );
 +        }
 +    }
 +}
index 6810a2431758910ab3a62bf5ac25286882ac5e97,0000000000000000000000000000000000000000..456ded3fc02680de1c7e03a2433399f5e965b857
mode 100644,000000..100644
--- /dev/null
@@@ -1,104 -1,0 +1,104 @@@
-     #[clippy::version = "1.64.0"]
 +use clippy_utils::{
 +    diagnostics::span_lint_and_sugg, is_res_lang_ctor, path_res, peel_hir_expr_refs, peel_ref_operators, sugg,
 +    ty::is_type_diagnostic_item,
 +};
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for binary comparisons to a literal `Option::None`.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// A programmer checking if some `foo` is `None` via a comparison `foo == None`
 +    /// is usually inspired from other programming languages (e.g. `foo is None`
 +    /// in Python).
 +    /// Checking if a value of type `Option<T>` is (not) equal to `None` in that
 +    /// way relies on `T: PartialEq` to do the comparison, which is unneeded.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(f: Option<u32>) -> &'static str {
 +    ///     if f != None { "yay" } else { "nay" }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo(f: Option<u32>) -> &'static str {
 +    ///     if f.is_some() { "yay" } else { "nay" }
 +    /// }
 +    /// ```
++    #[clippy::version = "1.65.0"]
 +    pub PARTIALEQ_TO_NONE,
 +    style,
 +    "Binary comparison to `Option<T>::None` relies on `T: PartialEq`, which is unneeded"
 +}
 +declare_lint_pass!(PartialeqToNone => [PARTIALEQ_TO_NONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PartialeqToNone {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        // Skip expanded code, as we have no control over it anyway...
 +        if e.span.from_expansion() {
 +            return;
 +        }
 +
 +        // If the expression is of type `Option`
 +        let is_ty_option =
 +            |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option);
 +
 +        // If the expression is a literal `Option::None`
 +        let is_none_ctor = |expr: &Expr<'_>| {
 +            !expr.span.from_expansion()
 +                && is_res_lang_ctor(cx, path_res(cx, peel_hir_expr_refs(expr).0), LangItem::OptionNone)
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +
 +        if let ExprKind::Binary(op, left_side, right_side) = e.kind {
 +            // All other comparisons (e.g. `>= None`) have special meaning wrt T
 +            let is_eq = match op.node {
 +                BinOpKind::Eq => true,
 +                BinOpKind::Ne => false,
 +                _ => return,
 +            };
 +
 +            // We are only interested in comparisons between `Option` and a literal `Option::None`
 +            let scrutinee = match (
 +                is_none_ctor(left_side) && is_ty_option(right_side),
 +                is_none_ctor(right_side) && is_ty_option(left_side),
 +            ) {
 +                (true, false) => right_side,
 +                (false, true) => left_side,
 +                _ => return,
 +            };
 +
 +            // Peel away refs/derefs (as long as we don't cross manual deref impls), as
 +            // autoref/autoderef will take care of those
 +            let sugg = format!(
 +                "{}.{}",
 +                sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability)
 +                    .maybe_par(),
 +                if is_eq { "is_none()" } else { "is_some()" }
 +            );
 +
 +            span_lint_and_sugg(
 +                cx,
 +                PARTIALEQ_TO_NONE,
 +                e.span,
 +                "binary comparison to literal `Option::None`",
 +                if is_eq {
 +                    "use `Option::is_none()` instead"
 +                } else {
 +                    "use `Option::is_some()` instead"
 +                },
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
index a4d265111f9aee26d241d913d790c19bb0949740,0000000000000000000000000000000000000000..97b5a4ce36413f4820b209a68f45c3beeb26db54
mode 100644,000000..100644
--- /dev/null
@@@ -1,194 -1,0 +1,194 @@@
- fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_hir::{
 +    intravisit, Body, Expr, ExprKind, FnDecl, HirId, Let, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for patterns that aren't exact representations of the types
 +    /// they are applied to.
 +    ///
 +    /// To satisfy this lint, you will have to adjust either the expression that is matched
 +    /// against or the pattern itself, as well as the bindings that are introduced by the
 +    /// adjusted patterns. For matching you will have to either dereference the expression
 +    /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
 +    /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
 +    /// to use the inverse. You can leave them as plain bindings if you wish for the value
 +    /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
 +    /// a reference into the matched structure.
 +    ///
 +    /// If you are looking for a way to learn about ownership semantics in more detail, it
 +    /// is recommended to look at IDE options available to you to highlight types, lifetimes
 +    /// and reference semantics in your code. The available tooling would expose these things
 +    /// in a general way even outside of the various pattern matching mechanics. Of course
 +    /// this lint can still be used to highlight areas of interest and ensure a good understanding
 +    /// of ownership semantics.
 +    ///
 +    /// ### Why is this bad?
 +    /// It isn't bad in general. But in some contexts it can be desirable
 +    /// because it increases ownership hints in the code, and will guard against some changes
 +    /// in ownership.
 +    ///
 +    /// ### Example
 +    /// This example shows the basic adjustments necessary to satisfy the lint. Note how
 +    /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
 +    /// is bound to a shared borrow via `ref inner`.
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// let value = &Some(Box::new(23));
 +    /// match value {
 +    ///     Some(inner) => println!("{}", inner),
 +    ///     None => println!("none"),
 +    /// }
 +    ///
 +    /// // Good
 +    /// let value = &Some(Box::new(23));
 +    /// match *value {
 +    ///     Some(ref inner) => println!("{}", inner),
 +    ///     None => println!("none"),
 +    /// }
 +    /// ```
 +    ///
 +    /// The following example demonstrates one of the advantages of the more verbose style.
 +    /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
 +    /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
 +    /// accidentally modify the wrong part of the structure.
 +    ///
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// let mut values = vec![(2, 3), (3, 4)];
 +    /// for (a, b) in &mut values {
 +    ///     *a += *b;
 +    /// }
 +    ///
 +    /// // Good
 +    /// let mut values = vec![(2, 3), (3, 4)];
 +    /// for &mut (ref mut a, b) in &mut values {
 +    ///     *a += b;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub PATTERN_TYPE_MISMATCH,
 +    restriction,
 +    "type of pattern does not match the expression type"
 +}
 +
 +declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if let StmtKind::Local(local) = stmt.kind {
 +            if in_external_macro(cx.sess(), local.pat.span) {
 +                return;
 +            }
 +            let deref_possible = match local.source {
 +                LocalSource::Normal => DerefPossible::Possible,
 +                _ => DerefPossible::Impossible,
 +            };
 +            apply_lint(cx, local.pat, deref_possible);
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Match(_, arms, _) = expr.kind {
 +            for arm in arms {
 +                let pat = &arm.pat;
 +                if apply_lint(cx, pat, DerefPossible::Possible) {
 +                    break;
 +                }
 +            }
 +        }
 +        if let ExprKind::Let(Let { pat, .. }) = expr.kind {
 +            apply_lint(cx, pat, DerefPossible::Possible);
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: intravisit::FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        for param in body.params {
 +            apply_lint(cx, param.pat, DerefPossible::Impossible);
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Copy)]
 +enum DerefPossible {
 +    Possible,
 +    Impossible,
 +}
 +
- fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> {
++fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
 +    let maybe_mismatch = find_first_mismatch(cx, pat);
 +    if let Some((span, mutability, level)) = maybe_mismatch {
 +        span_lint_and_help(
 +            cx,
 +            PATTERN_TYPE_MISMATCH,
 +            span,
 +            "type of pattern does not match the expression type",
 +            None,
 +            &format!(
 +                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
 +                match (deref_possible, level) {
 +                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
 +                    _ => "",
 +                },
 +                match mutability {
 +                    Mutability::Mut => "&mut _",
 +                    Mutability::Not => "&_",
 +                },
 +            ),
 +        );
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +#[derive(Debug, Copy, Clone)]
 +enum Level {
 +    Top,
 +    Lower,
 +}
 +
++fn find_first_mismatch(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> {
 +    let mut result = None;
 +    pat.walk(|p| {
 +        if result.is_some() {
 +            return false;
 +        }
 +        if in_external_macro(cx.sess(), p.span) {
 +            return true;
 +        }
 +        let adjust_pat = match p.kind {
 +            PatKind::Or([p, ..]) => p,
 +            _ => p,
 +        };
 +        if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
 +            if let [first, ..] = **adjustments {
 +                if let ty::Ref(.., mutability) = *first.kind() {
 +                    let level = if p.hir_id == pat.hir_id {
 +                        Level::Top
 +                    } else {
 +                        Level::Lower
 +                    };
 +                    result = Some((p.span, mutability, level));
 +                }
 +            }
 +        }
 +        result.is_none()
 +    });
 +    result
 +}
index 72dda67c72b25d09a64aa9c953d668c42342b4da,0000000000000000000000000000000000000000..47b8891e12302b4e4ef6f9526f1b012307158d4d
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,151 @@@
- fn is_expr_ty_usize<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 +use clippy_utils::source::snippet_opt;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +use std::fmt;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of the `offset` pointer method with a `usize` casted to an
 +    /// `isize`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If we’re always increasing the pointer address, we can avoid the numeric
 +    /// cast by using the `add` method instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![b'a', b'b', b'c'];
 +    /// let ptr = vec.as_ptr();
 +    /// let offset = 1_usize;
 +    ///
 +    /// unsafe {
 +    ///     ptr.offset(offset as isize);
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// let vec = vec![b'a', b'b', b'c'];
 +    /// let ptr = vec.as_ptr();
 +    /// let offset = 1_usize;
 +    ///
 +    /// unsafe {
 +    ///     ptr.add(offset);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.30.0"]
 +    pub PTR_OFFSET_WITH_CAST,
 +    complexity,
 +    "unneeded pointer offset cast"
 +}
 +
 +declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
 +        let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else {
 +            return
 +        };
 +
 +        // Check if the argument to the method call is a cast from usize
 +        let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else {
 +            return
 +        };
 +
 +        let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
 +        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                PTR_OFFSET_WITH_CAST,
 +                expr.span,
 +                &msg,
 +                "try",
 +                sugg,
 +                Applicability::MachineApplicable,
 +            );
 +        } else {
 +            span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, &msg);
 +        }
 +    }
 +}
 +
 +// If the given expression is a cast from a usize, return the lhs of the cast
 +fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind {
 +        if is_expr_ty_usize(cx, cast_lhs_expr) {
 +            return Some(cast_lhs_expr);
 +        }
 +    }
 +    None
 +}
 +
 +// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
 +// receiver, the arg of the method call, and the method.
 +fn expr_as_ptr_offset_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
 +    if let ExprKind::MethodCall(path_segment, arg_0, [arg_1, ..], _) = &expr.kind {
 +        if is_expr_ty_raw_ptr(cx, arg_0) {
 +            if path_segment.ident.name == sym::offset {
 +                return Some((arg_0, arg_1, Method::Offset));
 +            }
 +            if path_segment.ident.name == sym!(wrapping_offset) {
 +                return Some((arg_0, arg_1, Method::WrappingOffset));
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +// Is the type of the expression a usize?
- fn is_expr_ty_raw_ptr<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
++fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize
 +}
 +
 +// Is the type of the expression a raw pointer?
- fn build_suggestion<'tcx>(
-     cx: &LateContext<'tcx>,
++fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    cx.typeck_results().expr_ty(expr).is_unsafe_ptr()
 +}
 +
++fn build_suggestion(
++    cx: &LateContext<'_>,
 +    method: Method,
 +    receiver_expr: &Expr<'_>,
 +    cast_lhs_expr: &Expr<'_>,
 +) -> Option<String> {
 +    let receiver = snippet_opt(cx, receiver_expr.span)?;
 +    let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
 +    Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
 +}
 +
 +#[derive(Copy, Clone)]
 +enum Method {
 +    Offset,
 +    WrappingOffset,
 +}
 +
 +impl Method {
 +    #[must_use]
 +    fn suggestion(self) -> &'static str {
 +        match self {
 +            Self::Offset => "add",
 +            Self::WrappingOffset => "wrapping_add",
 +        }
 +    }
 +}
 +
 +impl fmt::Display for Method {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        match self {
 +            Self::Offset => write!(f, "offset"),
 +            Self::WrappingOffset => write!(f, "wrapping_offset"),
 +        }
 +    }
 +}
index bb86fb3b7d42f43858db4b853f7fe3cbcb6ca8f6,0000000000000000000000000000000000000000..5269bbd1f1acc06d510294fd2b018de8cf78c4c6
mode 100644,000000..100644
--- /dev/null
@@@ -1,239 -1,0 +1,240 @@@
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{
 +    eq_expr_value, get_parent_node, in_constant, is_else_clause, is_res_lang_ctor, path_to_local, path_to_local_id,
 +    peel_blocks, peel_blocks_with_stmt,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 +use rustc_hir::{BindingAnnotation, ByRef, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, symbol::Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions that could be replaced by the question mark operator.
 +    ///
 +    /// ### Why is this bad?
 +    /// Question mark usage is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///     return None;
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```ignore
 +    /// option?;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub QUESTION_MARK,
 +    style,
 +    "checks for expressions that could be replaced by the question mark operator"
 +}
 +
 +declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
 +
 +enum IfBlockType<'hir> {
 +    /// An `if x.is_xxx() { a } else { b } ` expression.
 +    ///
 +    /// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
 +    IfIs(
 +        &'hir Expr<'hir>,
 +        Ty<'hir>,
 +        Symbol,
 +        &'hir Expr<'hir>,
 +        Option<&'hir Expr<'hir>>,
 +    ),
 +    /// An `if let Xxx(a) = b { c } else { d }` expression.
 +    ///
 +    /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
 +    /// if_else (d)
 +    IfLet(
 +        Res,
 +        Ty<'hir>,
 +        Symbol,
 +        &'hir Expr<'hir>,
 +        &'hir Expr<'hir>,
 +        Option<&'hir Expr<'hir>>,
 +    ),
 +}
 +
 +/// Checks if the given expression on the given context matches the following structure:
 +///
 +/// ```ignore
 +/// if option.is_none() {
 +///    return None;
 +/// }
 +/// ```
 +///
 +/// ```ignore
 +/// if result.is_err() {
 +///     return result;
 +/// }
 +/// ```
 +///
 +/// If it matches, it will suggest to use the question mark operator instead
 +fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
 +        if !is_else_clause(cx.tcx, expr);
 +        if let ExprKind::MethodCall(segment, caller, ..) = &cond.kind;
 +        let caller_ty = cx.typeck_results().expr_ty(caller);
 +        let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
 +        if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
 +            let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env) &&
 +                !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
 +            let sugg = if let Some(else_inner) = r#else {
 +                if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
 +                    format!("Some({receiver_str}?)")
 +                } else {
 +                    return;
 +                }
 +            } else {
 +                format!("{receiver_str}{}?;", if by_ref { ".as_ref()" } else { "" })
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                QUESTION_MARK,
 +                expr.span,
 +                "this block may be rewritten with the `?` operator",
 +                "replace it with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +    if_chain! {
 +        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
 +        if !is_else_clause(cx.tcx, expr);
 +        if let PatKind::TupleStruct(ref path1, [field], ddpos) = let_pat.kind;
 +        if ddpos.as_opt_usize().is_none();
 +        if let PatKind::Binding(BindingAnnotation(by_ref, _), bind_id, ident, None) = field.kind;
 +        let caller_ty = cx.typeck_results().expr_ty(let_expr);
 +        let if_block = IfBlockType::IfLet(
 +            cx.qpath_res(path1, let_pat.hir_id),
 +            caller_ty,
 +            ident.name,
 +            let_expr,
 +            if_then,
 +            if_else
 +        );
 +        if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
 +            || is_early_return(sym::Result, cx, &if_block);
 +        if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
 +            let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
 +            let sugg = format!(
 +                "{receiver_str}{}?{}",
 +                if by_ref == ByRef::Yes { ".as_ref()" } else { "" },
 +                if requires_semi { ";" } else { "" }
 +            );
 +            span_lint_and_sugg(
 +                cx,
 +                QUESTION_MARK,
 +                expr.span,
 +                "this block may be rewritten with the `?` operator",
 +                "replace it with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
 +    match *if_block {
 +        IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
 +            // If the block could be identified as `if x.is_none()/is_err()`,
 +            // we then only need to check the if_then return to see if it is none/err.
 +            is_type_diagnostic_item(cx, caller_ty, smbl)
 +                && expr_return_none_or_err(smbl, cx, if_then, caller, None)
 +                && match smbl {
 +                    sym::Option => call_sym == sym!(is_none),
 +                    sym::Result => call_sym == sym!(is_err),
 +                    _ => false,
 +                }
 +        },
 +        IfBlockType::IfLet(res, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
 +            is_type_diagnostic_item(cx, let_expr_ty, smbl)
 +                && match smbl {
 +                    sym::Option => {
 +                        // We only need to check `if let Some(x) = option` not `if let None = option`,
 +                        // because the later one will be suggested as `if option.is_none()` thus causing conflict.
 +                        is_res_lang_ctor(cx, res, OptionSome)
 +                            && if_else.is_some()
 +                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None)
 +                    },
 +                    sym::Result => {
 +                        (is_res_lang_ctor(cx, res, ResultOk)
 +                            && if_else.is_some()
 +                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym)))
 +                            || is_res_lang_ctor(cx, res, ResultErr)
 +                                && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym))
++                                && if_else.is_none()
 +                    },
 +                    _ => false,
 +                }
 +        },
 +    }
 +}
 +
 +fn expr_return_none_or_err(
 +    smbl: Symbol,
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    cond_expr: &Expr<'_>,
 +    err_sym: Option<Symbol>,
 +) -> bool {
 +    match peel_blocks_with_stmt(expr).kind {
 +        ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym),
 +        ExprKind::Path(ref qpath) => match smbl {
 +            sym::Option => is_res_lang_ctor(cx, cx.qpath_res(qpath, expr.hir_id), OptionNone),
 +            sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
 +            _ => false,
 +        },
 +        ExprKind::Call(call_expr, args_expr) => {
 +            if_chain! {
 +                if smbl == sym::Result;
 +                if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind;
 +                if let Some(segment) = path.segments.first();
 +                if let Some(err_sym) = err_sym;
 +                if let Some(arg) = args_expr.first();
 +                if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind;
 +                if let Some(PathSegment { ident, .. }) = arg_path.segments.first();
 +                then {
 +                    return segment.ident.name == sym::Err && err_sym == ident.name;
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for QuestionMark {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !in_constant(cx, expr.hir_id) {
 +            check_is_none_or_err_and_early_return(cx, expr);
 +            check_if_let_some_or_err_and_early_return(cx, expr);
 +        }
 +    }
 +}
index 4cbe9597c5393eee7221480dbb487aa6d863ce76,0000000000000000000000000000000000000000..8e675d34a183698b970e86e0fca4f5dd27c1903d
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,166 @@@
-         fn count_closure_usage<'a, 'tcx>(
-             cx: &'a LateContext<'tcx>,
 +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 app = Applicability::MachineApplicable;
 +                                let mut hint = Sugg::ast(cx, body, "..");
 +
 +                                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 26075e9f70faa498d456dde4b9fbff350ab27b71,0000000000000000000000000000000000000000..833dc4913b46922a215653311abb91bb6da5c745
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,95 @@@
-             self.is_exported.push(cx.effective_visibilities.is_exported(item.owner_id.def_id));
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::CRATE_DEF_ID;
 +use rustc_span::hygiene::MacroKind;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for items declared `pub(crate)` that are not crate visible because they
 +    /// are inside a private module.
 +    ///
 +    /// ### Why is this bad?
 +    /// Writing `pub(crate)` is misleading when it's redundant due to the parent
 +    /// module's visibility.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// mod internal {
 +    ///     pub(crate) fn internal_fn() { }
 +    /// }
 +    /// ```
 +    /// This function is not visible outside the module and it can be declared with `pub` or
 +    /// private visibility
 +    /// ```rust
 +    /// mod internal {
 +    ///     pub fn internal_fn() { }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub REDUNDANT_PUB_CRATE,
 +    nursery,
 +    "Using `pub(crate)` visibility on items that are not crate visible due to the visibility of the module that contains them."
 +}
 +
 +#[derive(Default)]
 +pub struct RedundantPubCrate {
 +    is_exported: Vec<bool>,
 +}
 +
 +impl_lint_pass!(RedundantPubCrate => [REDUNDANT_PUB_CRATE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if_chain! {
 +            if cx.tcx.visibility(item.owner_id.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id());
 +            if !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false);
 +            if is_not_macro_export(item);
 +            then {
 +                let span = item.span.with_hi(item.ident.span.hi());
 +                let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id());
 +                span_lint_and_then(
 +                    cx,
 +                    REDUNDANT_PUB_CRATE,
 +                    span,
 +                    &format!("pub(crate) {descr} inside private module"),
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            item.vis_span,
 +                            "consider using",
 +                            "pub".to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +
 +        if let ItemKind::Mod { .. } = item.kind {
++            self.is_exported
++                .push(cx.effective_visibilities.is_exported(item.owner_id.def_id));
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if let ItemKind::Mod { .. } = item.kind {
 +            self.is_exported.pop().expect("unbalanced check_item/check_item_post");
 +        }
 +    }
 +}
 +
 +fn is_not_macro_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
 +    if let ItemKind::Use(path, _) = item.kind {
 +        if let Res::Def(DefKind::Macro(MacroKind::Bang), _) = path.res {
 +            return false;
 +        }
 +    } else if let ItemKind::Macro(..) = item.kind {
 +        return false;
 +    }
 +
 +    true
 +}
index 76d6ad0b23e6a82ffb31259f35dac6356fe45977,0000000000000000000000000000000000000000..8e214218f23ae1d5f617147d4e9ca2ff42cb37af
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,45 @@@
-     ("clippy::for_loop_over_option", "for_loops_over_fallibles"),
-     ("clippy::for_loop_over_result", "for_loops_over_fallibles"),
 +// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.
 +
 +#[rustfmt::skip]
 +pub static RENAMED_LINTS: &[(&str, &str)] = &[
 +    ("clippy::blacklisted_name", "clippy::disallowed_names"),
 +    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions"),
 +    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions"),
 +    ("clippy::box_vec", "clippy::box_collection"),
 +    ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
 +    ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
 +    ("clippy::disallowed_method", "clippy::disallowed_methods"),
 +    ("clippy::disallowed_type", "clippy::disallowed_types"),
 +    ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
 +    ("clippy::identity_conversion", "clippy::useless_conversion"),
 +    ("clippy::if_let_some_result", "clippy::match_result_ok"),
 +    ("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
 +    ("clippy::new_without_default_derive", "clippy::new_without_default"),
 +    ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
 +    ("clippy::option_expect_used", "clippy::expect_used"),
 +    ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
 +    ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
 +    ("clippy::option_unwrap_used", "clippy::unwrap_used"),
 +    ("clippy::ref_in_deref", "clippy::needless_borrow"),
 +    ("clippy::result_expect_used", "clippy::expect_used"),
 +    ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
 +    ("clippy::result_unwrap_used", "clippy::unwrap_used"),
 +    ("clippy::single_char_push_str", "clippy::single_char_add_str"),
 +    ("clippy::stutter", "clippy::module_name_repetitions"),
 +    ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
 +    ("clippy::zero_width_space", "clippy::invisible_characters"),
 +    ("clippy::drop_bounds", "drop_bounds"),
++    ("clippy::for_loop_over_option", "for_loops_over_fallibles"),
++    ("clippy::for_loop_over_result", "for_loops_over_fallibles"),
 +    ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
 +    ("clippy::into_iter_on_array", "array_into_iter"),
 +    ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
 +    ("clippy::invalid_ref", "invalid_value"),
++    ("clippy::let_underscore_drop", "let_underscore_drop"),
 +    ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
 +    ("clippy::panic_params", "non_fmt_panics"),
 +    ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
 +    ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
 +    ("clippy::unknown_clippy_lints", "unknown_lints"),
 +    ("clippy::unused_label", "unused_labels"),
 +];
index 66b79513032f6e8871e52ed543dafc9eb8585b5e,0000000000000000000000000000000000000000..2036e85db7e8ca73f5f73e80cbfa570498016131
mode 100644,000000..100644
--- /dev/null
@@@ -1,175 -1,0 +1,210 @@@
- use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
++use rustc_ast::node_id::{NodeId, NodeMap};
 +use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
- declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
++use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checking for imports with single component use path.
 +    ///
 +    /// ### Why is this bad?
 +    /// Import with single component use path such as `use cratename;`
 +    /// is not necessary, and thus should be removed.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use regex;
 +    ///
 +    /// fn main() {
 +    ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
 +    /// }
 +    /// ```
 +    /// Better as
 +    /// ```rust,ignore
 +    /// fn main() {
 +    ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub SINGLE_COMPONENT_PATH_IMPORTS,
 +    style,
 +    "imports with single component path are redundant"
 +}
 +
-         check_mod(cx, &krate.items);
-     }
- }
++#[derive(Default)]
++pub struct SingleComponentPathImports {
++    /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected
++    found: NodeMap<Vec<SingleUse>>,
++}
++
++struct SingleUse {
++    name: Symbol,
++    span: Span,
++    item_id: NodeId,
++    can_suggest: bool,
++}
++
++impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
 +
 +impl EarlyLintPass for SingleComponentPathImports {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
 +        if cx.sess().opts.edition < Edition::Edition2018 {
 +            return;
 +        }
- fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
-     // keep track of imports reused with `self` keyword,
-     // such as `self::crypto_hash` in the example below
-     // ```rust,ignore
-     // use self::crypto_hash::{Algorithm, Hasher};
-     // ```
-     let mut imports_reused_with_self = Vec::new();
-     // keep track of single use statements
-     // such as `crypto_hash` in the example below
-     // ```rust,ignore
-     // use crypto_hash;
-     // ```
-     let mut single_use_usages = Vec::new();
-     // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
-     // ```rust,ignore
-     // macro_rules! foo { () => {} };
-     // pub(crate) use foo;
-     // ```
-     let mut macros = Vec::new();
-     for item in items {
-         track_uses(
-             cx,
-             item,
-             &mut imports_reused_with_self,
-             &mut single_use_usages,
-             &mut macros,
-         );
 +
-     for (name, span, can_suggest) in single_use_usages {
-         if !imports_reused_with_self.contains(&name) {
++        self.check_mod(cx, &krate.items);
 +    }
 +
- fn track_uses(
-     cx: &EarlyContext<'_>,
-     item: &Item,
-     imports_reused_with_self: &mut Vec<Symbol>,
-     single_use_usages: &mut Vec<(Symbol, Span, bool)>,
-     macros: &mut Vec<Symbol>,
- ) {
-     if item.span.from_expansion() || item.vis.kind.is_pub() {
-         return;
-     }
++    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
++        for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() {
 +            if can_suggest {
 +                span_lint_and_sugg(
 +                    cx,
 +                    SINGLE_COMPONENT_PATH_IMPORTS,
 +                    span,
 +                    "this import is redundant",
 +                    "remove it entirely",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            } else {
 +                span_lint_and_help(
 +                    cx,
 +                    SINGLE_COMPONENT_PATH_IMPORTS,
 +                    span,
 +                    "this import is redundant",
 +                    None,
 +                    "remove this import",
 +                );
 +            }
 +        }
 +    }
 +}
 +
-     match &item.kind {
-         ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
-             check_mod(cx, items);
-         },
-         ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
-             macros.push(item.ident.name);
-         },
-         ItemKind::Use(use_tree) => {
-             let segments = &use_tree.prefix.segments;
-             // keep track of `use some_module;` usages
-             if segments.len() == 1 {
-                 if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
-                     let name = segments[0].ident.name;
-                     if !macros.contains(&name) {
-                         single_use_usages.push((name, item.span, true));
-                     }
-                 }
-                 return;
++impl SingleComponentPathImports {
++    fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) {
++        // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
++        // below. Removing the `use crypto_hash;` would make this a compile error
++        // ```
++        // use crypto_hash;
++        //
++        // use self::crypto_hash::{Algorithm, Hasher};
++        // ```
++        let mut imports_reused_with_self = Vec::new();
 +
-             if segments.is_empty() {
-                 // keep track of `use {some_module, some_other_module};` usages
-                 if let UseTreeKind::Nested(trees) = &use_tree.kind {
-                     for tree in trees {
-                         let segments = &tree.0.prefix.segments;
-                         if segments.len() == 1 {
-                             if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
-                                 let name = segments[0].ident.name;
-                                 if !macros.contains(&name) {
-                                     single_use_usages.push((name, tree.0.span, false));
-                                 }
-                             }
++        // keep track of single use statements such as `crypto_hash` in the example below
++        // ```
++        // use crypto_hash;
++        // ```
++        let mut single_use_usages = Vec::new();
++
++        // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
++        // ```
++        // macro_rules! foo { () => {} };
++        // pub(crate) use foo;
++        // ```
++        let mut macros = Vec::new();
++
++        for item in items {
++            self.track_uses(
++                cx,
++                item,
++                &mut imports_reused_with_self,
++                &mut single_use_usages,
++                &mut macros,
++            );
++        }
++
++        for usage in single_use_usages {
++            if !imports_reused_with_self.contains(&usage.name) {
++                self.found.entry(usage.item_id).or_default().push(usage);
 +            }
++        }
++    }
 +
-             } else {
-                 // keep track of `use self::some_module` usages
-                 if segments[0].ident.name == kw::SelfLower {
-                     // simple case such as `use self::module::SomeStruct`
-                     if segments.len() > 1 {
-                         imports_reused_with_self.push(segments[1].ident.name);
-                         return;
-                     }
++    fn track_uses(
++        &mut self,
++        cx: &EarlyContext<'_>,
++        item: &Item,
++        imports_reused_with_self: &mut Vec<Symbol>,
++        single_use_usages: &mut Vec<SingleUse>,
++        macros: &mut Vec<Symbol>,
++    ) {
++        if item.span.from_expansion() || item.vis.kind.is_pub() {
++            return;
++        }
++
++        match &item.kind {
++            ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
++                self.check_mod(cx, items);
++            },
++            ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
++                macros.push(item.ident.name);
++            },
++            ItemKind::Use(use_tree) => {
++                let segments = &use_tree.prefix.segments;
++
++                // keep track of `use some_module;` usages
++                if segments.len() == 1 {
++                    if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
++                        let name = segments[0].ident.name;
++                        if !macros.contains(&name) {
++                            single_use_usages.push(SingleUse {
++                                name,
++                                span: item.span,
++                                item_id: item.id,
++                                can_suggest: true,
++                            });
 +                        }
 +                    }
++                    return;
 +                }
-                     // nested case such as `use self::{module1::Struct1, module2::Struct2}`
 +
-                             if !segments.is_empty() {
-                                 imports_reused_with_self.push(segments[0].ident.name);
++                if segments.is_empty() {
++                    // keep track of `use {some_module, some_other_module};` usages
 +                    if let UseTreeKind::Nested(trees) = &use_tree.kind {
 +                        for tree in trees {
 +                            let segments = &tree.0.prefix.segments;
-             }
-         },
-         _ => {},
++                            if segments.len() == 1 {
++                                if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
++                                    let name = segments[0].ident.name;
++                                    if !macros.contains(&name) {
++                                        single_use_usages.push(SingleUse {
++                                            name,
++                                            span: tree.0.span,
++                                            item_id: item.id,
++                                            can_suggest: false,
++                                        });
++                                    }
++                                }
++                            }
++                        }
++                    }
++                } else {
++                    // keep track of `use self::some_module` usages
++                    if segments[0].ident.name == kw::SelfLower {
++                        // simple case such as `use self::module::SomeStruct`
++                        if segments.len() > 1 {
++                            imports_reused_with_self.push(segments[1].ident.name);
++                            return;
++                        }
++
++                        // nested case such as `use self::{module1::Struct1, module2::Struct2}`
++                        if let UseTreeKind::Nested(trees) = &use_tree.kind {
++                            for tree in trees {
++                                let segments = &tree.0.prefix.segments;
++                                if !segments.is_empty() {
++                                    imports_reused_with_self.push(segments[0].ident.name);
++                                }
 +                            }
 +                        }
 +                    }
 +                }
++            },
++            _ => {},
++        }
 +    }
 +}
index 760399231513f8a4b15668e35fb606f1745f313e,0000000000000000000000000000000000000000..a2109038a05782900e88d1d751b7aa5244040aca
mode 100644,000000..100644
--- /dev/null
@@@ -1,305 -1,0 +1,305 @@@
-     fn emit_lint<'tcx>(cx: &LateContext<'tcx>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{
 +    get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
 +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, PatKind, QPath, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks slow zero-filled vector initialization
 +    ///
 +    /// ### Why is this bad?
 +    /// These structures are non-idiomatic and less efficient than simply using
 +    /// `vec![0; len]`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use core::iter::repeat;
 +    /// # let len = 4;
 +    /// let mut vec1 = Vec::with_capacity(len);
 +    /// vec1.resize(len, 0);
 +    ///
 +    /// let mut vec1 = Vec::with_capacity(len);
 +    /// vec1.resize(vec1.capacity(), 0);
 +    ///
 +    /// let mut vec2 = Vec::with_capacity(len);
 +    /// vec2.extend(repeat(0).take(len));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let len = 4;
 +    /// let mut vec1 = vec![0; len];
 +    /// let mut vec2 = vec![0; len];
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub SLOW_VECTOR_INITIALIZATION,
 +    perf,
 +    "slow vector initialization"
 +}
 +
 +declare_lint_pass!(SlowVectorInit => [SLOW_VECTOR_INITIALIZATION]);
 +
 +/// `VecAllocation` contains data regarding a vector allocated with `with_capacity` and then
 +/// assigned to a variable. For example, `let mut vec = Vec::with_capacity(0)` or
 +/// `vec = Vec::with_capacity(0)`
 +struct VecAllocation<'tcx> {
 +    /// HirId of the variable
 +    local_id: HirId,
 +
 +    /// Reference to the expression which allocates the vector
 +    allocation_expr: &'tcx Expr<'tcx>,
 +
 +    /// Reference to the expression used as argument on `with_capacity` call. This is used
 +    /// to only match slow zero-filling idioms of the same length than vector initialization.
 +    len_expr: &'tcx Expr<'tcx>,
 +}
 +
 +/// Type of slow initialization
 +enum InitializationType<'tcx> {
 +    /// Extend is a slow initialization with the form `vec.extend(repeat(0).take(..))`
 +    Extend(&'tcx Expr<'tcx>),
 +
 +    /// Resize is a slow initialization with the form `vec.resize(.., 0)`
 +    Resize(&'tcx Expr<'tcx>),
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Matches initialization on reassignements. For example: `vec = Vec::with_capacity(100)`
 +        if_chain! {
 +            if let ExprKind::Assign(left, right, _) = expr.kind;
 +
 +            // Extract variable
 +            if let Some(local_id) = path_to_local(left);
 +
 +            // Extract len argument
 +            if let Some(len_arg) = Self::is_vec_with_capacity(cx, right);
 +
 +            then {
 +                let vi = VecAllocation {
 +                    local_id,
 +                    allocation_expr: right,
 +                    len_expr: len_arg,
 +                };
 +
 +                Self::search_initialization(cx, vi, expr.hir_id);
 +            }
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        // Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
 +        if_chain! {
 +            if let StmtKind::Local(local) = stmt.kind;
 +            if let PatKind::Binding(BindingAnnotation::MUT, local_id, _, None) = local.pat.kind;
 +            if let Some(init) = local.init;
 +            if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
 +
 +            then {
 +                let vi = VecAllocation {
 +                    local_id,
 +                    allocation_expr: init,
 +                    len_expr: len_arg,
 +                };
 +
 +                Self::search_initialization(cx, vi, stmt.hir_id);
 +            }
 +        }
 +    }
 +}
 +
 +impl SlowVectorInit {
 +    /// Checks if the given expression is `Vec::with_capacity(..)`. It will return the expression
 +    /// of the first argument of `with_capacity` call if it matches or `None` if it does not.
 +    fn is_vec_with_capacity<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = expr.kind;
 +            if let ExprKind::Path(QPath::TypeRelative(ty, name)) = func.kind;
 +            if name.ident.as_str() == "with_capacity";
 +            if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec);
 +            then {
 +                Some(arg)
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +
 +    /// Search initialization for the given vector
 +    fn search_initialization<'tcx>(cx: &LateContext<'tcx>, vec_alloc: VecAllocation<'tcx>, parent_node: HirId) {
 +        let enclosing_body = get_enclosing_block(cx, parent_node);
 +
 +        if enclosing_body.is_none() {
 +            return;
 +        }
 +
 +        let mut v = VectorInitializationVisitor {
 +            cx,
 +            vec_alloc,
 +            slow_expression: None,
 +            initialization_found: false,
 +        };
 +
 +        v.visit_block(enclosing_body.unwrap());
 +
 +        if let Some(ref allocation_expr) = v.slow_expression {
 +            Self::lint_initialization(cx, allocation_expr, &v.vec_alloc);
 +        }
 +    }
 +
 +    fn lint_initialization<'tcx>(
 +        cx: &LateContext<'tcx>,
 +        initialization: &InitializationType<'tcx>,
 +        vec_alloc: &VecAllocation<'_>,
 +    ) {
 +        match initialization {
 +            InitializationType::Extend(e) | InitializationType::Resize(e) => {
 +                Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization");
 +            },
 +        };
 +    }
 +
++    fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &str) {
 +        let len_expr = Sugg::hir(cx, vec_alloc.len_expr, "len");
 +
 +        span_lint_and_then(cx, SLOW_VECTOR_INITIALIZATION, slow_fill.span, msg, |diag| {
 +            diag.span_suggestion(
 +                vec_alloc.allocation_expr.span,
 +                "consider replace allocation with",
 +                format!("vec![0; {len_expr}]"),
 +                Applicability::Unspecified,
 +            );
 +        });
 +    }
 +}
 +
 +/// `VectorInitializationVisitor` searches for unsafe or slow vector initializations for the given
 +/// vector.
 +struct VectorInitializationVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +
 +    /// Contains the information.
 +    vec_alloc: VecAllocation<'tcx>,
 +
 +    /// Contains the slow initialization expression, if one was found.
 +    slow_expression: Option<InitializationType<'tcx>>,
 +
 +    /// `true` if the initialization of the vector has been found on the visited block.
 +    initialization_found: bool,
 +}
 +
 +impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
 +    /// Checks if the given expression is extending a vector with `repeat(0).take(..)`
 +    fn search_slow_extend_filling(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if self.initialization_found;
 +            if let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind;
 +            if path_to_local_id(self_arg, self.vec_alloc.local_id);
 +            if path.ident.name == sym!(extend);
 +            if self.is_repeat_take(extend_arg);
 +
 +            then {
 +                self.slow_expression = Some(InitializationType::Extend(expr));
 +            }
 +        }
 +    }
 +
 +    /// Checks if the given expression is resizing a vector with 0
 +    fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.initialization_found
 +            && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind
 +            && path_to_local_id(self_arg, self.vec_alloc.local_id)
 +            && path.ident.name == sym!(resize)
 +            // Check that is filled with 0
 +            && is_integer_literal(fill_arg, 0) {
 +                // Check that len expression is equals to `with_capacity` expression
 +                if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
 +                    self.slow_expression = Some(InitializationType::Resize(expr));
 +                } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
 +                    self.slow_expression = Some(InitializationType::Resize(expr));
 +                }
 +            }
 +    }
 +
 +    /// Returns `true` if give expression is `repeat(0).take(...)`
 +    fn is_repeat_take(&self, expr: &Expr<'_>) -> bool {
 +        if_chain! {
 +            if let ExprKind::MethodCall(take_path, recv, [len_arg, ..], _) = expr.kind;
 +            if take_path.ident.name == sym!(take);
 +            // Check that take is applied to `repeat(0)`
 +            if self.is_repeat_zero(recv);
 +            then {
 +                // Check that len expression is equals to `with_capacity` expression
 +                if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
 +                    return true;
 +                } else if let ExprKind::MethodCall(path, ..) = len_arg.kind && path.ident.as_str() == "capacity" {
 +                    return true;
 +                }
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    /// Returns `true` if given expression is `repeat(0)`
 +    fn is_repeat_zero(&self, expr: &Expr<'_>) -> bool {
 +        if_chain! {
 +            if let ExprKind::Call(fn_expr, [repeat_arg]) = expr.kind;
 +            if is_path_diagnostic_item(self.cx, fn_expr, sym::iter_repeat);
 +            if is_integer_literal(repeat_arg, 0);
 +            then {
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VectorInitializationVisitor<'a, 'tcx> {
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        if self.initialization_found {
 +            match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                    self.search_slow_extend_filling(expr);
 +                    self.search_slow_resize_filling(expr);
 +                },
 +                _ => (),
 +            }
 +
 +            self.initialization_found = false;
 +        } else {
 +            walk_stmt(self, stmt);
 +        }
 +    }
 +
 +    fn visit_block(&mut self, block: &'tcx Block<'_>) {
 +        if self.initialization_found {
 +            if let Some(s) = block.stmts.get(0) {
 +                self.visit_stmt(s);
 +            }
 +
 +            self.initialization_found = false;
 +        } else {
 +            walk_block(self, block);
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        // Skip all the expressions previous to the vector initialization
 +        if self.vec_alloc.allocation_expr.hir_id == expr.hir_id {
 +            self.initialization_found = true;
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..301aa5798bf556eeeb4bf2389fc671c25c08cbf0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++use clippy_utils::{numeric_literal::NumericLiteral, source::snippet_with_context};
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal.
++    /// ### Why is this bad?
++    ///       It's most probably a typo and may lead to unexpected behaviours.
++    /// ### Example
++    /// ```rust
++    /// let x = 3_i32 ^ 4_i32;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let x = 3_i32.pow(4);
++    /// ```
++    #[clippy::version = "1.66.0"]
++    pub SUSPICIOUS_XOR_USED_AS_POW,
++    restriction,
++    "XOR (`^`) operator possibly used as exponentiation operator"
++}
++declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]);
++
++impl LateLintPass<'_> for ConfusingXorAndPow {
++    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
++        if !in_external_macro(cx.sess(), expr.span) &&
++              let ExprKind::Binary(op, left, right) = &expr.kind &&
++            op.node == BinOpKind::BitXor &&
++            left.span.ctxt() == right.span.ctxt() &&
++            let ExprKind::Lit(lit_left) = &left.kind &&
++            let ExprKind::Lit(lit_right) = &right.kind &&
++            let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
++            let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) &&
++            let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) &&
++            let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) &&
++                      left_val.is_decimal() &&
++                      right_val.is_decimal() {
++                                      clippy_utils::diagnostics::span_lint_and_sugg(
++                                              cx,
++                                              SUSPICIOUS_XOR_USED_AS_POW,
++                                              expr.span,
++                                              "`^` is not the exponentiation operator",
++                                              "did you mean to write",
++                                              format!("{}.pow({})", left_val.format(), right_val.format()),
++                                              Applicability::MaybeIncorrect,
++                                          );
++              }
++    }
++}
index f46c21e126552f1681438ac10ae6399a2776a963,0000000000000000000000000000000000000000..c374529d1ea9f5abc9fd54bf49a36163c47463e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,255 -1,0 +1,261 @@@
- use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual swapping.
 +    ///
++    /// Note that the lint will not be emitted in const blocks, as the suggestion would not be applicable.
++    ///
 +    /// ### Why is this bad?
 +    /// The `std::mem::swap` function exposes the intent better
 +    /// without deinitializing or copying either variable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = 42;
 +    /// let mut b = 1337;
 +    ///
 +    /// let t = b;
 +    /// b = a;
 +    /// a = t;
 +    /// ```
 +    /// Use std::mem::swap():
 +    /// ```rust
 +    /// let mut a = 1;
 +    /// let mut b = 2;
 +    /// std::mem::swap(&mut a, &mut b);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MANUAL_SWAP,
 +    complexity,
 +    "manual swap of two variables"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `foo = bar; bar = foo` sequences.
 +    ///
 +    /// ### Why is this bad?
 +    /// This looks like a failed attempt to swap.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut a = 1;
 +    /// # let mut b = 2;
 +    /// a = b;
 +    /// b = a;
 +    /// ```
 +    /// If swapping is intended, use `swap()` instead:
 +    /// ```rust
 +    /// # let mut a = 1;
 +    /// # let mut b = 2;
 +    /// std::mem::swap(&mut a, &mut b);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ALMOST_SWAPPED,
 +    correctness,
 +    "`foo = bar; bar = foo` sequence"
 +}
 +
 +declare_lint_pass!(Swap => [MANUAL_SWAP, ALMOST_SWAPPED]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Swap {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
 +        check_manual_swap(cx, block);
 +        check_suspicious_swap(cx, block);
 +        check_xor_swap(cx, block);
 +    }
 +}
 +
 +fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, span: Span, is_xor_based: bool) {
 +    let mut applicability = Applicability::MachineApplicable;
 +
 +    if !can_mut_borrow_both(cx, e1, e2) {
 +        if let ExprKind::Index(lhs1, idx1) = e1.kind {
 +            if let ExprKind::Index(lhs2, idx2) = e2.kind {
 +                if eq_expr_value(cx, lhs1, lhs2) {
 +                    let ty = cx.typeck_results().expr_ty(lhs1).peel_refs();
 +
 +                    if matches!(ty.kind(), ty::Slice(_))
 +                        || matches!(ty.kind(), ty::Array(_, _))
 +                        || is_type_diagnostic_item(cx, ty, sym::Vec)
 +                        || is_type_diagnostic_item(cx, ty, sym::VecDeque)
 +                    {
 +                        let slice = Sugg::hir_with_applicability(cx, lhs1, "<slice>", &mut applicability);
 +                        span_lint_and_sugg(
 +                            cx,
 +                            MANUAL_SWAP,
 +                            span,
 +                            &format!("this looks like you are swapping elements of `{slice}` manually"),
 +                            "try",
 +                            format!(
 +                                "{}.swap({}, {})",
 +                                slice.maybe_par(),
 +                                snippet_with_applicability(cx, idx1.span, "..", &mut applicability),
 +                                snippet_with_applicability(cx, idx2.span, "..", &mut applicability),
 +                            ),
 +                            applicability,
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +        return;
 +    }
 +
 +    let first = Sugg::hir_with_applicability(cx, e1, "..", &mut applicability);
 +    let second = Sugg::hir_with_applicability(cx, e2, "..", &mut applicability);
 +    let Some(sugg) = std_or_core(cx) else { return };
 +
 +    span_lint_and_then(
 +        cx,
 +        MANUAL_SWAP,
 +        span,
 +        &format!("this looks like you are swapping `{first}` and `{second}` manually"),
 +        |diag| {
 +            diag.span_suggestion(
 +                span,
 +                "try",
 +                format!("{sugg}::mem::swap({}, {})", first.mut_addr(), second.mut_addr()),
 +                applicability,
 +            );
 +            if !is_xor_based {
 +                diag.note(&format!("or maybe you should use `{sugg}::mem::replace`?"));
 +            }
 +        },
 +    );
 +}
 +
 +/// Implementation of the `MANUAL_SWAP` lint.
 +fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
++    if in_constant(cx, block.hir_id) {
++        return;
++    }
++
 +    for w in block.stmts.windows(3) {
 +        if_chain! {
 +            // let t = foo();
 +            if let StmtKind::Local(tmp) = w[0].kind;
 +            if let Some(tmp_init) = tmp.init;
 +            if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
 +
 +            // foo() = bar();
 +            if let StmtKind::Semi(first) = w[1].kind;
 +            if let ExprKind::Assign(lhs1, rhs1, _) = first.kind;
 +
 +            // bar() = t;
 +            if let StmtKind::Semi(second) = w[2].kind;
 +            if let ExprKind::Assign(lhs2, rhs2, _) = second.kind;
 +            if let ExprKind::Path(QPath::Resolved(None, rhs2)) = rhs2.kind;
 +            if rhs2.segments.len() == 1;
 +
 +            if ident.name == rhs2.segments[0].ident.name;
 +            if eq_expr_value(cx, tmp_init, lhs1);
 +            if eq_expr_value(cx, rhs1, lhs2);
 +            then {
 +                let span = w[0].span.to(second.span);
 +                generate_swap_warning(cx, lhs1, lhs2, span, false);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `ALMOST_SWAPPED` lint.
 +fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
 +    for w in block.stmts.windows(2) {
 +        if_chain! {
 +            if let StmtKind::Semi(first) = w[0].kind;
 +            if let StmtKind::Semi(second) = w[1].kind;
 +            if first.span.ctxt() == second.span.ctxt();
 +            if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
 +            if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
 +            if eq_expr_value(cx, lhs0, rhs1);
 +            if eq_expr_value(cx, lhs1, rhs0);
 +            then {
 +                let lhs0 = Sugg::hir_opt(cx, lhs0);
 +                let rhs0 = Sugg::hir_opt(cx, rhs0);
 +                let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
 +                    (
 +                        format!(" `{first}` and `{second}`"),
 +                        first.mut_addr().to_string(),
 +                        second.mut_addr().to_string(),
 +                    )
 +                } else {
 +                    (String::new(), String::new(), String::new())
 +                };
 +
 +                let span = first.span.to(second.span);
 +                let Some(sugg) = std_or_core(cx) else { return };
 +
 +                span_lint_and_then(cx,
 +                    ALMOST_SWAPPED,
 +                    span,
 +                    &format!("this looks like you are trying to swap{what}"),
 +                    |diag| {
 +                        if !what.is_empty() {
 +                            diag.span_suggestion(
 +                                span,
 +                                "try",
 +                                format!(
 +                                    "{sugg}::mem::swap({lhs}, {rhs})",
 +                                ),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                            diag.note(
 +                                &format!("or maybe you should use `{sugg}::mem::replace`?")
 +                            );
 +                        }
 +                    });
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the xor case for `MANUAL_SWAP` lint.
 +fn check_xor_swap(cx: &LateContext<'_>, block: &Block<'_>) {
 +    for window in block.stmts.windows(3) {
 +        if_chain! {
 +            if let Some((lhs0, rhs0)) = extract_sides_of_xor_assign(&window[0]);
 +            if let Some((lhs1, rhs1)) = extract_sides_of_xor_assign(&window[1]);
 +            if let Some((lhs2, rhs2)) = extract_sides_of_xor_assign(&window[2]);
 +            if eq_expr_value(cx, lhs0, rhs1);
 +            if eq_expr_value(cx, lhs2, rhs1);
 +            if eq_expr_value(cx, lhs1, rhs0);
 +            if eq_expr_value(cx, lhs1, rhs2);
 +            then {
 +                let span = window[0].span.to(window[2].span);
 +                generate_swap_warning(cx, lhs0, rhs0, span, true);
 +            }
 +        };
 +    }
 +}
 +
 +/// Returns the lhs and rhs of an xor assignment statement.
 +fn extract_sides_of_xor_assign<'a, 'hir>(stmt: &'a Stmt<'hir>) -> Option<(&'a Expr<'hir>, &'a Expr<'hir>)> {
 +    if let StmtKind::Semi(expr) = stmt.kind {
 +        if let ExprKind::AssignOp(
 +            Spanned {
 +                node: BinOpKind::BitXor,
 +                ..
 +            },
 +            lhs,
 +            rhs,
 +        ) = expr.kind
 +        {
 +            return Some((lhs, rhs));
 +        }
 +    }
 +    None
 +}
index 8cf3efc8dc73e8df43334c4aa3d73bfd4d094371,0000000000000000000000000000000000000000..63b326048a48fe2c1f4322daa6b3d8fe10fbe573
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,74 @@@
- use rustc_hir::{HirId, Item, ItemKind};
 +use clippy_utils::diagnostics::span_lint_and_help;
- use rustc_span::sym;
++use clippy_utils::has_repr_attr;
++use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::Const;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
- fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
-     cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr))
- }
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute.
 +    ///
 +    /// ### Why is this bad?
 +    /// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjunction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct RarelyUseful {
 +    ///     some_field: u32,
 +    ///     last: [u32; 0],
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// #[repr(C)]
 +    /// struct MoreOftenUseful {
 +    ///     some_field: usize,
 +    ///     last: [u32; 0],
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TRAILING_EMPTY_ARRAY,
 +    nursery,
 +    "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
 +}
 +declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) {
 +            span_lint_and_help(
 +                cx,
 +                TRAILING_EMPTY_ARRAY,
 +                item.span,
 +                "trailing zero-sized array in a struct which is not marked with a `repr` attribute",
 +                None,
 +                &format!(
 +                    "consider annotating `{}` with `#[repr(C)]` or another `repr` attribute",
 +                    cx.tcx.def_path_str(item.owner_id.to_def_id())
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if_chain! {
 +        // First check if last field is an array
 +        if let ItemKind::Struct(data, _) = &item.kind;
 +        if let Some(last_field) = data.fields().last();
 +        if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind;
 +
 +        // Then check if that that array zero-sized
 +        let length_ldid = cx.tcx.hir().local_def_id(length.hir_id);
 +        let length = Const::from_anon_const(cx.tcx, length_ldid);
 +        let length = length.try_eval_usize(cx.tcx, cx.param_env);
 +        if let Some(length) = length;
 +        then {
 +            length == 0
 +        } else {
 +            false
 +        }
 +    }
 +}
index 3d4bbbf648c65bbfdc37ee89d0e8b4f412cb18e5,0000000000000000000000000000000000000000..34642f4b122f22f097df02fada46a66a69129192
mode 100644,000000..100644
--- /dev/null
@@@ -1,368 -1,0 +1,336 @@@
-             (
-                 ReducedTy::OrderedFields(_, Some(from_sub_ty)),
-                 ReducedTy::UnorderedFields(to_sub_ty),
-             )
-             | (
-                 ReducedTy::UnorderedFields(from_sub_ty),
-                 ReducedTy::OrderedFields(_, Some(to_sub_ty)),
-             ) => {
 +use super::TRANSMUTE_UNDEFINED_REPR;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::ty::is_c_void;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::SubstsRef;
 +use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
 +
 +#[expect(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty_orig: Ty<'tcx>,
 +    to_ty_orig: Ty<'tcx>,
 +) -> bool {
 +    let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
 +    let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
 +
 +    while from_ty != to_ty {
 +        let reduced_tys = reduce_refs(cx, from_ty, to_ty);
 +        match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) {
 +            // Various forms of type erasure.
 +            (ReducedTy::TypeErasure { raw_ptr_only: false }, _)
 +            | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false,
 +            (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false,
 +            (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false,
 +
 +            // `Repr(C)` <-> unordered type.
 +            // If the first field of the `Repr(C)` type matches then the transmute is ok
-             }
-             (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty))
-                 if reduced_tys.to_fat_ptr =>
-             {
++            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty))
++            | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
 +                from_ty = from_sub_ty;
 +                to_ty = to_sub_ty;
 +                continue;
-             }
++            },
++            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
 +                from_ty = from_sub_ty;
 +                to_ty = to_sub_ty;
 +                continue;
-             }
++            },
 +            (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
 +                if reduced_tys.from_fat_ptr =>
 +            {
 +                from_ty = from_sub_ty;
 +                to_ty = to_sub_ty;
 +                continue;
-             }
++            },
 +
 +            // ptr <-> ptr
 +            (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty))
 +                if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_))
 +                    && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) =>
 +            {
 +                from_ty = from_sub_ty;
 +                to_ty = to_sub_ty;
 +                continue;
-             }
++            },
 +
 +            // fat ptr <-> (*size, *size)
 +            (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty))
 +                if reduced_tys.from_fat_ptr && is_size_pair(to_ty) =>
 +            {
 +                return false;
-             }
++            },
 +            (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_))
 +                if reduced_tys.to_fat_ptr && is_size_pair(from_ty) =>
 +            {
 +                return false;
-                             diag.note(&format!(
-                                 "the contained type `{from_ty}` has an undefined layout"
-                             ));
++            },
 +
 +            // fat ptr -> some struct | some struct -> fat ptr
 +            (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => {
 +                span_lint_and_then(
 +                    cx,
 +                    TRANSMUTE_UNDEFINED_REPR,
 +                    e.span,
 +                    &format!("transmute from `{from_ty_orig}` which has an undefined layout"),
 +                    |diag| {
 +                        if from_ty_orig.peel_refs() != from_ty.peel_refs() {
-             }
++                            diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
 +                        }
 +                    },
 +                );
 +                return true;
-                             diag.note(&format!(
-                                 "the contained type `{to_ty}` has an undefined layout"
-                             ));
++            },
 +            (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => {
 +                span_lint_and_then(
 +                    cx,
 +                    TRANSMUTE_UNDEFINED_REPR,
 +                    e.span,
 +                    &format!("transmute to `{to_ty_orig}` which has an undefined layout"),
 +                    |diag| {
 +                        if to_ty_orig.peel_refs() != to_ty.peel_refs() {
-             }
++                            diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
 +                        }
 +                    },
 +                );
 +                return true;
-             (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty))
-                 if from_ty != to_ty =>
-             {
++            },
 +
-                                 diag.note(&format!(
-                                     "the contained type `{from_ty}` has an undefined layout"
-                                 ));
++            (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
 +                let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
 +                        = (from_ty.kind(), to_ty.kind())
 +                        && from_def == to_def
 +                    {
 +                        if same_except_params(from_subs, to_subs) {
 +                            return false;
 +                        }
 +                        Some(from_def.did())
 +                    } else {
 +                        None
 +                    };
 +                span_lint_and_then(
 +                    cx,
 +                    TRANSMUTE_UNDEFINED_REPR,
 +                    e.span,
 +                    &format!(
 +                        "transmute from `{from_ty_orig}` to `{to_ty_orig}`, both of which have an undefined layout"
 +                    ),
 +                    |diag| {
 +                        if let Some(same_adt_did) = same_adt_did {
 +                            diag.note(&format!(
 +                                "two instances of the same generic type (`{}`) may have different layouts",
 +                                cx.tcx.item_name(same_adt_did)
 +                            ));
 +                        } else {
 +                            if from_ty_orig.peel_refs() != from_ty {
-                                 diag.note(&format!(
-                                     "the contained type `{to_ty}` has an undefined layout"
-                                 ));
++                                diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
 +                            }
 +                            if to_ty_orig.peel_refs() != to_ty {
-             }
++                                diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
 +                            }
 +                        }
 +                    },
 +                );
 +                return true;
-                 ReducedTy::Other(_)
-                 | ReducedTy::OrderedFields(..)
-                 | ReducedTy::TypeErasure { raw_ptr_only: true },
++            },
 +            (
 +                ReducedTy::UnorderedFields(from_ty),
-                             diag.note(&format!(
-                                 "the contained type `{from_ty}` has an undefined layout"
-                             ));
++                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
 +            ) => {
 +                span_lint_and_then(
 +                    cx,
 +                    TRANSMUTE_UNDEFINED_REPR,
 +                    e.span,
 +                    &format!("transmute from `{from_ty_orig}` which has an undefined layout"),
 +                    |diag| {
 +                        if from_ty_orig.peel_refs() != from_ty {
-             }
++                            diag.note(&format!("the contained type `{from_ty}` has an undefined layout"));
 +                        }
 +                    },
 +                );
 +                return true;
-                 ReducedTy::Other(_)
-                 | ReducedTy::OrderedFields(..)
-                 | ReducedTy::TypeErasure { raw_ptr_only: true },
++            },
 +            (
-                             diag.note(&format!(
-                                 "the contained type `{to_ty}` has an undefined layout"
-                             ));
++                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
 +                ReducedTy::UnorderedFields(to_ty),
 +            ) => {
 +                span_lint_and_then(
 +                    cx,
 +                    TRANSMUTE_UNDEFINED_REPR,
 +                    e.span,
 +                    &format!("transmute into `{to_ty_orig}` which has an undefined layout"),
 +                    |diag| {
 +                        if to_ty_orig.peel_refs() != to_ty {
-             }
++                            diag.note(&format!("the contained type `{to_ty}` has an undefined layout"));
 +                        }
 +                    },
 +                );
 +                return true;
-                 ReducedTy::OrderedFields(..)
-                 | ReducedTy::Other(_)
-                 | ReducedTy::TypeErasure { raw_ptr_only: true },
-                 ReducedTy::OrderedFields(..)
-                 | ReducedTy::Other(_)
-                 | ReducedTy::TypeErasure { raw_ptr_only: true },
++            },
 +            (
-             }
++                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
++                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
 +            )
 +            | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => {
 +                break;
- fn reduce_refs<'tcx>(
-     cx: &LateContext<'tcx>,
-     mut from_ty: Ty<'tcx>,
-     mut to_ty: Ty<'tcx>,
- ) -> ReducedTys<'tcx> {
++            },
 +        }
 +    }
 +
 +    false
 +}
 +
 +#[expect(clippy::struct_excessive_bools)]
 +struct ReducedTys<'tcx> {
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +    from_raw_ptr: bool,
 +    to_raw_ptr: bool,
 +    from_fat_ptr: bool,
 +    to_fat_ptr: bool,
 +}
 +
 +/// Remove references so long as both types are references.
-     let (from_fat_ptr, to_fat_ptr) =
-         loop {
-             break match (from_ty.kind(), to_ty.kind()) {
-                 (
-                     &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
-                     &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
-                 ) => {
-                     from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
-                     from_ty = from_sub_ty;
-                     to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
-                     to_ty = to_sub_ty;
-                     continue;
-                 }
-                 (
-                     &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })),
-                     _,
-                 ) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => (true, false),
-                 (
-                     _,
-                     &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })),
-                 ) if !unsized_ty.is_sized(cx.tcx, cx.param_env) => (false, true),
-                 _ => (false, false),
-             };
++fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> {
 +    let mut from_raw_ptr = false;
 +    let mut to_raw_ptr = false;
-     ReducedTys { from_ty, to_ty, from_raw_ptr, to_raw_ptr, from_fat_ptr, to_fat_ptr }
++    let (from_fat_ptr, to_fat_ptr) = loop {
++        break match (from_ty.kind(), to_ty.kind()) {
++            (
++                &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
++                &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
++            ) => {
++                from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
++                from_ty = from_sub_ty;
++                to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
++                to_ty = to_sub_ty;
++                continue;
++            },
++            (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
++                if !unsized_ty.is_sized(cx.tcx, cx.param_env) =>
++            {
++                (true, false)
++            },
++            (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
++                if !unsized_ty.is_sized(cx.tcx, cx.param_env) =>
++            {
++                (false, true)
++            },
++            _ => (false, false),
 +        };
-             }
++    };
++    ReducedTys {
++        from_ty,
++        to_ty,
++        from_raw_ptr,
++        to_raw_ptr,
++        from_fat_ptr,
++        to_fat_ptr,
++    }
 +}
 +
 +enum ReducedTy<'tcx> {
 +    /// The type can be used for type erasure.
 +    TypeErasure { raw_ptr_only: bool },
 +    /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
 +    /// sized fields with a defined order.
 +    /// The second value is the first non-zero sized type.
 +    OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>),
 +    /// The type is a struct containing multiple non-zero sized fields with no defined order.
 +    UnorderedFields(Ty<'tcx>),
 +    /// Any other type.
 +    Other(Ty<'tcx>),
 +}
 +
 +/// Reduce structs containing a single non-zero sized field to it's contained type.
 +fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
 +    loop {
 +        ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
 +        return match *ty.kind() {
 +            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
 +                ReducedTy::TypeErasure { raw_ptr_only: false }
-             }
++            },
 +            ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
 +                ty = sub_ty;
 +                continue;
-             }
++            },
 +            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false },
 +            ty::Tuple(args) => {
 +                let mut iter = args.iter();
 +                let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
 +                    return ReducedTy::OrderedFields(ty, None);
 +                };
 +                if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
 +                    ty = sized_ty;
 +                    continue;
 +                }
 +                ReducedTy::UnorderedFields(ty)
-             }
-             ty::Adt(def, _)
-                 if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) =>
-             {
++            },
 +            ty::Adt(def, substs) if def.is_struct() => {
 +                let mut iter = def
 +                    .non_enum_variant()
 +                    .fields
 +                    .iter()
 +                    .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
 +                let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
 +                    return ReducedTy::TypeErasure { raw_ptr_only: false };
 +                };
 +                if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
 +                    ty = sized_ty;
 +                    continue;
 +                }
 +                if def.repr().inhibit_struct_field_reordering_opt() {
 +                    ReducedTy::OrderedFields(ty, Some(sized_ty))
 +                } else {
 +                    ReducedTy::UnorderedFields(ty)
 +                }
-             }
++            },
++            ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
 +                ReducedTy::TypeErasure { raw_ptr_only: false }
-             (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2))
-                 if adt1 == adt2 && same_except_params(subs1, subs2) =>
-             {
-                 ()
-             }
++            },
 +            // TODO: Check if the conversion to or from at least one of a union's fields is valid.
 +            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false },
 +            ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false },
 +            ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true },
 +            _ => ReducedTy::Other(ty),
 +        };
 +    }
 +}
 +
 +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if_chain! {
 +        if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty);
 +        if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
 +        then {
 +            layout.layout.size().bytes() == 0
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn is_size_pair(ty: Ty<'_>) -> bool {
 +    if let ty::Tuple(tys) = *ty.kind()
 +        && let [ty1, ty2] = &**tys
 +    {
 +        matches!(ty1.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +            && matches!(ty2.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> bool {
 +    // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as
 +    // `Array<6>`
 +    for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) {
 +        match (ty1.kind(), ty2.kind()) {
 +            (ty::Param(_), _) | (_, ty::Param(_)) => (),
++            (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (),
 +            _ => return false,
 +        }
 +    }
 +    true
 +}
index 641cdf5d330e1a21e13b2a1205e33a97fc6eac6e,0000000000000000000000000000000000000000..49d863ec03f1d014c575c776da02a9b3c712d1ba
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,82 @@@
- use rustc_hir as hir;
++use rustc_hir as hir;
 +use rustc_hir::Expr;
 +use rustc_hir_typeck::{cast, FnCtxt, Inherited};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{cast::CastKind, Ty};
 +use rustc_span::DUMMY_SP;
-             &fn_ctxt, e, from_ty, to_ty,
 +
 +// check if the component types of the transmuted collection and the result have different ABI,
 +// size or alignment
 +pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
 +    if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
 +        && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
 +        && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
 +        && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
 +    {
 +        from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
 +    } else {
 +        // no idea about layout, so don't lint
 +        false
 +    }
 +}
 +
 +/// Check if the type conversion can be expressed as a pointer cast, instead of
 +/// a transmute. In certain cases, including some invalid casts from array
 +/// references to pointers, this may cause additional errors to be emitted and/or
 +/// ICE error messages. This function will panic if that occurs.
 +pub(super) fn can_be_expressed_as_pointer_cast<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +) -> bool {
 +    use CastKind::{AddrPtrCast, ArrayPtrCast, FnPtrAddrCast, FnPtrPtrCast, PtrAddrCast, PtrPtrCast};
 +    matches!(
 +        check_cast(cx, e, from_ty, to_ty),
 +        Some(PtrPtrCast | PtrAddrCast | AddrPtrCast | ArrayPtrCast | FnPtrPtrCast | FnPtrAddrCast)
 +    )
 +}
 +
 +/// If a cast from `from_ty` to `to_ty` is valid, returns an Ok containing the kind of
 +/// the cast. In certain cases, including some invalid casts from array references
 +/// to pointers, this may cause additional errors to be emitted and/or ICE error
 +/// messages. This function will panic if that occurs.
 +fn check_cast<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> Option<CastKind> {
 +    let hir_id = e.hir_id;
 +    let local_def_id = hir_id.owner.def_id;
 +
 +    Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
 +        let fn_ctxt = FnCtxt::new(inherited, cx.param_env, hir_id);
 +
 +        // If we already have errors, we can't be sure we can pointer cast.
 +        assert!(
 +            !fn_ctxt.errors_reported_since_creation(),
 +            "Newly created FnCtxt contained errors"
 +        );
 +
 +        if let Ok(check) = cast::CastCheck::new(
-             DUMMY_SP, DUMMY_SP, hir::Constness::NotConst,
++            &fn_ctxt,
++            e,
++            from_ty,
++            to_ty,
 +            // We won't show any error to the user, so we don't care what the span is here.
++            DUMMY_SP,
++            DUMMY_SP,
++            hir::Constness::NotConst,
 +        ) {
 +            let res = check.do_check(&fn_ctxt);
 +
 +            // do_check's documentation says that it might return Ok and create
 +            // errors in the fcx instead of returning Err in some cases. Those cases
 +            // should be filtered out before getting here.
 +            assert!(
 +                !fn_ctxt.errors_reported_since_creation(),
 +                "`fn_ctxt` contained errors after cast check!"
 +            );
 +
 +            res.ok()
 +        } else {
 +            None
 +        }
 +    })
 +}
index 802415e163df541c83b28a9dbcb2b7469be5854e,0000000000000000000000000000000000000000..43665a922d444750b6445975dd00fb12625fa1e2
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,62 @@@
-         .filter(|&name| matches!(name, sym::HashMap | sym::Vec | sym::HashSet
-             | sym::VecDeque
-             | sym::LinkedList
-             | sym::BTreeMap
-             | sym::BTreeSet
-             | sym::BinaryHeap))
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::{path_def_id, qpath_generic_tys};
 +use rustc_hir::{self as hir, def_id::DefId, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::{sym, Symbol};
 +
 +use super::BOX_COLLECTION;
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
 +    if_chain! {
 +        if Some(def_id) == cx.tcx.lang_items().owned_box();
 +        if let Some(item_type) = get_std_collection(cx, qpath);
 +        then {
 +            let generic = match item_type {
 +                sym::String => "",
 +                _ => "<..>",
 +            };
 +
 +            let box_content = format!("{item_type}{generic}");
 +            span_lint_and_help(
 +                cx,
 +                BOX_COLLECTION,
 +                hir_ty.span,
 +                &format!(
 +                    "you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"),
 +                None,
 +                &format!(
 +                    "`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation")
 +            );
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
 +    let param = qpath_generic_tys(qpath).next()?;
 +    let id = path_def_id(cx, param)?;
 +    cx.tcx
 +        .get_diagnostic_name(id)
++        .filter(|&name| {
++            matches!(
++                name,
++                sym::HashMap
++                    | sym::Vec
++                    | sym::HashSet
++                    | sym::VecDeque
++                    | sym::LinkedList
++                    | sym::BTreeMap
++                    | sym::BTreeSet
++                    | sym::BinaryHeap
++            )
++        })
 +        .or_else(|| {
 +            cx.tcx
 +                .lang_items()
 +                .string()
 +                .filter(|did| id == *did)
 +                .map(|_| sym::String)
 +        })
 +}
index f6de87b0526cd2fe0b8a7ee70c1d23d0353b1d7c,0000000000000000000000000000000000000000..20978e81dc584619c5ade53fc1f15b806920fdf1
mode 100644,000000..100644
--- /dev/null
@@@ -1,576 -1,0 +1,578 @@@
-         let is_exported = cx.effective_visibilities.is_exported(cx.tcx.hir().local_def_id(field.hir_id));
 +mod borrowed_box;
 +mod box_collection;
 +mod linked_list;
 +mod option_option;
 +mod rc_buffer;
 +mod rc_mutex;
 +mod redundant_allocation;
 +mod type_complexity;
 +mod utils;
 +mod vec_box;
 +
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
 +    TraitItemKind, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Box<T>` where T is a collection such as Vec anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// Collections already keeps their contents in a separate area on
 +    /// the heap. So if you `Box` them, you just add another level of indirection
 +    /// without any benefit whatsoever.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// struct X {
 +    ///     values: Box<Vec<Foo>>,
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// struct X {
 +    ///     values: Vec<Foo>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub BOX_COLLECTION,
 +    perf,
 +    "usage of `Box<Vec<T>>`, vector elements are already on the heap"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Vec<Box<T>>` where T: Sized anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Vec` already keeps its contents in a separate area on
 +    /// the heap. So if you `Box` its contents, you just add another level of indirection.
 +    ///
 +    /// ### Known problems
 +    /// Vec<Box<T: Sized>> makes sense if T is a large type (see [#3530](https://github.com/rust-lang/rust-clippy/issues/3530),
 +    /// 1st comment).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X {
 +    ///     values: Vec<Box<i32>>,
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// struct X {
 +    ///     values: Vec<i32>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub VEC_BOX,
 +    complexity,
 +    "usage of `Vec<Box<T>>` where T: Sized, vector elements are already on the heap"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Option<Option<_>>` in function signatures and type
 +    /// definitions
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option<_>` represents an optional value. `Option<Option<_>>`
 +    /// represents an optional optional value which is logically the same thing as an optional
 +    /// value but has an unneeded extra level of wrapping.
 +    ///
 +    /// If you have a case where `Some(Some(_))`, `Some(None)` and `None` are distinct cases,
 +    /// consider a custom `enum` instead, with clear names for each case.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn get_data() -> Option<Option<u32>> {
 +    ///     None
 +    /// }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// pub enum Contents {
 +    ///     Data(Vec<u8>), // Was Some(Some(Vec<u8>))
 +    ///     NotYetFetched, // Was Some(None)
 +    ///     None,          // Was None
 +    /// }
 +    ///
 +    /// fn get_data() -> Contents {
 +    ///     Contents::None
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_OPTION,
 +    pedantic,
 +    "usage of `Option<Option<T>>`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of any `LinkedList`, suggesting to use a
 +    /// `Vec` or a `VecDeque` (formerly called `RingBuf`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Gankro says:
 +    ///
 +    /// > The TL;DR of `LinkedList` is that it's built on a massive amount of
 +    /// pointers and indirection.
 +    /// > It wastes memory, it has terrible cache locality, and is all-around slow.
 +    /// `RingBuf`, while
 +    /// > "only" amortized for push/pop, should be faster in the general case for
 +    /// almost every possible
 +    /// > workload, and isn't even amortized at all if you can predict the capacity
 +    /// you need.
 +    /// >
 +    /// > `LinkedList`s are only really good if you're doing a lot of merging or
 +    /// splitting of lists.
 +    /// > This is because they can just mangle some pointers instead of actually
 +    /// copying the data. Even
 +    /// > if you're doing a lot of insertion in the middle of the list, `RingBuf`
 +    /// can still be better
 +    /// > because of how expensive it is to seek to the middle of a `LinkedList`.
 +    ///
 +    /// ### Known problems
 +    /// False positives – the instances where using a
 +    /// `LinkedList` makes sense are few and far between, but they can still happen.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::LinkedList;
 +    /// let x: LinkedList<usize> = LinkedList::new();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LINKEDLIST,
 +    pedantic,
 +    "usage of LinkedList, usually a vector is faster, or a more specialized data structure like a `VecDeque`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `&Box<T>` anywhere in the code.
 +    /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information.
 +    ///
 +    /// ### Why is this bad?
 +    /// A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function.
 +    /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also
 +    /// auto-deref to `&T` at the function call site if passed a `&Box<T>`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo(bar: &Box<T>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo(bar: &T) { ... }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub BORROWED_BOX,
 +    complexity,
 +    "a borrow of a boxed type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of redundant allocations anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions such as `Rc<&T>`, `Rc<Rc<T>>`, `Rc<Arc<T>>`, `Rc<Box<T>>`, `Arc<&T>`, `Arc<Rc<T>>`,
 +    /// `Arc<Arc<T>>`, `Arc<Box<T>>`, `Box<&T>`, `Box<Rc<T>>`, `Box<Arc<T>>`, `Box<Box<T>>`, add an unnecessary level of indirection.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// fn foo(bar: Rc<&usize>) {}
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// fn foo(bar: &usize) {}
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub REDUNDANT_ALLOCATION,
 +    perf,
 +    "redundant allocation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
 +    /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
 +    ///
 +    /// While mutating a buffer type would still be possible with `Rc::get_mut()`, it only
 +    /// works if there are no additional references yet, which usually defeats the purpose of
 +    /// enclosing it in a shared ownership type. Instead, additionally wrapping the inner
 +    /// type with an interior mutable container (such as `RefCell` or `Mutex`) would normally
 +    /// be used.
 +    ///
 +    /// ### Known problems
 +    /// This pattern can be desirable to avoid the overhead of a `RefCell` or `Mutex` for
 +    /// cases where mutation only happens before there are any additional references.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # use std::rc::Rc;
 +    /// fn foo(interned: Rc<String>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo(interned: Rc<str>) { ... }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub RC_BUFFER,
 +    restriction,
 +    "shared ownership of a buffer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types used in structs, parameters and `let`
 +    /// declarations above a certain complexity threshold.
 +    ///
 +    /// ### Why is this bad?
 +    /// Too complex types make the code less readable. Consider
 +    /// using a `type` definition to simplify them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// struct Foo {
 +    ///     inner: Rc<Vec<Vec<Box<(u32, u32, u32, u32)>>>>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TYPE_COMPLEXITY,
 +    complexity,
 +    "usage of very complex types that might be better factored into `type` definitions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Rc<Mutex<T>>`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Rc` is used in single thread and `Mutex` is used in multi thread.
 +    /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
 +    ///
 +    /// ### Known problems
 +    /// Sometimes combining generic types can lead to the requirement that a
 +    /// type use Rc in conjunction with Mutex. We must consider those cases false positives, but
 +    /// alas they are quite hard to rule out. Luckily they are also rare.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::rc::Rc;
 +    /// use std::sync::Mutex;
 +    /// fn foo(interned: Rc<Mutex<i32>>) { ... }
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust,ignore
 +    /// use std::rc::Rc;
 +    /// use std::cell::RefCell
 +    /// fn foo(interned: Rc<RefCell<i32>>) { ... }
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub RC_MUTEX,
 +    restriction,
 +    "usage of `Rc<Mutex<T>>`"
 +}
 +
 +pub struct Types {
 +    vec_box_size_threshold: u64,
 +    type_complexity_threshold: u64,
 +    avoid_breaking_exported_api: bool,
 +}
 +
 +impl_lint_pass!(Types => [BOX_COLLECTION, VEC_BOX, OPTION_OPTION, LINKEDLIST, BORROWED_BOX, REDUNDANT_ALLOCATION, RC_BUFFER, RC_MUTEX, TYPE_COMPLEXITY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Types {
 +    fn check_fn(&mut self, cx: &LateContext<'_>, _: FnKind<'_>, decl: &FnDecl<'_>, _: &Body<'_>, _: Span, id: HirId) {
 +        let is_in_trait_impl =
 +            if let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(cx.tcx.hir().get_parent_item(id).def_id) {
 +                matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +            } else {
 +                false
 +            };
 +
 +        let is_exported = cx.effective_visibilities.is_exported(cx.tcx.hir().local_def_id(id));
 +
 +        self.check_fn_decl(
 +            cx,
 +            decl,
 +            CheckTyContext {
 +                is_in_trait_impl,
 +                is_exported,
 +                ..CheckTyContext::default()
 +            },
 +        );
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 +
 +        match item.kind {
 +            ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _) => self.check_ty(
 +                cx,
 +                ty,
 +                CheckTyContext {
 +                    is_exported,
 +                    ..CheckTyContext::default()
 +                },
 +            ),
 +            // functions, enums, structs, impls and traits are covered
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        match item.kind {
 +            ImplItemKind::Const(ty, _) => {
 +                let is_in_trait_impl = if let Some(hir::Node::Item(item)) = cx
 +                    .tcx
 +                    .hir()
 +                    .find_by_def_id(cx.tcx.hir().get_parent_item(item.hir_id()).def_id)
 +                {
 +                    matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +                } else {
 +                    false
 +                };
 +
 +                self.check_ty(
 +                    cx,
 +                    ty,
 +                    CheckTyContext {
 +                        is_in_trait_impl,
 +                        ..CheckTyContext::default()
 +                    },
 +                );
 +            },
 +            // Methods are covered by check_fn.
 +            // Type aliases are ignored because oftentimes it's impossible to
 +            // make type alias declaration in trait simpler, see #1013
 +            ImplItemKind::Fn(..) | ImplItemKind::Type(..) => (),
 +        }
 +    }
 +
 +    fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
++        let is_exported = cx
++            .effective_visibilities
++            .is_exported(cx.tcx.hir().local_def_id(field.hir_id));
 +
 +        self.check_ty(
 +            cx,
 +            field.ty,
 +            CheckTyContext {
 +                is_exported,
 +                ..CheckTyContext::default()
 +            },
 +        );
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'_>) {
 +        let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id);
 +
 +        let context = CheckTyContext {
 +            is_exported,
 +            ..CheckTyContext::default()
 +        };
 +
 +        match item.kind {
 +            TraitItemKind::Const(ty, _) | TraitItemKind::Type(_, Some(ty)) => {
 +                self.check_ty(cx, ty, context);
 +            },
 +            TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, sig.decl, context),
 +            TraitItemKind::Type(..) => (),
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
 +        if let Some(ty) = local.ty {
 +            self.check_ty(
 +                cx,
 +                ty,
 +                CheckTyContext {
 +                    is_local: true,
 +                    ..CheckTyContext::default()
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +impl Types {
 +    pub fn new(vec_box_size_threshold: u64, type_complexity_threshold: u64, avoid_breaking_exported_api: bool) -> Self {
 +        Self {
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        }
 +    }
 +
 +    fn check_fn_decl(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, context: CheckTyContext) {
 +        // Ignore functions in trait implementations as they are usually forced by the trait definition.
 +        //
 +        // FIXME: ideally we would like to warn *if the complicated type can be simplified*, but it's hard
 +        // to check.
 +        if context.is_in_trait_impl {
 +            return;
 +        }
 +
 +        for input in decl.inputs {
 +            self.check_ty(cx, input, context);
 +        }
 +
 +        if let FnRetTy::Return(ty) = decl.output {
 +            self.check_ty(cx, ty, context);
 +        }
 +    }
 +
 +    /// Recursively check for `TypePass` lints in the given type. Stop at the first
 +    /// lint found.
 +    ///
 +    /// The parameter `is_local` distinguishes the context of the type.
 +    fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, mut context: CheckTyContext) {
 +        if hir_ty.span.from_expansion() {
 +            return;
 +        }
 +
 +        // Skip trait implementations; see issue #605.
 +        if context.is_in_trait_impl {
 +            return;
 +        }
 +
 +        if !context.is_nested_call && type_complexity::check(cx, hir_ty, self.type_complexity_threshold) {
 +            return;
 +        }
 +
 +        match hir_ty.kind {
 +            TyKind::Path(ref qpath) if !context.is_local => {
 +                let hir_id = hir_ty.hir_id;
 +                let res = cx.qpath_res(qpath, hir_id);
 +                if let Some(def_id) = res.opt_def_id() {
 +                    if self.is_type_change_allowed(context) {
 +                        // All lints that are being checked in this block are guarded by
 +                        // the `avoid_breaking_exported_api` configuration. When adding a
 +                        // new lint, please also add the name to the configuration documentation
 +                        // in `clippy_lints::utils::conf.rs`
 +
 +                        let mut triggered = false;
 +                        triggered |= box_collection::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= redundant_allocation::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= rc_buffer::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= vec_box::check(cx, hir_ty, qpath, def_id, self.vec_box_size_threshold);
 +                        triggered |= option_option::check(cx, hir_ty, qpath, def_id);
 +                        triggered |= linked_list::check(cx, hir_ty, def_id);
 +                        triggered |= rc_mutex::check(cx, hir_ty, qpath, def_id);
 +
 +                        if triggered {
 +                            return;
 +                        }
 +                    }
 +                }
 +                match *qpath {
 +                    QPath::Resolved(Some(ty), p) => {
 +                        context.is_nested_call = true;
 +                        self.check_ty(cx, ty, context);
 +                        for ty in p.segments.iter().flat_map(|seg| {
 +                            seg.args
 +                                .as_ref()
 +                                .map_or_else(|| [].iter(), |params| params.args.iter())
 +                                .filter_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                })
 +                        }) {
 +                            self.check_ty(cx, ty, context);
 +                        }
 +                    },
 +                    QPath::Resolved(None, p) => {
 +                        context.is_nested_call = true;
 +                        for ty in p.segments.iter().flat_map(|seg| {
 +                            seg.args
 +                                .as_ref()
 +                                .map_or_else(|| [].iter(), |params| params.args.iter())
 +                                .filter_map(|arg| match arg {
 +                                    GenericArg::Type(ty) => Some(ty),
 +                                    _ => None,
 +                                })
 +                        }) {
 +                            self.check_ty(cx, ty, context);
 +                        }
 +                    },
 +                    QPath::TypeRelative(ty, seg) => {
 +                        context.is_nested_call = true;
 +                        self.check_ty(cx, ty, context);
 +                        if let Some(params) = seg.args {
 +                            for ty in params.args.iter().filter_map(|arg| match arg {
 +                                GenericArg::Type(ty) => Some(ty),
 +                                _ => None,
 +                            }) {
 +                                self.check_ty(cx, ty, context);
 +                            }
 +                        }
 +                    },
 +                    QPath::LangItem(..) => {},
 +                }
 +            },
 +            TyKind::Rptr(lt, ref mut_ty) => {
 +                context.is_nested_call = true;
 +                if !borrowed_box::check(cx, hir_ty, lt, mut_ty) {
 +                    self.check_ty(cx, mut_ty.ty, context);
 +                }
 +            },
 +            TyKind::Slice(ty) | TyKind::Array(ty, _) | TyKind::Ptr(MutTy { ty, .. }) => {
 +                context.is_nested_call = true;
 +                self.check_ty(cx, ty, context);
 +            },
 +            TyKind::Tup(tys) => {
 +                context.is_nested_call = true;
 +                for ty in tys {
 +                    self.check_ty(cx, ty, context);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    /// This function checks if the type is allowed to change in the current context
 +    /// based on the `avoid_breaking_exported_api` configuration
 +    fn is_type_change_allowed(&self, context: CheckTyContext) -> bool {
 +        !(context.is_exported && self.avoid_breaking_exported_api)
 +    }
 +}
 +
 +#[allow(clippy::struct_excessive_bools)]
 +#[derive(Clone, Copy, Default)]
 +struct CheckTyContext {
 +    is_in_trait_impl: bool,
 +    /// `true` for types on local variables.
 +    is_local: bool,
 +    /// `true` for types that are part of the public API.
 +    is_exported: bool,
 +    is_nested_call: bool,
 +}
index 2b964b64a3305d702ea51f8684316d61fc758738,0000000000000000000000000000000000000000..fae5385ffc848dc3d09805baac4d749f3fc5dfc4
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,104 @@@
- pub(super) fn check(
-     cx: &LateContext<'_>,
-     hir_ty: &hir::Ty<'_>,
-     qpath: &QPath<'_>,
-     def_id: DefId,
- ) -> bool {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::{path_def_id, qpath_generic_tys};
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::LateContext;
++use rustc_middle::ty::TypeVisitable;
 +use rustc_span::symbol::sym;
 +
 +use super::{utils, REDUNDANT_ALLOCATION};
 +
-                 diag.span_suggestion(
-                     hir_ty.span,
-                     "try",
-                     format!("{generic_snippet}"),
-                     applicability,
-                 );
++pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
 +    let mut applicability = Applicability::MaybeIncorrect;
 +    let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
 +        "Box"
 +    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
 +        "Rc"
 +    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
 +        "Arc"
 +    } else {
 +        return false;
 +    };
 +
 +    if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
 +        let generic_snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{outer_sym}<{generic_snippet}>`"),
 +            |diag| {
-         Some(ty) => {
++                diag.span_suggestion(hir_ty.span, "try", format!("{generic_snippet}"), applicability);
 +                diag.note(&format!(
 +                    "`{generic_snippet}` is already a pointer, `{outer_sym}<{generic_snippet}>` allocates a pointer on the heap"
 +                ));
 +            },
 +        );
 +        return true;
 +    }
 +
 +    let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
 +    let Some(id) = path_def_id(cx, ty) else { return false };
 +    let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
 +        Some(sym::Arc) => ("Arc", ty),
 +        Some(sym::Rc) => ("Rc", ty),
 +        _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
 +        _ => return false,
 +    };
 +
 +    let TyKind::Path(inner_qpath) = &ty.kind else {
 +        return false
 +    };
 +    let inner_span = match qpath_generic_tys(inner_qpath).next() {
-             if !hir_ty_to_ty(cx.tcx, ty).is_sized(cx.tcx, cx.param_env) {
++        Some(hir_ty) => {
 +            // Reallocation of a fat pointer causes it to become thin. `hir_ty_to_ty` is safe to use
 +            // here because `mod.rs` guarantees this lint is only run on types outside of bodies and
 +            // is not run on locals.
-             ty.span
-         }
++            let ty = hir_ty_to_ty(cx.tcx, hir_ty);
++            if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) {
 +                return false;
 +            }
++            hir_ty.span
++        },
 +        None => return false,
 +    };
 +    if inner_sym == outer_sym {
 +        let generic_snippet = snippet_with_applicability(cx, inner_span, "..", &mut applicability);
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
 +            |diag| {
 +                diag.span_suggestion(
 +                    hir_ty.span,
 +                    "try",
 +                    format!("{outer_sym}<{generic_snippet}>"),
 +                    applicability,
 +                );
 +                diag.note(&format!(
 +                    "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
 +                ));
 +            },
 +        );
 +    } else {
 +        let generic_snippet = snippet(cx, inner_span, "..");
 +        span_lint_and_then(
 +            cx,
 +            REDUNDANT_ALLOCATION,
 +            hir_ty.span,
 +            &format!("usage of `{outer_sym}<{inner_sym}<{generic_snippet}>>`"),
 +            |diag| {
 +                diag.note(&format!(
 +                    "`{inner_sym}<{generic_snippet}>` is already on the heap, `{outer_sym}<{inner_sym}<{generic_snippet}>>` makes an extra allocation"
 +                ));
 +                diag.help(&format!(
 +                    "consider using just `{outer_sym}<{generic_snippet}>` or `{inner_sym}<{generic_snippet}>`"
 +                ));
 +            },
 +        );
 +    }
 +    true
 +}
index 9ad2cb853d39a474255c0256dd2e30e5ce778ecf,0000000000000000000000000000000000000000..7a3c7cd8a99fc714c1a0d3e7039a2e985f23c8bf
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,64 @@@
-             if ty_ty_size <= box_size_threshold;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::last_path_segment;
 +use clippy_utils::source::snippet;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind};
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::TypeVisitable;
 +use rustc_span::symbol::sym;
 +
 +use super::VEC_BOX;
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    hir_ty: &hir::Ty<'_>,
 +    qpath: &QPath<'_>,
 +    def_id: DefId,
 +    box_size_threshold: u64,
 +) -> bool {
 +    if cx.tcx.is_diagnostic_item(sym::Vec, def_id) {
 +        if_chain! {
 +            // Get the _ part of Vec<_>
 +            if let Some(last) = last_path_segment(qpath).args;
 +            if let Some(ty) = last.args.iter().find_map(|arg| match arg {
 +                GenericArg::Type(ty) => Some(ty),
 +                _ => None,
 +            });
 +            // ty is now _ at this point
 +            if let TyKind::Path(ref ty_qpath) = ty.kind;
 +            let res = cx.qpath_res(ty_qpath, ty.hir_id);
 +            if let Some(def_id) = res.opt_def_id();
 +            if Some(def_id) == cx.tcx.lang_items().owned_box();
 +            // At this point, we know ty is Box<T>, now get T
 +            if let Some(last) = last_path_segment(ty_qpath).args;
 +            if let Some(boxed_ty) = last.args.iter().find_map(|arg| match arg {
 +                GenericArg::Type(ty) => Some(ty),
 +                _ => None,
 +            });
 +            let ty_ty = hir_ty_to_ty(cx.tcx, boxed_ty);
 +            if !ty_ty.has_escaping_bound_vars();
 +            if ty_ty.is_sized(cx.tcx, cx.param_env);
 +            if let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes());
++            if ty_ty_size < box_size_threshold;
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    VEC_BOX,
 +                    hir_ty.span,
 +                    "`Vec<T>` is already on the heap, the boxing is unnecessary",
 +                    "try",
 +                    format!("Vec<{}>", snippet(cx, boxed_ty.span, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +                true
 +            } else {
 +                false
 +            }
 +        }
 +    } else {
 +        false
 +    }
 +}
index d2e675a783eaaf382517370b34526011b2cf2923,0000000000000000000000000000000000000000..e8f15a4447352a9cedd66892e34831cb089a800f
mode 100644,000000..100644
--- /dev/null
@@@ -1,358 -1,0 +1,392 @@@
-             && !block_has_safety_comment(cx, block)
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::source::walk_span_to_context;
 +use clippy_utils::{get_parent_node, is_lint_allowed};
 +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_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
 +
 +impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
 +    fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
 +        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)
- fn block_has_safety_comment(cx: &LateContext<'_>, block: &hir::Block<'_>) -> bool {
++            && !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",
 +            );
 +        }
 +    }
 +
 +    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)
 +        {
 +            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",
 +            );
 +        }
 +    }
 +}
 +
 +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.
-     span_from_macro_expansion_has_safety_comment(cx, block.span) || span_in_body_has_safety_comment(cx, block.span)
++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.
 +
++    span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span)
 +}
 +
 +/// Checks if the lines immediately preceding the item contain a safety comment.
 +#[allow(clippy::collapsible_match)]
 +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
 +    if span_from_macro_expansion_has_safety_comment(cx, item.span) {
 +        return true;
 +    }
 +
 +    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;
 +                    }
 +                },
 +                _ => {
 +                    // Doesn't support impls in this position. Pretend a comment was found.
 +                    return 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()
 +            {
 +                unsafe_line.sf.lines(|lines| {
 +                    comment_start_line.line < unsafe_line.line && text_has_safety_comment(
 +                        src,
 +                        &lines[comment_start_line.line + 1..=unsafe_line.line],
 +                        unsafe_line.sf.start_pos.to_usize(),
 +                    )
 +                })
 +            } else {
 +                // Problem getting source text. Pretend a comment was found.
 +                true
 +            }
 +        } else {
 +            // No parent node. Pretend a comment was found.
 +            true
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +fn comment_start_before_impl_in_mod(
 +    cx: &LateContext<'_>,
 +    parent_mod: &hir::Mod<'_>,
 +    parent_mod_span: Span,
 +    imple: &hir::Item<'_>,
 +) -> Option<BytePos> {
 +    parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
 +        if *item_id == imple.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
 +    })
 +}
 +
 +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
 +    let source_map = cx.sess().source_map();
 +    let ctxt = span.ctxt();
 +    if ctxt == SyntaxContext::root() {
 +        false
 +    } 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| {
 +                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(),
 +                )
 +            })
 +        } else {
 +            // Problem getting source text. Pretend a comment was found.
 +            true
 +        }
 +    }
 +}
 +
 +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(),
 +                )
 +            })
 +        } 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.
 +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
 +    let mut lines = line_starts
 +        .array_windows::<2>()
 +        .rev()
 +        .map_while(|[start, end]| {
 +            let start = start.to_usize() - offset;
 +            let end = end.to_usize() - offset;
 +            src.get(start..end).map(|text| (start, text.trim_start()))
 +        })
 +        .filter(|(_, text)| !text.is_empty());
 +
 +    let Some((line_start, line)) = lines.next() else {
 +        return false;
 +    };
 +    // Check for a sequence of line comments.
 +    if line.starts_with("//") {
 +        let mut line = line;
 +        loop {
 +            if line.to_ascii_uppercase().contains("SAFETY:") {
 +                return true;
 +            }
 +            match lines.next() {
 +                Some((_, x)) if x.starts_with("//") => line = x,
 +                _ => return false,
 +            }
 +        }
 +    }
 +    // 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("/*") {
 +            let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start();
 +            let mut tokens = tokenize(src);
 +            return src[..tokens.next().unwrap().len as usize]
 +                .to_ascii_uppercase()
 +                .contains("SAFETY:")
 +                && tokens.all(|t| t.kind == TokenKind::Whitespace);
 +        }
 +        match lines.next() {
 +            Some(x) => (line_start, line) = x,
 +            None => return false,
 +        }
 +    }
 +}
index 32cd468120141ec5a4c79a5b1dc1c0f946ab906c,0000000000000000000000000000000000000000..952586527689af042908087005a2c5974580b1ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-             for &(ref use_tree, _) in nested_use_tree {
 +use clippy_utils::diagnostics::span_lint;
 +use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind};
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::Ident;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for imports that remove "unsafe" from an item's
 +    /// name.
 +    ///
 +    /// ### Why is this bad?
 +    /// Renaming makes it less clear which traits and
 +    /// structures are unsafe.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use std::cell::{UnsafeCell as TotallySafeCell};
 +    ///
 +    /// extern crate crossbeam;
 +    /// use crossbeam::{spawn_unsafe as spawn};
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNSAFE_REMOVED_FROM_NAME,
 +    style,
 +    "`unsafe` removed from API names on import"
 +}
 +
 +declare_lint_pass!(UnsafeNameRemoval => [UNSAFE_REMOVED_FROM_NAME]);
 +
 +impl EarlyLintPass for UnsafeNameRemoval {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if let ItemKind::Use(ref use_tree) = item.kind {
 +            check_use_tree(use_tree, cx, item.span);
 +        }
 +    }
 +}
 +
 +fn check_use_tree(use_tree: &UseTree, cx: &EarlyContext<'_>, span: Span) {
 +    match use_tree.kind {
 +        UseTreeKind::Simple(Some(new_name), ..) => {
 +            let old_name = use_tree
 +                .prefix
 +                .segments
 +                .last()
 +                .expect("use paths cannot be empty")
 +                .ident;
 +            unsafe_to_safe_check(old_name, new_name, cx, span);
 +        },
 +        UseTreeKind::Simple(None, ..) | UseTreeKind::Glob => {},
 +        UseTreeKind::Nested(ref nested_use_tree) => {
++            for (use_tree, _) in nested_use_tree {
 +                check_use_tree(use_tree, cx, span);
 +            }
 +        },
 +    }
 +}
 +
 +fn unsafe_to_safe_check(old_name: Ident, new_name: Ident, cx: &EarlyContext<'_>, span: Span) {
 +    let old_str = old_name.name.as_str();
 +    let new_str = new_name.name.as_str();
 +    if contains_unsafe(old_str) && !contains_unsafe(new_str) {
 +        span_lint(
 +            cx,
 +            UNSAFE_REMOVED_FROM_NAME,
 +            span,
 +            &format!("removed `unsafe` from the name of `{old_str}` in use as `{new_str}`"),
 +        );
 +    }
 +}
 +
 +#[must_use]
 +fn contains_unsafe(name: &str) -> bool {
 +    name.contains("Unsafe") || name.contains("unsafe")
 +}
index b452be0840948e86c718816d360715e1615922c9,0000000000000000000000000000000000000000..4ee16d9a5e4ad94d4a8631ad95ff845ed252fde4
mode 100644,000000..100644
--- /dev/null
@@@ -1,231 -1,0 +1,231 @@@
-     #[clippy::version = "1.64.0"]
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
 +use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
 +use rustc_ast::Mutability;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter::OnlyBodies;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the creation of a `peekable` iterator that is never `.peek()`ed
 +    ///
 +    /// ### Why is this bad?
 +    /// Creating a peekable iterator without using any of its methods is likely a mistake,
 +    /// or just a leftover after a refactor.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let collection = vec![1, 2, 3];
 +    /// let iter = collection.iter().peekable();
 +    ///
 +    /// for item in iter {
 +    ///     // ...
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let collection = vec![1, 2, 3];
 +    /// let iter = collection.iter();
 +    ///
 +    /// for item in iter {
 +    ///     // ...
 +    /// }
 +    /// ```
++    #[clippy::version = "1.65.0"]
 +    pub UNUSED_PEEKABLE,
 +    nursery,
 +    "creating a peekable iterator without using any of its methods"
 +}
 +
 +declare_lint_pass!(UnusedPeekable => [UNUSED_PEEKABLE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +        // Don't lint `Peekable`s returned from a block
 +        if let Some(expr) = block.expr
 +            && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr))
 +            && match_type(cx, ty, &paths::PEEKABLE)
 +        {
 +            return;
 +        }
 +
 +        for (idx, stmt) in block.stmts.iter().enumerate() {
 +            if !stmt.span.from_expansion()
 +                && let StmtKind::Local(local) = stmt.kind
 +                && let PatKind::Binding(_, binding, ident, _) = local.pat.kind
 +                && let Some(init) = local.init
 +                && !init.span.from_expansion()
 +                && let Some(ty) = cx.typeck_results().expr_ty_opt(init)
 +                && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
 +                && match_type(cx, ty, &paths::PEEKABLE)
 +            {
 +                let mut vis = PeekableVisitor::new(cx, binding);
 +
 +                if idx + 1 == block.stmts.len() && block.expr.is_none() {
 +                    return;
 +                }
 +
 +                for stmt in &block.stmts[idx..] {
 +                    vis.visit_stmt(stmt);
 +                }
 +
 +                if let Some(expr) = block.expr {
 +                    vis.visit_expr(expr);
 +                }
 +
 +                if !vis.found_peek_call {
 +                    span_lint_and_help(
 +                        cx,
 +                        UNUSED_PEEKABLE,
 +                        ident.span,
 +                        "`peek` never called on `Peekable` iterator",
 +                        None,
 +                        "consider removing the call to `peekable`"
 +                   );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct PeekableVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    expected_hir_id: HirId,
 +    found_peek_call: bool,
 +}
 +
 +impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
 +        Self {
 +            cx,
 +            expected_hir_id,
 +            found_peek_call: false,
 +        }
 +    }
 +}
 +
 +impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
 +    type NestedFilter = OnlyBodies;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
 +        if self.found_peek_call {
 +            return;
 +        }
 +
 +        if path_to_local_id(ex, self.expected_hir_id) {
 +            for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
 +                match node {
 +                    Node::Expr(expr) => {
 +                        match expr.kind {
 +                            // some_function(peekable)
 +                            //
 +                            // If the Peekable is passed to a function, stop
 +                            ExprKind::Call(_, args) => {
 +                                if let Some(func_did) = fn_def_id(self.cx, expr)
 +                                    && let Some(into_iter_did) = self
 +                                        .cx
 +                                        .tcx
 +                                        .lang_items()
 +                                        .into_iter_fn()
 +                                    && func_did == into_iter_did
 +                                {
 +                                    // Probably a for loop desugar, stop searching
 +                                    return;
 +                                }
 +
 +                                if args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg)) {
 +                                    self.found_peek_call = true;
 +                                }
 +
 +                                return;
 +                            },
 +                            // Catch anything taking a Peekable mutably
 +                            ExprKind::MethodCall(
 +                                PathSegment {
 +                                    ident: method_name_ident,
 +                                    ..
 +                                },
 +                                self_arg,
 +                                remaining_args,
 +                                _,
 +                            ) => {
 +                                let method_name = method_name_ident.name.as_str();
 +
 +                                // `Peekable` methods
 +                                if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq")
 +                                    && arg_is_mut_peekable(self.cx, self_arg)
 +                                {
 +                                    self.found_peek_call = true;
 +                                    return;
 +                                }
 +
 +                                // foo.some_method() excluding Iterator methods
 +                                if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg))
 +                                    && !is_trait_method(self.cx, expr, sym::Iterator)
 +                                {
 +                                    self.found_peek_call = true;
 +                                    return;
 +                                }
 +
 +                                // foo.by_ref(), keep checking for `peek`
 +                                if method_name == "by_ref" {
 +                                    continue;
 +                                }
 +
 +                                return;
 +                            },
 +                            ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => {
 +                            },
 +                            ExprKind::AddrOf(_, Mutability::Not, _) => return,
 +                            _ => {
 +                                self.found_peek_call = true;
 +                                return;
 +                            },
 +                        }
 +                    },
 +                    Node::Local(Local { init: Some(init), .. }) => {
 +                        if arg_is_mut_peekable(self.cx, init) {
 +                            self.found_peek_call = true;
 +                        }
 +
 +                        return;
 +                    },
 +                    Node::Stmt(stmt) => {
 +                        match stmt.kind {
 +                            StmtKind::Local(_) | StmtKind::Item(_) => self.found_peek_call = true,
 +                            StmtKind::Expr(_) | StmtKind::Semi(_) => {},
 +                        }
 +
 +                        return;
 +                    },
 +                    Node::Block(_) | Node::ExprField(_) => {},
 +                    _ => {
 +                        return;
 +                    },
 +                }
 +            }
 +        }
 +
 +        walk_expr(self, ex);
 +    }
 +}
 +
 +fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
 +    if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
 +        && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
 +        && match_type(cx, ty, &paths::PEEKABLE)
 +    {
 +        true
 +    } else {
 +        false
 +    }
 +}
index 5ab351bc29ca0b8b1ba653287f1933cc70d99217,0000000000000000000000000000000000000000..aac6719a8dc0feef10b0ea7c4f1578f8732195ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,67 @@@
-     if let ExprKind::MethodCall(box MethodCall { seg, receiver, .. }) = &expr.kind
-         && let method_name = seg.ident.name.as_str()
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +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]);
 +
 +fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
-             let f = token_lit.symbol.as_str().parse::<f64>().unwrap();
++    if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind
++        && let method_name = name_ident.ident.name.as_str()
 +        && (method_name == "ceil" || method_name == "round" || method_name == "floor")
 +        && let ExprKind::Lit(token_lit) = &receiver.kind
 +        && token_lit.is_semantic_float() {
-             match token_lit.suffix {
-                 Some(suffix) => f_str.push_str(suffix.as_str()),
-                 None => {}
 +            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
 +            }
 +        } else {
 +            None
 +        }
 +}
 +
 +impl EarlyLintPass for UnusedRounding {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if let Some((method_name, float)) = is_useless_rounding(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 cd1d90e860b9fe865baef8a27bcbeb3078b7b0ec,0000000000000000000000000000000000000000..cad8da18c2fbc5c80f14a259600f019ee8226963
mode 100644,000000..100644
--- /dev/null
@@@ -1,148 -1,0 +1,152 @@@
- use rustc_ast::ast;
- use rustc_ast::visit::FnKind;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::{position_before_rarrow, snippet_opt};
 +use if_chain::if_chain;
++use rustc_ast::{ast, visit::FnKind, ClosureBinder};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::BytePos;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unit (`()`) expressions that can be removed.
 +    ///
 +    /// ### Why is this bad?
 +    /// Such expressions add no value, but can make the code
 +    /// less readable. Depending on formatting they can make a `break` or `return`
 +    /// statement look like a function call.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn return_unit() -> () {
 +    ///     ()
 +    /// }
 +    /// ```
 +    /// is equivalent to
 +    /// ```rust
 +    /// fn return_unit() {}
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub UNUSED_UNIT,
 +    style,
 +    "needless unit expression"
 +}
 +
 +declare_lint_pass!(UnusedUnit => [UNUSED_UNIT]);
 +
 +impl EarlyLintPass for UnusedUnit {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, span: Span, _: ast::NodeId) {
 +        if_chain! {
 +            if let ast::FnRetTy::Ty(ref ty) = kind.decl().output;
 +            if let ast::TyKind::Tup(ref vals) = ty.kind;
 +            if vals.is_empty() && !ty.span.from_expansion() && get_def(span) == get_def(ty.span);
 +            then {
++                // implicit types in closure signatures are forbidden when `for<...>` is present
++                if let FnKind::Closure(&ClosureBinder::For { .. }, ..) = kind {
++                    return;
++                }
++
 +                lint_unneeded_unit_return(cx, ty, span);
 +            }
 +        }
 +    }
 +
 +    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
 +        if_chain! {
 +            if let Some(stmt) = block.stmts.last();
 +            if let ast::StmtKind::Expr(ref expr) = stmt.kind;
 +            if is_unit_expr(expr);
 +            let ctxt = block.span.ctxt();
 +            if stmt.span.ctxt() == ctxt && expr.span.ctxt() == ctxt;
 +            then {
 +                let sp = expr.span;
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNUSED_UNIT,
 +                    sp,
 +                    "unneeded unit expression",
 +                    "remove the final `()`",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
 +        match e.kind {
 +            ast::ExprKind::Ret(Some(ref expr)) | ast::ExprKind::Break(_, Some(ref expr)) => {
 +                if is_unit_expr(expr) && !expr.span.from_expansion() {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNUSED_UNIT,
 +                        expr.span,
 +                        "unneeded `()`",
 +                        "remove the `()`",
 +                        String::new(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_poly_trait_ref(&mut self, cx: &EarlyContext<'_>, poly: &ast::PolyTraitRef) {
 +        let segments = &poly.trait_ref.path.segments;
 +
 +        if_chain! {
 +            if segments.len() == 1;
 +            if ["Fn", "FnMut", "FnOnce"].contains(&segments[0].ident.name.as_str());
 +            if let Some(args) = &segments[0].args;
 +            if let ast::GenericArgs::Parenthesized(generic_args) = &**args;
 +            if let ast::FnRetTy::Ty(ty) = &generic_args.output;
 +            if ty.kind.is_unit();
 +            then {
 +                lint_unneeded_unit_return(cx, ty, generic_args.span);
 +            }
 +        }
 +    }
 +}
 +
 +// get the def site
 +#[must_use]
 +fn get_def(span: Span) -> Option<Span> {
 +    if span.from_expansion() {
 +        Some(span.ctxt().outer_expn_data().def_site)
 +    } else {
 +        None
 +    }
 +}
 +
 +// is this expr a `()` unit?
 +fn is_unit_expr(expr: &ast::Expr) -> bool {
 +    if let ast::ExprKind::Tup(ref vals) = expr.kind {
 +        vals.is_empty()
 +    } else {
 +        false
 +    }
 +}
 +
 +fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
 +    let (ret_span, appl) =
 +        snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| {
 +            position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
 +                (
 +                    #[expect(clippy::cast_possible_truncation)]
 +                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
 +                    Applicability::MachineApplicable,
 +                )
 +            })
 +        });
 +    span_lint_and_sugg(
 +        cx,
 +        UNUSED_UNIT,
 +        ret_span,
 +        "unneeded unit return type",
 +        "remove the `-> ()`",
 +        String::new(),
 +        appl,
 +    );
 +}
index c6cdf3f85fc3dd891445c71db7a53847ea485ff8,0000000000000000000000000000000000000000..e2860db71a5a772a945f11bced95c435051bf8d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,314 @@@
-     /// - False positive with associated types in traits (#4140)
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::ty::same_type_and_consts;
 +use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +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};
 +use rustc_semver::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
-             ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
-                 Res::SelfTyParam { .. } | Res::SelfTyAlias { .. } => (),
-                 Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
-                 _ => span_lint(cx, path.span),
-             },
-             // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
 +    ///
 +    /// ### 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 {
 +    msrv: Option<RustcVersion>,
 +    stack: Vec<StackItem>,
 +}
 +
 +impl UseSelf {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> 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 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 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 {
-                     if let Res::Def(DefKind::Ctor(ctor_of, _), ..) = path.res {
-                         match ctor_of {
-                             CtorOf::Variant => lint_path_to_variant(cx, path),
-                             CtorOf::Struct => span_lint(cx, path.span),
-                         }
-                     }
++            ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path),
 +            ExprKind::Call(fun, _) => {
 +                if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
-             // unit enum variants (`Enum::A`)
-             ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(cx, path),
++                    check_path(cx, path);
 +                }
 +            },
-                 match path.res {
-                     Res::Def(DefKind::Ctor(ctor_of, _), ..) => match ctor_of {
-                             CtorOf::Variant => lint_path_to_variant(cx, path),
-                             CtorOf::Struct => span_lint(cx, path.span),
-                     },
-                     Res::Def(DefKind::Variant, ..) => lint_path_to_variant(cx, path),
-                     Res::Def(DefKind::Struct, ..) => span_lint(cx, path.span),
-                     _ => ()
-                 }
++            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 meets_msrv(self.msrv, 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 668123e4d6e394e5e73f7f0fb9b2ec6a990de8f2,0000000000000000000000000000000000000000..b37d4239477eaa1c2ed866cd3112173f71f6f2b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,558 -1,0 +1,577 @@@
-     pub fn reason(&self) -> Option<&str> {
 +//! 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
 +    }
 +
-             } => Some(reason),
++    pub fn reason(&self) -> Option<String> {
 +        match self {
 +            Self::WithReason {
 +                reason: Some(reason), ..
-     /// 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.
++            } => 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),
-     /// Whether `expect` should be allowed in test functions
++    /// 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 `unwrap` should be allowed in test functions
++    /// Whether `expect` should be allowed within `#[cfg(test)]`
 +    (allow_expect_in_tests: bool = false),
 +    /// Lint: UNWRAP_USED.
 +    ///
-     /// Lint: RESULT_LARGE_ERR
++    /// 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()])),
 +}
 +
 +/// 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 096b601572b4db5dcce28ff090f39b8e1d4eadf6,0000000000000000000000000000000000000000..4b33d492a0e478f7ab5c0003f45ede45a917887d
mode 100644,000000..100644
--- /dev/null
@@@ -1,239 -1,0 +1,239 @@@
- use clippy_utils::{def_path_res, is_expn_of, match_def_path, paths};
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
-             if let Some(def_id) = def_path_res(cx, module, None).opt_def_id() {
++use clippy_utils::{def_path_def_ids, is_expn_of, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty::{self};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Symbol;
 +
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for interning symbols that have already been pre-interned and defined as constants.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster and easier to use the symbol constant.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _ = sym::f32;
 +    /// ```
 +    pub INTERNING_DEFINED_SYMBOL,
 +    internal,
 +    "interning a symbol that is pre-interned and defined as a constant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary conversion from Symbol to a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster use symbols directly instead of strings.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +#[derive(Default)]
 +pub struct InterningDefinedSymbol {
 +    // Maps the symbol value to the constant DefId.
 +    symbol_map: FxHashMap<u32, DefId>,
 +}
 +
 +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        if !self.symbol_map.is_empty() {
 +            return;
 +        }
 +
 +        for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
++            for def_id in def_path_def_ids(cx, module) {
 +                for item in cx.tcx.module_children(def_id).iter() {
 +                    if_chain! {
 +                        if let Res::Def(DefKind::Const, item_def_id) = item.res;
 +                        let ty = cx.tcx.type_of(item_def_id);
 +                        if match_type(cx, ty, &paths::SYMBOL);
 +                        if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
 +                        if let Ok(value) = value.to_u32();
 +                        then {
 +                            self.symbol_map.insert(value, item_def_id);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = &expr.kind;
 +            if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
 +            if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
 +            if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
 +            let value = Symbol::intern(&arg).as_u32();
 +            if let Some(&def_id) = self.symbol_map.get(&value);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    INTERNING_DEFINED_SYMBOL,
 +                    is_expn_of(expr.span, "sym").unwrap_or(expr.span),
 +                    "interning a defined symbol",
 +                    "try",
 +                    cx.tcx.def_path_str(def_id),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +        if let ExprKind::Binary(op, left, right) = expr.kind {
 +            if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
 +                let data = [
 +                    (left, self.symbol_str_expr(left, cx)),
 +                    (right, self.symbol_str_expr(right, cx)),
 +                ];
 +                match data {
 +                    // both operands are a symbol string
 +                    [(_, Some(left)), (_, Some(right))] => {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_SYMBOL_STR,
 +                            expr.span,
 +                            "unnecessary `Symbol` to string conversion",
 +                            "try",
 +                            format!(
 +                                "{} {} {}",
 +                                left.as_symbol_snippet(cx),
 +                                op.node.as_str(),
 +                                right.as_symbol_snippet(cx),
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                    // one of the operands is a symbol string
 +                    [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
 +                        // creating an owned string for comparison
 +                        if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                UNNECESSARY_SYMBOL_STR,
 +                                expr.span,
 +                                "unnecessary string allocation",
 +                                "try",
 +                                format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
 +                                Applicability::MachineApplicable,
 +                            );
 +                        }
 +                    },
 +                    // nothing found
 +                    [(_, None), (_, None)] => {},
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl InterningDefinedSymbol {
 +    fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
 +        static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
 +        static SYMBOL_STR_PATHS: &[&[&str]] = &[
 +            &paths::SYMBOL_AS_STR,
 +            &paths::SYMBOL_TO_IDENT_STRING,
 +            &paths::TO_STRING_METHOD,
 +        ];
 +        let call = if_chain! {
 +            if let ExprKind::AddrOf(_, _, e) = expr.kind;
 +            if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
 +            then { e } else { expr }
 +        };
 +        if_chain! {
 +            // is a method call
 +            if let ExprKind::MethodCall(_, item, [], _) = call.kind;
 +            if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
 +            let ty = cx.typeck_results().expr_ty(item);
 +            // ...on either an Ident or a Symbol
 +            if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
 +                Some(false)
 +            } else if match_type(cx, ty, &paths::IDENT) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            // ...which converts it to a string
 +            let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
 +            if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
 +            then {
 +                let is_to_owned = path.last().unwrap().ends_with("string");
 +                return Some(SymbolStrExpr::Expr {
 +                    item,
 +                    is_ident,
 +                    is_to_owned,
 +                });
 +            }
 +        }
 +        // is a string constant
 +        if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
 +            let value = Symbol::intern(&s).as_u32();
 +            // ...which matches a symbol constant
 +            if let Some(&def_id) = self.symbol_map.get(&value) {
 +                return Some(SymbolStrExpr::Const(def_id));
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +enum SymbolStrExpr<'tcx> {
 +    /// a string constant with a corresponding symbol constant
 +    Const(DefId),
 +    /// a "symbol to string" expression like `symbol.as_str()`
 +    Expr {
 +        /// part that evaluates to `Symbol` or `Ident`
 +        item: &'tcx Expr<'tcx>,
 +        is_ident: bool,
 +        /// whether an owned `String` is created like `to_ident_string()`
 +        is_to_owned: bool,
 +    },
 +}
 +
 +impl<'tcx> SymbolStrExpr<'tcx> {
 +    /// Returns a snippet that evaluates to a `Symbol` and is const if possible
 +    fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
 +        match *self {
 +            Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
 +            Self::Expr { item, is_ident, .. } => {
 +                let mut snip = snippet(cx, item.span.source_callsite(), "..");
 +                if is_ident {
 +                    // get `Ident.name`
 +                    snip.to_mut().push_str(".name");
 +                }
 +                snip
 +            },
 +        }
 +    }
 +}
index 22a5aa5351ad5b1c044319975a19c81869ddba15,0000000000000000000000000000000000000000..680935f2329e4e5037ffdf2c1f01022c962f3702
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,108 @@@
- use rustc_hir::def::{DefKind, Res};
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::def_path_res;
 +use clippy_utils::diagnostics::span_lint;
 +use if_chain::if_chain;
 +use rustc_hir as hir;
-     if def_path_res(cx, path, None) != Res::Err {
++use rustc_hir::def::DefKind;
 +use rustc_hir::Item;
 +use rustc_hir_analysis::hir_ty_to_ty;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, FloatTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::Symbol;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the paths module for invalid paths.
 +    ///
 +    /// ### Why is this bad?
 +    /// It indicates a bug in the code.
 +    ///
 +    /// ### Example
 +    /// None.
 +    pub INVALID_PATHS,
 +    internal,
 +    "invalid path"
 +}
 +
 +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let local_def_id = &cx.tcx.parent_module(item.hir_id());
 +        let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
 +        if_chain! {
 +            if mod_name.as_str() == "paths";
 +            if let hir::ItemKind::Const(ty, body_id) = item.kind;
 +            let ty = hir_ty_to_ty(cx.tcx, ty);
 +            if let ty::Array(el_ty, _) = &ty.kind();
 +            if let ty::Ref(_, el_ty, _) = &el_ty.kind();
 +            if el_ty.is_str();
 +            let body = cx.tcx.hir().body(body_id);
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
 +            let path: Vec<&str> = path
 +                .iter()
 +                .map(|x| {
 +                    if let Constant::Str(s) = x {
 +                        s.as_str()
 +                    } else {
 +                        // We checked the type of the constant above
 +                        unreachable!()
 +                    }
 +                })
 +                .collect();
 +            if !check_path(cx, &path[..]);
 +            then {
 +                span_lint(cx, INVALID_PATHS, item.span, "invalid path");
 +            }
 +        }
 +    }
 +}
 +
 +// This is not a complete resolver for paths. It works on all the paths currently used in the paths
 +// module.  That's all it does and all it needs to do.
 +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
++    if !def_path_res(cx, path).is_empty() {
 +        return true;
 +    }
 +
 +    // Some implementations can't be found by `path_to_res`, particularly inherent
 +    // implementations of native types. Check lang items.
 +    let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
 +    let lang_items = cx.tcx.lang_items();
 +    // This list isn't complete, but good enough for our current list of paths.
 +    let incoherent_impls = [
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
 +        SimplifiedTypeGen::SliceSimplifiedType,
 +        SimplifiedTypeGen::StrSimplifiedType,
 +    ]
 +    .iter()
 +    .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter().copied());
 +    for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) {
 +        let lang_item_path = cx.get_def_path(item_def_id);
 +        if path_syms.starts_with(&lang_item_path) {
 +            if let [item] = &path_syms[lang_item_path.len()..] {
 +                if matches!(
 +                    cx.tcx.def_kind(item_def_id),
 +                    DefKind::Mod | DefKind::Enum | DefKind::Trait
 +                ) {
 +                    for child in cx.tcx.module_children(item_def_id) {
 +                        if child.ident.name == *item {
 +                            return true;
 +                        }
 +                    }
 +                } else {
 +                    for child in cx.tcx.associated_item_def_ids(item_def_id) {
 +                        if cx.tcx.item_name(*child) == *item {
 +                            return true;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    false
 +}
index 0dac64376b06539109482966d66918fb66f7efab,0000000000000000000000000000000000000000..1aebb8b3104ba611482d1f86c9b4e5f2a0b00fc8
mode 100644,000000..100644
--- /dev/null
@@@ -1,342 -1,0 +1,342 @@@
- pub(super) fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &hir::Ty<'_>) -> bool {
 +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::{is_lint_allowed, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_ast::ast::LitKind;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Ensures every lint is associated to a `LintPass`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The compiler only knows lints via a `LintPass`. Without
 +    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
 +    /// know the name of the lint.
 +    ///
 +    /// ### Known problems
 +    /// Only checks for lints associated using the
 +    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub LINT_1, ... }
 +    /// declare_lint! { pub LINT_2, ... }
 +    /// declare_lint! { pub FORGOTTEN_LINT, ... }
 +    /// // ...
 +    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
 +    /// // missing FORGOTTEN_LINT
 +    /// ```
 +    pub LINT_WITHOUT_LINT_PASS,
 +    internal,
 +    "declaring a lint without associating it in a LintPass"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated lint without an updated description,
 +    /// i.e. `default lint description`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the lint is not finished.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
 +    /// ```
 +    pub DEFAULT_LINT,
 +    internal,
 +    "found 'default lint description' in a lint declaration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for invalid `clippy::version` attributes.
 +    ///
 +    /// Valid values are:
 +    /// * "pre 1.29.0"
 +    /// * any valid semantic version
 +    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found an invalid `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for declared clippy lints without the `clippy::version` attribute.
 +    ///
 +    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found clippy lint without `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
 +    /// i.e. `"default deprecation note"`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the documentation is incomplete.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// TODO
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "default deprecation note"
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// This lint has been replaced by `cooler_lint`
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "this lint has been replaced by `cooler_lint`"
 +    /// }
 +    /// ```
 +    pub DEFAULT_DEPRECATION_REASON,
 +    internal,
 +    "found 'default deprecation note' in a deprecated lint declaration"
 +}
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct LintWithoutLintPass {
 +    declared_lints: FxHashMap<Symbol, Span>,
 +    registered_lints: FxHashSet<Symbol>,
 +}
 +
 +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
 +            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
 +        {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
 +            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
 +                check_invalid_clippy_version_attribute(cx, item);
 +
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                let fields;
 +                if is_lint_ref_ty {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
 +                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
 +                            fields = struct_fields;
 +                    } else {
 +                        return;
 +                    }
 +                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
 +                    fields = struct_fields;
 +                } else {
 +                    return;
 +                }
 +
 +                let field = fields
 +                    .iter()
 +                    .find(|f| f.ident.as_str() == "desc")
 +                    .expect("lints must have a description field");
 +
 +                if let ExprKind::Lit(Spanned {
 +                    node: LitKind::Str(ref sym, _),
 +                    ..
 +                }) = field.expr.kind
 +                {
 +                    let sym_str = sym.as_str();
 +                    if is_lint_ref_ty {
 +                        if sym_str == "default lint description" {
 +                            span_lint(
 +                                cx,
 +                                DEFAULT_LINT,
 +                                item.span,
 +                                &format!("the lint `{}` has the default lint description", item.ident.name),
 +                            );
 +                        }
 +
 +                        self.declared_lints.insert(item.ident.name, item.span);
 +                    } else if sym_str == "default deprecation note" {
 +                        span_lint(
 +                            cx,
 +                            DEFAULT_DEPRECATION_REASON,
 +                            item.span,
 +                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
 +                        );
 +                    }
 +                }
 +            }
 +        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
 +            if !matches!(
 +                cx.tcx.item_name(macro_call.def_id).as_str(),
 +                "impl_lint_pass" | "declare_lint_pass"
 +            ) {
 +                return;
 +            }
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: None,
 +                items: impl_item_refs,
 +                ..
 +            }) = item.kind
 +            {
 +                let mut collector = LintCollector {
 +                    output: &mut self.registered_lints,
 +                    cx,
 +                };
 +                let body_id = cx.tcx.hir().body_owned_by(
 +                    cx.tcx.hir().local_def_id(
 +                        impl_item_refs
 +                            .iter()
 +                            .find(|iiref| iiref.ident.as_str() == "get_lints")
 +                            .expect("LintPass needs to implement get_lints")
 +                            .id
 +                            .hir_id(),
 +                    ),
 +                );
 +                collector.visit_expr(cx.tcx.hir().body(body_id).value);
 +            }
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
 +            return;
 +        }
 +
 +        for (lint_name, &lint_span) in &self.declared_lints {
 +            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
 +            // file points to "<rustc macros>".
 +            // `compiletest-rs` thinks that's an error in a different file and
 +            // just ignores it. This causes the test in compile-fail/lint_pass
 +            // not able to capture the error.
 +            // Therefore, we need to climb the macro expansion tree and find the
 +            // actual span that invoked `declare_tool_lint!`:
 +            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
 +
 +            if !self.registered_lints.contains(lint_name) {
 +                span_lint(
 +                    cx,
 +                    LINT_WITHOUT_LINT_PASS,
 +                    lint_span,
 +                    &format!("the lint `{lint_name}` is not added to any `LintPass`"),
 +                );
 +            }
 +        }
 +    }
 +}
 +
++pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Rptr(
 +        _,
 +        MutTy {
 +            ty: inner,
 +            mutbl: Mutability::Not,
 +        },
 +    ) = ty.kind
 +    {
 +        if let TyKind::Path(ref path) = inner.kind {
 +            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
 +                return match_def_path(cx, def_id, &paths::LINT);
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
 +    if let Some(value) = extract_clippy_version_value(cx, item) {
 +        // The `sym!` macro doesn't work as it only expects a single token.
 +        // It's better to keep it this way and have a direct `Symbol::intern` call here.
 +        if value == Symbol::intern("pre 1.29.0") {
 +            return;
 +        }
 +
 +        if RustcVersion::parse(value.as_str()).is_err() {
 +            span_lint_and_help(
 +                cx,
 +                INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +                item.span,
 +                "this item has an invalid `clippy::version` attribute",
 +                None,
 +                "please use a valid semantic version, see `doc/adding_lints.md`",
 +            );
 +        }
 +    } else {
 +        span_lint_and_help(
 +            cx,
 +            MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +            item.span,
 +            "this lint is missing the `clippy::version` attribute or version value",
 +            None,
 +            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
 +        );
 +    }
 +}
 +
 +/// This function extracts the version value of a `clippy::version` attribute if the given value has
 +/// one
 +pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    attrs.iter().find_map(|attr| {
 +        if_chain! {
 +            // Identify attribute
 +            if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
 +            if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
 +            if tool_name.ident.name == sym::clippy;
 +            if attr_name.ident.name == sym::version;
 +            if let Some(version) = attr.value_str();
 +            then { Some(version) } else { None }
 +        }
 +    })
 +}
 +
 +struct LintCollector<'a, 'tcx> {
 +    output: &'a mut FxHashSet<Symbol>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
 +        if path.segments.len() == 1 {
 +            self.output.insert(path.segments[0].ident.name);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index cfba7fa8791de3c7567f09c8a41f643f7cd38427,0000000000000000000000000000000000000000..08980cb12ed6b4d94e9cff29002c97815e65dbc7
mode 100644,000000..100644
--- /dev/null
@@@ -1,334 -1,0 +1,303 @@@
- use clippy_utils::{def_path_res, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 +use clippy_utils::source::snippet_with_applicability;
- use rustc_middle::ty::{self, AssocKind, DefIdTree, Ty};
++use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, ExprKind, Local, Mutability, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::interpret::{Allocation, ConstValue, GlobalAlloc};
- use rustc_span::symbol::{Ident, Symbol};
++use rustc_middle::ty::{self, DefIdTree, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
-             if let Some(def_id) = inherent_def_path_res(cx, &segments[..]);
++use rustc_span::symbol::Symbol;
 +use rustc_span::Span;
 +
 +use std::str;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of def paths when a diagnostic item or a `LangItem` could be used.
 +    ///
 +    /// ### Why is this bad?
 +    /// The path for an item is subject to change and is less efficient to look up than a
 +    /// diagnostic item or a `LangItem`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
 +    /// ```
 +    pub UNNECESSARY_DEF_PATH,
 +    internal,
 +    "using a def path when a diagnostic item or a `LangItem` is available"
 +}
 +
 +impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
 +
 +#[derive(Default)]
 +pub struct UnnecessaryDefPath {
 +    array_def_ids: FxHashSet<(DefId, Span)>,
 +    linted_def_ids: FxHashSet<DefId>,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) {
 +            return;
 +        }
 +
 +        match expr.kind {
 +            ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span),
 +            ExprKind::Array(elements) => self.check_array(cx, elements, expr.span),
 +            _ => {},
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        for &(def_id, span) in &self.array_def_ids {
 +            if self.linted_def_ids.contains(&def_id) {
 +                continue;
 +            }
 +
 +            let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) {
 +                ("diagnostic item", format!("sym::{sym}"))
 +            } else if let Some(sym) = get_lang_item_name(cx, def_id) {
 +                ("language item", format!("LangItem::{sym}"))
 +            } else {
 +                continue;
 +            };
 +
 +            span_lint_and_help(
 +                cx,
 +                UNNECESSARY_DEF_PATH,
 +                span,
 +                &format!("hardcoded path to a {msg}"),
 +                None,
 +                &format!("convert all references to use `{sugg}`"),
 +            );
 +        }
 +    }
 +}
 +
 +impl UnnecessaryDefPath {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) {
 +        enum Item {
 +            LangItem(&'static str),
 +            DiagnosticItem(Symbol),
 +        }
 +        static PATHS: &[&[&str]] = &[
 +            &["clippy_utils", "match_def_path"],
 +            &["clippy_utils", "match_trait_method"],
 +            &["clippy_utils", "ty", "match_type"],
 +            &["clippy_utils", "is_expr_path_def_path"],
 +        ];
 +
 +        if_chain! {
 +            if let [cx_arg, def_arg, args @ ..] = args;
 +            if let ExprKind::Path(path) = &func.kind;
 +            if let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id();
 +            if let Some(which_path) = match_any_def_paths(cx, id, PATHS);
 +            let item_arg = if which_path == 4 { &args[1] } else { &args[0] };
 +            // Extract the path to the matched type
 +            if let Some(segments) = path_to_matched_type(cx, item_arg);
 +            let segments: Vec<&str> = segments.iter().map(|sym| &**sym).collect();
-         if let Some(def_id) = inherent_def_path_res(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
++            if let Some(def_id) = def_path_def_ids(cx, &segments[..]).next();
 +            then {
 +                // Check if the target item is a diagnostic item or LangItem.
 +                #[rustfmt::skip]
 +                let (msg, item) = if let Some(item_name)
 +                    = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id)
 +                {
 +                    (
 +                        "use of a def path to a diagnostic item",
 +                        Item::DiagnosticItem(*item_name),
 +                    )
 +                } else if let Some(item_name) = get_lang_item_name(cx, def_id) {
 +                    (
 +                        "use of a def path to a `LangItem`",
 +                        Item::LangItem(item_name),
 +                    )
 +                } else {
 +                    return;
 +                };
 +
 +                let has_ctor = match cx.tcx.def_kind(def_id) {
 +                    DefKind::Struct => {
 +                        let variant = cx.tcx.adt_def(def_id).non_enum_variant();
 +                        variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
 +                    },
 +                    DefKind::Variant => {
 +                        let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
 +                        variant.ctor_def_id.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
 +                    },
 +                    _ => false,
 +                };
 +
 +                let mut app = Applicability::MachineApplicable;
 +                let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
 +                let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
 +                let (sugg, with_note) = match (which_path, item) {
 +                    // match_def_path
 +                    (0, Item::DiagnosticItem(item)) => (
 +                        format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"),
 +                        has_ctor,
 +                    ),
 +                    (0, Item::LangItem(item)) => (
 +                        format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"),
 +                        has_ctor,
 +                    ),
 +                    // match_trait_method
 +                    (1, Item::DiagnosticItem(item)) => {
 +                        (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false)
 +                    },
 +                    // match_type
 +                    (2, Item::DiagnosticItem(item)) => (
 +                        format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
 +                        false,
 +                    ),
 +                    (2, Item::LangItem(item)) => (
 +                        format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"),
 +                        false,
 +                    ),
 +                    // is_expr_path_def_path
 +                    (3, Item::DiagnosticItem(item)) if has_ctor => (
 +                        format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",),
 +                        false,
 +                    ),
 +                    (3, Item::LangItem(item)) if has_ctor => (
 +                        format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",),
 +                        false,
 +                    ),
 +                    (3, Item::DiagnosticItem(item)) => (
 +                        format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
 +                        false,
 +                    ),
 +                    (3, Item::LangItem(item)) => (
 +                        format!(
 +                            "path_res({cx_snip}, {def_snip}).opt_def_id()\
 +                                .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))",
 +                        ),
 +                        false,
 +                    ),
 +                    _ => return,
 +                };
 +
 +                span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| {
 +                    diag.span_suggestion(span, "try", sugg, app);
 +                    if with_note {
 +                        diag.help(
 +                            "if this `DefId` came from a constructor expression or pattern then the \
 +                                    parent `DefId` should be used instead",
 +                        );
 +                    }
 +                });
 +
 +                self.linted_def_ids.insert(def_id);
 +            }
 +        }
 +    }
 +
 +    fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) {
 +        let Some(path) = path_from_array(elements) else { return };
 +
-         let &alloc = alloc.provenance().values().next()?;
++        for def_id in def_path_def_ids(cx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
 +            self.array_def_ids.insert((def_id, span));
 +        }
 +    }
 +}
 +
 +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<String>> {
 +    match peel_hir_expr_refs(expr).0.kind {
 +        ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) {
 +            Res::Local(hir_id) => {
 +                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
 +                if let Some(Node::Local(Local { init: Some(init), .. })) = cx.tcx.hir().find(parent_id) {
 +                    path_to_matched_type(cx, init)
 +                } else {
 +                    None
 +                }
 +            },
 +            Res::Def(DefKind::Static(_), def_id) => read_mir_alloc_def_path(
 +                cx,
 +                cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
 +                cx.tcx.type_of(def_id),
 +            ),
 +            Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
 +                ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => {
 +                    read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id))
 +                },
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        ExprKind::Array(exprs) => path_from_array(exprs),
 +        _ => None,
 +    }
 +}
 +
 +fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
 +    let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
- // def_path_res will match field names before anything else, but for this we want to match
- // inherent functions first.
- fn inherent_def_path_res(cx: &LateContext<'_>, segments: &[&str]) -> Option<DefId> {
-     def_path_res(cx, segments, None).opt_def_id().map(|def_id| {
-         if cx.tcx.def_kind(def_id) == DefKind::Field {
-             let method_name = *segments.last().unwrap();
-             cx.tcx
-                 .def_key(def_id)
-                 .parent
-                 .and_then(|parent_idx| {
-                     cx.tcx
-                         .inherent_impls(DefId {
-                             index: parent_idx,
-                             krate: def_id.krate,
-                         })
-                         .iter()
-                         .find_map(|impl_id| {
-                             cx.tcx.associated_items(*impl_id).find_by_name_and_kind(
-                                 cx.tcx,
-                                 Ident::from_str(method_name),
-                                 AssocKind::Fn,
-                                 *impl_id,
-                             )
-                         })
-                 })
-                 .map_or(def_id, |item| item.def_id)
-         } else {
-             def_id
-         }
-     })
- }
++        let &alloc = alloc.provenance().ptrs().values().next()?;
 +        if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
 +            (alloc.inner(), ty)
 +        } else {
 +            return None;
 +        }
 +    } else {
 +        (alloc, ty)
 +    };
 +
 +    if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind()
 +        && let ty::Ref(_, ty, Mutability::Not) = *ty.kind()
 +        && ty.is_str()
 +    {
 +        alloc
 +            .provenance()
++            .ptrs()
 +            .values()
 +            .map(|&alloc| {
 +                if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc) {
 +                    let alloc = alloc.inner();
 +                    str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
 +                        .ok().map(ToOwned::to_owned)
 +                } else {
 +                    None
 +                }
 +            })
 +            .collect()
 +    } else {
 +        None
 +    }
 +}
 +
 +fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> {
 +    exprs
 +        .iter()
 +        .map(|expr| {
 +            if let ExprKind::Lit(lit) = &expr.kind {
 +                if let LitKind::Str(sym, _) = lit.node {
 +                    return Some((*sym.as_str()).to_owned());
 +                }
 +            }
 +
 +            None
 +        })
 +        .collect()
 +}
 +
 +fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> {
 +    if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) {
 +        Some(lang_item.variant_name())
 +    } else {
 +        None
 +    }
 +}
index 36574198f9174a314dcc95ddf3356f1ba2aea54e,0000000000000000000000000000000000000000..6b321765bc082d8c1912bd1413e9dfdbee71e059
mode 100644,000000..100644
--- /dev/null
@@@ -1,544 -1,0 +1,557 @@@
-             sym::print_macro | sym::println_macro => {
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn, MacroCall};
 +use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
++use clippy_utils::{is_in_cfg_test, is_in_test_function};
 +use rustc_ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, HirIdMap, Impl, Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, BytePos};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `println!("")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// println!();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINTLN_EMPTY_STRING,
 +    style,
 +    "using `println!(\"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `print!()` with a format
 +    /// string that ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "World";
 +    /// print!("Hello {}!\n", name);
 +    /// ```
 +    /// use println!() instead
 +    /// ```rust
 +    /// # let name = "World";
 +    /// println!("Hello {}!", name);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_WITH_NEWLINE,
 +    style,
 +    "using `print!()` with a format string that ends in a single newline"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stdout*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stdout* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// Only catches `print!` and `println!` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("Hello world!");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_STDOUT,
 +    restriction,
 +    "printing on stdout"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stderr*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stderr* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// Only catches `eprint!` and `eprintln!` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// eprintln!("Hello world!");
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub PRINT_STDERR,
 +    restriction,
 +    "printing on stderr"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Debug` formatting. The purpose of this
 +    /// lint is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// The purpose of the `Debug` trait is to facilitate
 +    /// debugging Rust code. It should not be used in user-facing output.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = "bar";
 +    /// println!("{:?}", foo);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USE_DEBUG,
 +    restriction,
 +    "use of `Debug`-based formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `print!`/`println!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `println!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("{}", "foo");
 +    /// ```
 +    /// use the literal without formatting:
 +    /// ```rust
 +    /// println!("foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_LITERAL,
 +    style,
 +    "printing a literal with a format string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `writeln!(buf, "")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!(buf)`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITELN_EMPTY_STRING,
 +    style,
 +    "using `writeln!(buf, \"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `write!()` with a format
 +    /// string that
 +    /// ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// write!(buf, "Hello {}!\n", name);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// writeln!(buf, "Hello {}!", name);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_WITH_NEWLINE,
 +    style,
 +    "using `write!()` with a format string that ends in a single newline"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `write!`/`writeln!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `writeln!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "{}", "foo");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_LITERAL,
 +    style,
 +    "writing a literal with a format string"
 +}
 +
 +#[derive(Default)]
 +pub struct Write {
 +    in_debug_impl: bool,
++    allow_print_in_tests: bool,
++}
++
++impl Write {
++    pub fn new(allow_print_in_tests: bool) -> Self {
++        Self {
++            allow_print_in_tests,
++            ..Default::default()
++        }
++    }
 +}
 +
 +impl_lint_pass!(Write => [
 +    PRINT_WITH_NEWLINE,
 +    PRINTLN_EMPTY_STRING,
 +    PRINT_STDOUT,
 +    PRINT_STDERR,
 +    USE_DEBUG,
 +    PRINT_LITERAL,
 +    WRITE_WITH_NEWLINE,
 +    WRITELN_EMPTY_STRING,
 +    WRITE_LITERAL,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Write {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        if is_debug_impl(cx, item) {
 +            self.in_debug_impl = true;
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        if is_debug_impl(cx, item) {
 +            self.in_debug_impl = false;
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
 +        let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id) else { return };
 +        let Some(name) = diag_name.as_str().strip_suffix("_macro") else { return };
 +
 +        let is_build_script = cx
 +            .sess()
 +            .opts
 +            .crate_name
 +            .as_ref()
 +            .map_or(false, |crate_name| crate_name == "build_script_build");
 +
++        let allowed_in_tests = self.allow_print_in_tests
++            && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id));
 +        match diag_name {
-             sym::eprint_macro | sym::eprintln_macro => {
++            sym::print_macro | sym::println_macro if !allowed_in_tests => {
 +                if !is_build_script {
 +                    span_lint(cx, PRINT_STDOUT, macro_call.span, &format!("use of `{name}!`"));
 +                }
 +            },
++            sym::eprint_macro | sym::eprintln_macro if !allowed_in_tests => {
 +                span_lint(cx, PRINT_STDERR, macro_call.span, &format!("use of `{name}!`"));
 +            },
 +            sym::write_macro | sym::writeln_macro => {},
 +            _ => return,
 +        }
 +
 +        let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn) else { return };
 +
 +        // ignore `writeln!(w)` and `write!(v, some_macro!())`
 +        if format_args.format_string.span.from_expansion() {
 +            return;
 +        }
 +
 +        match diag_name {
 +            sym::print_macro | sym::eprint_macro | sym::write_macro => {
 +                check_newline(cx, &format_args, &macro_call, name);
 +            },
 +            sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
 +                check_empty_string(cx, &format_args, &macro_call, name);
 +            },
 +            _ => {},
 +        }
 +
 +        check_literal(cx, &format_args, name);
 +
 +        if !self.in_debug_impl {
 +            for arg in &format_args.args {
 +                if arg.format.r#trait == sym::Debug {
 +                    span_lint(cx, USE_DEBUG, arg.span, "use of `Debug`-based formatting");
 +                }
 +            }
 +        }
 +    }
 +}
 +fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
 +    if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), .. }) = &item.kind
 +        && let Some(trait_id) = trait_ref.trait_def_id()
 +    {
 +        cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
 +    } else {
 +        false
 +    }
 +}
 +
 +fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
 +    let format_string_parts = &format_args.format_string.parts;
 +    let mut format_string_span = format_args.format_string.span;
 +
 +    let Some(last) = format_string_parts.last() else { return };
 +
 +    let count_vertical_whitespace = || {
 +        format_string_parts
 +            .iter()
 +            .flat_map(|part| part.as_str().chars())
 +            .filter(|ch| matches!(ch, '\r' | '\n'))
 +            .count()
 +    };
 +
 +    if last.as_str().ends_with('\n')
 +        // ignore format strings with other internal vertical whitespace
 +        && count_vertical_whitespace() == 1
 +
 +        // ignore trailing arguments: `print!("Issue\n{}", 1265);`
 +        && format_string_parts.len() > format_args.args.len()
 +    {
 +        let lint = if name == "write" {
 +            format_string_span = expand_past_previous_comma(cx, format_string_span);
 +
 +            WRITE_WITH_NEWLINE
 +        } else {
 +            PRINT_WITH_NEWLINE
 +        };
 +
 +        span_lint_and_then(
 +            cx,
 +            lint,
 +            macro_call.span,
 +            &format!("using `{name}!()` with a format string that ends in a single newline"),
 +            |diag| {
 +                let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
 +                let Some(format_snippet) = snippet_opt(cx, format_string_span) else { return };
 +
 +                if format_string_parts.len() == 1 && last.as_str() == "\n" {
 +                    // print!("\n"), write!(f, "\n")
 +
 +                    diag.multipart_suggestion(
 +                        &format!("use `{name}ln!` instead"),
 +                        vec![(name_span, format!("{name}ln")), (format_string_span, String::new())],
 +                        Applicability::MachineApplicable,
 +                    );
 +                } else if format_snippet.ends_with("\\n\"") {
 +                    // print!("...\n"), write!(f, "...\n")
 +
 +                    let hi = format_string_span.hi();
 +                    let newline_span = format_string_span.with_lo(hi - BytePos(3)).with_hi(hi - BytePos(1));
 +
 +                    diag.multipart_suggestion(
 +                        &format!("use `{name}ln!` instead"),
 +                        vec![(name_span, format!("{name}ln")), (newline_span, String::new())],
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            },
 +        );
 +    }
 +}
 +
 +fn check_empty_string(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, macro_call: &MacroCall, name: &str) {
 +    if let [part] = &format_args.format_string.parts[..]
 +        && let mut span = format_args.format_string.span
 +        && part.as_str() == "\n"
 +    {
 +        let lint = if name == "writeln" {
 +            span = expand_past_previous_comma(cx, span);
 +
 +            WRITELN_EMPTY_STRING
 +        } else {
 +            PRINTLN_EMPTY_STRING
 +        };
 +
 +        span_lint_and_then(
 +            cx,
 +            lint,
 +            macro_call.span,
 +            &format!("empty string literal in `{name}!`"),
 +            |diag| {
 +                diag.span_suggestion(
 +                    span,
 +                    "remove the empty string",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +        );
 +    }
 +}
 +
 +fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgsExpn<'_>, name: &str) {
 +    let mut counts = HirIdMap::<usize>::default();
 +    for param in format_args.params() {
 +        *counts.entry(param.value.hir_id).or_default() += 1;
 +    }
 +
 +    for arg in &format_args.args {
 +        let value = arg.param.value;
 +
 +        if counts[&value.hir_id] == 1
 +            && arg.format.is_default()
 +            && let ExprKind::Lit(lit) = &value.kind
 +            && !value.span.from_expansion()
 +            && let Some(value_string) = snippet_opt(cx, value.span)
 +        {
 +            let (replacement, replace_raw) = match lit.node {
 +                LitKind::Str(..) => extract_str_literal(&value_string),
 +                LitKind::Char(ch) => (
 +                    match ch {
 +                        '"' => "\\\"",
 +                        '\'' => "'",
 +                        _ => &value_string[1..value_string.len() - 1],
 +                    }
 +                    .to_string(),
 +                    false,
 +                ),
 +                LitKind::Bool(b) => (b.to_string(), false),
 +                _ => continue,
 +            };
 +
 +            let lint = if name.starts_with("write") {
 +                WRITE_LITERAL
 +            } else {
 +                PRINT_LITERAL
 +            };
 +
 +            let format_string_is_raw = format_args.format_string.style.is_some();
 +            let replacement = match (format_string_is_raw, replace_raw) {
 +                (false, false) => Some(replacement),
 +                (false, true) => Some(replacement.replace('"', "\\\"").replace('\\', "\\\\")),
 +                (true, false) => match conservative_unescape(&replacement) {
 +                    Ok(unescaped) => Some(unescaped),
 +                    Err(UnescapeErr::Lint) => None,
 +                    Err(UnescapeErr::Ignore) => continue,
 +                },
 +                (true, true) => {
 +                    if replacement.contains(['#', '"']) {
 +                        None
 +                    } else {
 +                        Some(replacement)
 +                    }
 +                },
 +            };
 +
 +            span_lint_and_then(
 +                cx,
 +                lint,
 +                value.span,
 +                "literal with an empty format string",
 +                |diag| {
 +                    if let Some(replacement) = replacement
 +                        // `format!("{}", "a")`, `format!("{named}", named = "b")
 +                        //              ~~~~~                      ~~~~~~~~~~~~~
 +                        && let Some(value_span) = format_args.value_with_prev_comma_span(value.hir_id)
 +                    {
 +                        let replacement = replacement.replace('{', "{{").replace('}', "}}");
 +                        diag.multipart_suggestion(
 +                            "try this",
 +                            vec![(arg.span, replacement), (value_span, String::new())],
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +/// Removes the raw marker, `#`s and quotes from a str, and returns if the literal is raw
 +///
 +/// `r#"a"#` -> (`a`, true)
 +///
 +/// `"b"` -> (`b`, false)
 +fn extract_str_literal(literal: &str) -> (String, bool) {
 +    let (literal, raw) = match literal.strip_prefix('r') {
 +        Some(stripped) => (stripped.trim_matches('#'), true),
 +        None => (literal, false),
 +    };
 +
 +    (literal[1..literal.len() - 1].to_string(), raw)
 +}
 +
 +enum UnescapeErr {
 +    /// Should still be linted, can be manually resolved by author, e.g.
 +    ///
 +    /// ```ignore
 +    /// print!(r"{}", '"');
 +    /// ```
 +    Lint,
 +    /// Should not be linted, e.g.
 +    ///
 +    /// ```ignore
 +    /// print!(r"{}", '\r');
 +    /// ```
 +    Ignore,
 +}
 +
 +/// Unescape a normal string into a raw string
 +fn conservative_unescape(literal: &str) -> Result<String, UnescapeErr> {
 +    let mut unescaped = String::with_capacity(literal.len());
 +    let mut chars = literal.chars();
 +    let mut err = false;
 +
 +    while let Some(ch) = chars.next() {
 +        match ch {
 +            '#' => err = true,
 +            '\\' => match chars.next() {
 +                Some('\\') => unescaped.push('\\'),
 +                Some('"') => err = true,
 +                _ => return Err(UnescapeErr::Ignore),
 +            },
 +            _ => unescaped.push(ch),
 +        }
 +    }
 +
 +    if err { Err(UnescapeErr::Lint) } else { Ok(unescaped) }
 +}
index 83fee7bb39c22a60fa569b8079901f95db3d1fb0,0000000000000000000000000000000000000000..fb9f4740ecc5003922077019e7c3c12e4d634e77
mode 100644,000000..100644
--- /dev/null
@@@ -1,19 -1,0 +1,19 @@@
- version = "0.1.66"
 +[package]
 +name = "clippy_utils"
++version = "0.1.67"
 +edition = "2021"
 +publish = false
 +
 +[dependencies]
 +arrayvec = { version = "0.7", default-features = false }
 +if_chain = "1.0"
 +itertools = "0.10.1"
 +rustc-semver = "1.1"
 +
 +[features]
 +deny-warnings = []
 +internal = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 23aed4b5ba2f40f2c675009c338736431a5fdd1e,0000000000000000000000000000000000000000..939c61189ec8dadf090cad8f5ee3019143fa5e2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,734 -1,0 +1,742 @@@
-             MethodCall(box ast::MethodCall { seg: ls, receiver: lr, args: la, .. }),
-             MethodCall(box ast::MethodCall { seg: rs, receiver: rr, args: ra, .. })
-         ) => {
-             eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r))
-         },
 +//! Utilities for manipulating and extracting information from `rustc_ast::ast`.
 +//!
 +//! - The `eq_foobar` functions test for semantic equality but ignores `NodeId`s and `Span`s.
 +
 +#![allow(clippy::similar_names, clippy::wildcard_imports, clippy::enum_glob_use)]
 +
 +use crate::{both, over};
 +use rustc_ast::ptr::P;
 +use rustc_ast::{self as ast, *};
 +use rustc_span::symbol::Ident;
 +use std::mem;
 +
 +pub mod ident_iter;
 +pub use ident_iter::IdentIter;
 +
 +pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
 +    use BinOpKind::*;
 +    matches!(
 +        kind,
 +        Sub | Div | Eq | Lt | Le | Gt | Ge | Ne | And | Or | BitXor | BitAnd | BitOr
 +    )
 +}
 +
 +/// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
 +pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
 +}
 +
 +pub fn eq_id(l: Ident, r: Ident) -> bool {
 +    l.name == r.name
 +}
 +
 +pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
 +    use PatKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_pat(l, r),
 +        (_, Paren(r)) => eq_pat(l, r),
 +        (Wild, Wild) | (Rest, Rest) => true,
 +        (Lit(l), Lit(r)) => eq_expr(l, r),
 +        (Ident(b1, i1, s1), Ident(b2, i2, s2)) => b1 == b2 && eq_id(*i1, *i2) && both(s1, s2, |l, r| eq_pat(l, r)),
 +        (Range(lf, lt, le), Range(rf, rt, re)) => {
 +            eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt) && eq_range_end(&le.node, &re.node)
 +        },
 +        (Box(l), Box(r))
 +        | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
 +        | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
 +        (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
 +        (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
 +            eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
 +        },
 +        (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
 +            lr == rr && eq_maybe_qself(lqself, rqself) && eq_path(lp, rp) && unordered_over(lfs, rfs, eq_field_pat)
 +        },
 +        (Or(ls), Or(rs)) => unordered_over(ls, rs, |l, r| eq_pat(l, r)),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_range_end(l: &RangeEnd, r: &RangeEnd) -> bool {
 +    match (l, r) {
 +        (RangeEnd::Excluded, RangeEnd::Excluded) => true,
 +        (RangeEnd::Included(l), RangeEnd::Included(r)) => {
 +            matches!(l, RangeSyntax::DotDotEq) == matches!(r, RangeSyntax::DotDotEq)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && eq_pat(&l.pat, &r.pat)
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_qself(l: &P<QSelf>, r: &P<QSelf>) -> bool {
 +    l.position == r.position && eq_ty(&l.ty, &r.ty)
 +}
 +
 +pub fn eq_maybe_qself(l: &Option<P<QSelf>>, r: &Option<P<QSelf>>) -> bool {
 +    match (l, r) {
 +        (Some(l), Some(r)) => eq_qself(l, r),
 +        (None, None) => true,
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_path(l: &Path, r: &Path) -> bool {
 +    over(&l.segments, &r.segments, eq_path_seg)
 +}
 +
 +pub fn eq_path_seg(l: &PathSegment, r: &PathSegment) -> bool {
 +    eq_id(l.ident, r.ident) && both(&l.args, &r.args, |l, r| eq_generic_args(l, r))
 +}
 +
 +pub fn eq_generic_args(l: &GenericArgs, r: &GenericArgs) -> bool {
 +    match (l, r) {
 +        (GenericArgs::AngleBracketed(l), GenericArgs::AngleBracketed(r)) => over(&l.args, &r.args, eq_angle_arg),
 +        (GenericArgs::Parenthesized(l), GenericArgs::Parenthesized(r)) => {
 +            over(&l.inputs, &r.inputs, |l, r| eq_ty(l, r)) && eq_fn_ret_ty(&l.output, &r.output)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_angle_arg(l: &AngleBracketedArg, r: &AngleBracketedArg) -> bool {
 +    match (l, r) {
 +        (AngleBracketedArg::Arg(l), AngleBracketedArg::Arg(r)) => eq_generic_arg(l, r),
 +        (AngleBracketedArg::Constraint(l), AngleBracketedArg::Constraint(r)) => eq_assoc_constraint(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
 +    match (l, r) {
 +        (GenericArg::Lifetime(l), GenericArg::Lifetime(r)) => eq_id(l.ident, r.ident),
 +        (GenericArg::Type(l), GenericArg::Type(r)) => eq_ty(l, r),
 +        (GenericArg::Const(l), GenericArg::Const(r)) => eq_expr(&l.value, &r.value),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_expr_opt(l: &Option<P<Expr>>, r: &Option<P<Expr>>) -> bool {
 +    both(l, r, |l, r| eq_expr(l, r))
 +}
 +
 +pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
 +    match (l, r) {
 +        (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb),
 +        (StructRest::Rest(_), StructRest::Rest(_)) | (StructRest::None, StructRest::None) => true,
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
 +    use ExprKind::*;
 +    if !over(&l.attrs, &r.attrs, eq_attr) {
 +        return false;
 +    }
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_expr(l, r),
 +        (_, Paren(r)) => eq_expr(l, r),
 +        (Err, Err) => true,
 +        (Box(l), Box(r)) | (Try(l), Try(r)) | (Await(l), Await(r)) => eq_expr(l, r),
 +        (Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
 +        (Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
 +        (Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
 +        (
-             })
++            MethodCall(box ast::MethodCall {
++                seg: ls,
++                receiver: lr,
++                args: la,
++                ..
++            }),
++            MethodCall(box ast::MethodCall {
++                seg: rs,
++                receiver: rr,
++                args: ra,
++                ..
++            }),
++        ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)),
 +        (Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
 +        (Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
 +        (Lit(l), Lit(r)) => l == r,
 +        (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
 +        (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
 +        (If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
 +        (While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
 +        (ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
 +            eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
 +        },
 +        (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
 +        (Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
 +        (TryBlock(l), TryBlock(r)) => eq_block(l, r),
 +        (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
 +        (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re),
 +        (Continue(ll), Continue(rl)) => eq_label(ll, rl),
 +        (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2),
 +        (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
 +        (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
 +        (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
 +        (
 +            Closure(box ast::Closure {
 +                binder: lb,
 +                capture_clause: lc,
 +                asyncness: la,
 +                movability: lm,
 +                fn_decl: lf,
 +                body: le,
 +                ..
 +            }),
 +            Closure(box ast::Closure {
 +                binder: rb,
 +                capture_clause: rc,
 +                asyncness: ra,
 +                movability: rm,
 +                fn_decl: rf,
 +                body: re,
 +                ..
++            }),
 +        ) => {
 +            eq_closure_binder(lb, rb)
 +                && lc == rc
 +                && la.is_async() == ra.is_async()
 +                && lm == rm
 +                && eq_fn_decl(lf, rf)
 +                && eq_expr(le, re)
 +        },
 +        (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
 +        (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
 +        (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        (Struct(lse), Struct(rse)) => {
 +            eq_maybe_qself(&lse.qself, &rse.qself)
 +                && eq_path(&lse.path, &rse.path)
 +                && eq_struct_rest(&lse.rest, &rse.rest)
 +                && unordered_over(&lse.fields, &rse.fields, eq_field)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && eq_expr(&l.expr, &r.expr)
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && eq_pat(&l.pat, &r.pat)
 +        && eq_expr(&l.body, &r.body)
 +        && eq_expr_opt(&l.guard, &r.guard)
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_label(l: &Option<Label>, r: &Option<Label>) -> bool {
 +    both(l, r, |l, r| eq_id(l.ident, r.ident))
 +}
 +
 +pub fn eq_block(l: &Block, r: &Block) -> bool {
 +    l.rules == r.rules && over(&l.stmts, &r.stmts, eq_stmt)
 +}
 +
 +pub fn eq_stmt(l: &Stmt, r: &Stmt) -> bool {
 +    use StmtKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Local(l), Local(r)) => {
 +            eq_pat(&l.pat, &r.pat)
 +                && both(&l.ty, &r.ty, |l, r| eq_ty(l, r))
 +                && eq_local_kind(&l.kind, &r.kind)
 +                && over(&l.attrs, &r.attrs, eq_attr)
 +        },
 +        (Item(l), Item(r)) => eq_item(l, r, eq_item_kind),
 +        (Expr(l), Expr(r)) | (Semi(l), Semi(r)) => eq_expr(l, r),
 +        (Empty, Empty) => true,
 +        (MacCall(l), MacCall(r)) => {
 +            l.style == r.style && eq_mac_call(&l.mac, &r.mac) && over(&l.attrs, &r.attrs, eq_attr)
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_local_kind(l: &LocalKind, r: &LocalKind) -> bool {
 +    use LocalKind::*;
 +    match (l, r) {
 +        (Decl, Decl) => true,
 +        (Init(l), Init(r)) => eq_expr(l, r),
 +        (InitElse(li, le), InitElse(ri, re)) => eq_expr(li, ri) && eq_block(le, re),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> bool) -> bool {
 +    eq_id(l.ident, r.ident) && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
 +}
 +
 +#[expect(clippy::too_many_lines)] // Just a big match statement
 +pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
 +    use ItemKind::*;
 +    match (l, r) {
 +        (ExternCrate(l), ExternCrate(r)) => l == r,
 +        (Use(l), Use(r)) => eq_use_tree(l, r),
 +        (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (
 +            Fn(box ast::Fn {
 +                defaultness: ld,
 +                sig: lf,
 +                generics: lg,
 +                body: lb,
 +            }),
 +            Fn(box ast::Fn {
 +                defaultness: rd,
 +                sig: rf,
 +                generics: rg,
 +                body: rb,
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (Mod(lu, lmk), Mod(ru, rmk)) => {
 +            lu == ru
 +                && match (lmk, rmk) {
 +                    (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
 +                        linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
 +                    },
 +                    (ModKind::Unloaded, ModKind::Unloaded) => true,
 +                    _ => false,
 +                }
 +        },
 +        (ForeignMod(l), ForeignMod(r)) => {
 +            both(&l.abi, &r.abi, eq_str_lit) && over(&l.items, &r.items, |l, r| eq_item(l, r, eq_foreign_item_kind))
 +        },
 +        (
 +            TyAlias(box ast::TyAlias {
 +                defaultness: ld,
 +                generics: lg,
 +                bounds: lb,
 +                ty: lt,
 +                ..
 +            }),
 +            TyAlias(box ast::TyAlias {
 +                defaultness: rd,
 +                generics: rg,
 +                bounds: rb,
 +                ty: rt,
 +                ..
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (Enum(le, lg), Enum(re, rg)) => over(&le.variants, &re.variants, eq_variant) && eq_generics(lg, rg),
 +        (Struct(lv, lg), Struct(rv, rg)) | (Union(lv, lg), Union(rv, rg)) => {
 +            eq_variant_data(lv, rv) && eq_generics(lg, rg)
 +        },
 +        (
 +            Trait(box ast::Trait {
 +                is_auto: la,
 +                unsafety: lu,
 +                generics: lg,
 +                bounds: lb,
 +                items: li,
 +            }),
 +            Trait(box ast::Trait {
 +                is_auto: ra,
 +                unsafety: ru,
 +                generics: rg,
 +                bounds: rb,
 +                items: ri,
 +            }),
 +        ) => {
 +            la == ra
 +                && matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
 +        },
 +        (TraitAlias(lg, lb), TraitAlias(rg, rb)) => eq_generics(lg, rg) && over(lb, rb, eq_generic_bound),
 +        (
 +            Impl(box ast::Impl {
 +                unsafety: lu,
 +                polarity: lp,
 +                defaultness: ld,
 +                constness: lc,
 +                generics: lg,
 +                of_trait: lot,
 +                self_ty: lst,
 +                items: li,
 +            }),
 +            Impl(box ast::Impl {
 +                unsafety: ru,
 +                polarity: rp,
 +                defaultness: rd,
 +                constness: rc,
 +                generics: rg,
 +                of_trait: rot,
 +                self_ty: rst,
 +                items: ri,
 +            }),
 +        ) => {
 +            matches!(lu, Unsafe::No) == matches!(ru, Unsafe::No)
 +                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
 +                && eq_defaultness(*ld, *rd)
 +                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
 +                && eq_generics(lg, rg)
 +                && both(lot, rot, |l, r| eq_path(&l.path, &r.path))
 +                && eq_ty(lst, rst)
 +                && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
 +    use ForeignItemKind::*;
 +    match (l, r) {
 +        (Static(lt, lm, le), Static(rt, rm, re)) => lm == rm && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (
 +            Fn(box ast::Fn {
 +                defaultness: ld,
 +                sig: lf,
 +                generics: lg,
 +                body: lb,
 +            }),
 +            Fn(box ast::Fn {
 +                defaultness: rd,
 +                sig: rf,
 +                generics: rg,
 +                body: rb,
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (
 +            TyAlias(box ast::TyAlias {
 +                defaultness: ld,
 +                generics: lg,
 +                bounds: lb,
 +                ty: lt,
 +                ..
 +            }),
 +            TyAlias(box ast::TyAlias {
 +                defaultness: rd,
 +                generics: rg,
 +                bounds: rb,
 +                ty: rt,
 +                ..
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
 +    use AssocItemKind::*;
 +    match (l, r) {
 +        (Const(ld, lt, le), Const(rd, rt, re)) => eq_defaultness(*ld, *rd) && eq_ty(lt, rt) && eq_expr_opt(le, re),
 +        (
 +            Fn(box ast::Fn {
 +                defaultness: ld,
 +                sig: lf,
 +                generics: lg,
 +                body: lb,
 +            }),
 +            Fn(box ast::Fn {
 +                defaultness: rd,
 +                sig: rf,
 +                generics: rg,
 +                body: rb,
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd) && eq_fn_sig(lf, rf) && eq_generics(lg, rg) && both(lb, rb, |l, r| eq_block(l, r))
 +        },
 +        (
 +            Type(box ast::TyAlias {
 +                defaultness: ld,
 +                generics: lg,
 +                bounds: lb,
 +                ty: lt,
 +                ..
 +            }),
 +            Type(box ast::TyAlias {
 +                defaultness: rd,
 +                generics: rg,
 +                bounds: rb,
 +                ty: rt,
 +                ..
 +            }),
 +        ) => {
 +            eq_defaultness(*ld, *rd)
 +                && eq_generics(lg, rg)
 +                && over(lb, rb, eq_generic_bound)
 +                && both(lt, rt, |l, r| eq_ty(l, r))
 +        },
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_variant(l: &Variant, r: &Variant) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +        && eq_vis(&l.vis, &r.vis)
 +        && eq_id(l.ident, r.ident)
 +        && eq_variant_data(&l.data, &r.data)
 +        && both(&l.disr_expr, &r.disr_expr, |l, r| eq_expr(&l.value, &r.value))
 +}
 +
 +pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
 +    use VariantData::*;
 +    match (l, r) {
 +        (Unit(_), Unit(_)) => true,
 +        (Struct(l, _), Struct(r, _)) | (Tuple(l, _), Tuple(r, _)) => over(l, r, eq_struct_field),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
 +    l.is_placeholder == r.is_placeholder
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +        && eq_vis(&l.vis, &r.vis)
 +        && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
 +        && eq_ty(&l.ty, &r.ty)
 +}
 +
 +pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
 +    eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
 +}
 +
 +pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
 +    matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
 +        && l.asyncness.is_async() == r.asyncness.is_async()
 +        && matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
 +        && eq_ext(&l.ext, &r.ext)
 +}
 +
 +pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
 +    over(&l.params, &r.params, eq_generic_param)
 +        && over(&l.where_clause.predicates, &r.where_clause.predicates, |l, r| {
 +            eq_where_predicate(l, r)
 +        })
 +}
 +
 +pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
 +    use WherePredicate::*;
 +    match (l, r) {
 +        (BoundPredicate(l), BoundPredicate(r)) => {
 +            over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
 +                eq_generic_param(l, r)
 +            }) && eq_ty(&l.bounded_ty, &r.bounded_ty)
 +                && over(&l.bounds, &r.bounds, eq_generic_bound)
 +        },
 +        (RegionPredicate(l), RegionPredicate(r)) => {
 +            eq_id(l.lifetime.ident, r.lifetime.ident) && over(&l.bounds, &r.bounds, eq_generic_bound)
 +        },
 +        (EqPredicate(l), EqPredicate(r)) => eq_ty(&l.lhs_ty, &r.lhs_ty) && eq_ty(&l.rhs_ty, &r.rhs_ty),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
 +    eq_path(&l.prefix, &r.prefix) && eq_use_tree_kind(&l.kind, &r.kind)
 +}
 +
 +pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
 +    eq_expr(&l.value, &r.value)
 +}
 +
 +pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
 +    use UseTreeKind::*;
 +    match (l, r) {
 +        (Glob, Glob) => true,
 +        (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
 +        (Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_defaultness(l: Defaultness, r: Defaultness) -> bool {
 +    matches!(
 +        (l, r),
 +        (Defaultness::Final, Defaultness::Final) | (Defaultness::Default(_), Defaultness::Default(_))
 +    )
 +}
 +
 +pub fn eq_vis(l: &Visibility, r: &Visibility) -> bool {
 +    use VisibilityKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Public, Public) | (Inherited, Inherited) => true,
 +        (Restricted { path: l, .. }, Restricted { path: r, .. }) => eq_path(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
 +    eq_fn_ret_ty(&l.output, &r.output)
 +        && over(&l.inputs, &r.inputs, |l, r| {
 +            l.is_placeholder == r.is_placeholder
 +                && eq_pat(&l.pat, &r.pat)
 +                && eq_ty(&l.ty, &r.ty)
 +                && over(&l.attrs, &r.attrs, eq_attr)
 +        })
 +}
 +
 +pub fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool {
 +    match (l, r) {
 +        (ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true,
 +        (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => {
 +            lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r))
 +        },
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_fn_ret_ty(l: &FnRetTy, r: &FnRetTy) -> bool {
 +    match (l, r) {
 +        (FnRetTy::Default(_), FnRetTy::Default(_)) => true,
 +        (FnRetTy::Ty(l), FnRetTy::Ty(r)) => eq_ty(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
 +    use TyKind::*;
 +    match (&l.kind, &r.kind) {
 +        (Paren(l), _) => eq_ty(l, r),
 +        (_, Paren(r)) => eq_ty(l, r),
 +        (Never, Never) | (Infer, Infer) | (ImplicitSelf, ImplicitSelf) | (Err, Err) | (CVarArgs, CVarArgs) => true,
 +        (Slice(l), Slice(r)) => eq_ty(l, r),
 +        (Array(le, ls), Array(re, rs)) => eq_ty(le, re) && eq_expr(&ls.value, &rs.value),
 +        (Ptr(l), Ptr(r)) => l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty),
 +        (Rptr(ll, l), Rptr(rl, r)) => {
 +            both(ll, rl, |l, r| eq_id(l.ident, r.ident)) && l.mutbl == r.mutbl && eq_ty(&l.ty, &r.ty)
 +        },
 +        (BareFn(l), BareFn(r)) => {
 +            l.unsafety == r.unsafety
 +                && eq_ext(&l.ext, &r.ext)
 +                && over(&l.generic_params, &r.generic_params, eq_generic_param)
 +                && eq_fn_decl(&l.decl, &r.decl)
 +        },
 +        (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
 +        (Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
 +        (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
 +        (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
 +        (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
 +        (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
 +    use Extern::*;
 +    match (l, r) {
 +        (None, None) | (Implicit(_), Implicit(_)) => true,
 +        (Explicit(l, _), Explicit(r, _)) => eq_str_lit(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_str_lit(l: &StrLit, r: &StrLit) -> bool {
 +    l.style == r.style && l.symbol == r.symbol && l.suffix == r.suffix
 +}
 +
 +pub fn eq_poly_ref_trait(l: &PolyTraitRef, r: &PolyTraitRef) -> bool {
 +    eq_path(&l.trait_ref.path, &r.trait_ref.path)
 +        && over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
 +            eq_generic_param(l, r)
 +        })
 +}
 +
 +pub fn eq_generic_param(l: &GenericParam, r: &GenericParam) -> bool {
 +    use GenericParamKind::*;
 +    l.is_placeholder == r.is_placeholder
 +        && eq_id(l.ident, r.ident)
 +        && over(&l.bounds, &r.bounds, eq_generic_bound)
 +        && match (&l.kind, &r.kind) {
 +            (Lifetime, Lifetime) => true,
 +            (Type { default: l }, Type { default: r }) => both(l, r, |l, r| eq_ty(l, r)),
 +            (
 +                Const {
 +                    ty: lt,
 +                    kw_span: _,
 +                    default: ld,
 +                },
 +                Const {
 +                    ty: rt,
 +                    kw_span: _,
 +                    default: rd,
 +                },
 +            ) => eq_ty(lt, rt) && both(ld, rd, eq_anon_const),
 +            _ => false,
 +        }
 +        && over(&l.attrs, &r.attrs, eq_attr)
 +}
 +
 +pub fn eq_generic_bound(l: &GenericBound, r: &GenericBound) -> bool {
 +    use GenericBound::*;
 +    match (l, r) {
 +        (Trait(ptr1, tbm1), Trait(ptr2, tbm2)) => tbm1 == tbm2 && eq_poly_ref_trait(ptr1, ptr2),
 +        (Outlives(l), Outlives(r)) => eq_id(l.ident, r.ident),
 +        _ => false,
 +    }
 +}
 +
 +fn eq_term(l: &Term, r: &Term) -> bool {
 +    match (l, r) {
 +        (Term::Ty(l), Term::Ty(r)) => eq_ty(l, r),
 +        (Term::Const(l), Term::Const(r)) => eq_anon_const(l, r),
 +        _ => false,
 +    }
 +}
 +
 +pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool {
 +    use AssocConstraintKind::*;
 +    eq_id(l.ident, r.ident)
 +        && match (&l.kind, &r.kind) {
 +            (Equality { term: l }, Equality { term: r }) => eq_term(l, r),
 +            (Bound { bounds: l }, Bound { bounds: r }) => over(l, r, eq_generic_bound),
 +            _ => false,
 +        }
 +}
 +
 +pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
 +    eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
 +}
 +
 +pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
 +    use AttrKind::*;
 +    l.style == r.style
 +        && match (&l.kind, &r.kind) {
 +            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
 +            (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_mac_args(&l.item.args, &r.item.args),
 +            _ => false,
 +        }
 +}
 +
 +pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
 +    use MacArgs::*;
 +    match (l, r) {
 +        (Empty, Empty) => true,
 +        (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
 +        (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re),
 +        (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind,
 +        _ => false,
 +    }
 +}
index 07e4ef6a2fef3c4c8f6ed67d9ad1cb80205cc36e,0000000000000000000000000000000000000000..315aea9aa091bc3b4f8f8f3d3e83fb0cc3c83b2f
mode 100644,000000..100644
--- /dev/null
@@@ -1,682 -1,0 +1,678 @@@
-             (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
-             (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
 +#![allow(clippy::float_cmp)]
 +
 +use crate::{clip, is_direct_expn_of, sext, unsext};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitFloatType, LitKind};
 +use rustc_data_structures::sync::Lrc;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir;
 +use rustc_middle::mir::interpret::Scalar;
 +use rustc_middle::ty::SubstsRef;
 +use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
 +use rustc_middle::{bug, span_bug};
 +use rustc_span::symbol::Symbol;
 +use std::cmp::Ordering::{self, Equal};
 +use std::hash::{Hash, Hasher};
 +use std::iter;
 +
 +/// A `LitKind`-like enum to fold constant `Expr`s into.
 +#[derive(Debug, Clone)]
 +pub enum Constant {
 +    /// A `String` (e.g., "abc").
 +    Str(String),
 +    /// A binary string (e.g., `b"abc"`).
 +    Binary(Lrc<[u8]>),
 +    /// A single `char` (e.g., `'a'`).
 +    Char(char),
 +    /// An integer's bit representation.
 +    Int(u128),
 +    /// An `f32`.
 +    F32(f32),
 +    /// An `f64`.
 +    F64(f64),
 +    /// `true` or `false`.
 +    Bool(bool),
 +    /// An array of constants.
 +    Vec(Vec<Constant>),
 +    /// Also an array, but with only one constant, repeated N times.
 +    Repeat(Box<Constant>, u64),
 +    /// A tuple of constants.
 +    Tuple(Vec<Constant>),
 +    /// A raw pointer.
 +    RawPtr(u128),
 +    /// A reference
 +    Ref(Box<Constant>),
 +    /// A literal with syntax error.
 +    Err,
 +}
 +
 +impl PartialEq for Constant {
 +    fn eq(&self, other: &Self) -> bool {
 +        match (self, other) {
-             (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
-             (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
++            (Self::Str(ls), Self::Str(rs)) => ls == rs,
++            (Self::Binary(l), Self::Binary(r)) => l == r,
 +            (&Self::Char(l), &Self::Char(r)) => l == r,
 +            (&Self::Int(l), &Self::Int(r)) => l == r,
 +            (&Self::F64(l), &Self::F64(r)) => {
 +                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
 +                // `Fw32 == Fw64`, so don’t compare them.
 +                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
 +                l.to_bits() == r.to_bits()
 +            },
 +            (&Self::F32(l), &Self::F32(r)) => {
 +                // We want `Fw32 == FwAny` and `FwAny == Fw64`, and by transitivity we must have
 +                // `Fw32 == Fw64`, so don’t compare them.
 +                // `to_bits` is required to catch non-matching 0.0, -0.0, and NaNs.
 +                f64::from(l).to_bits() == f64::from(r).to_bits()
 +            },
 +            (&Self::Bool(l), &Self::Bool(r)) => l == r,
 +            (&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
-             (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
-             (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
++            (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => ls == rs && lv == rv,
++            (Self::Ref(lb), Self::Ref(rb)) => *lb == *rb,
 +            // TODO: are there inter-type equalities?
 +            _ => false,
 +        }
 +    }
 +}
 +
 +impl Hash for Constant {
 +    fn hash<H>(&self, state: &mut H)
 +    where
 +        H: Hasher,
 +    {
 +        std::mem::discriminant(self).hash(state);
 +        match *self {
 +            Self::Str(ref s) => {
 +                s.hash(state);
 +            },
 +            Self::Binary(ref b) => {
 +                b.hash(state);
 +            },
 +            Self::Char(c) => {
 +                c.hash(state);
 +            },
 +            Self::Int(i) => {
 +                i.hash(state);
 +            },
 +            Self::F32(f) => {
 +                f64::from(f).to_bits().hash(state);
 +            },
 +            Self::F64(f) => {
 +                f.to_bits().hash(state);
 +            },
 +            Self::Bool(b) => {
 +                b.hash(state);
 +            },
 +            Self::Vec(ref v) | Self::Tuple(ref v) => {
 +                v.hash(state);
 +            },
 +            Self::Repeat(ref c, l) => {
 +                c.hash(state);
 +                l.hash(state);
 +            },
 +            Self::RawPtr(u) => {
 +                u.hash(state);
 +            },
 +            Self::Ref(ref r) => {
 +                r.hash(state);
 +            },
 +            Self::Err => {},
 +        }
 +    }
 +}
 +
 +impl Constant {
 +    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
 +        match (left, right) {
-             (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
-             (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() {
++            (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)),
++            (Self::Char(l), Self::Char(r)) => Some(l.cmp(r)),
 +            (&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() {
 +                ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))),
 +                ty::Uint(_) => Some(l.cmp(&r)),
 +                _ => bug!("Not an int type"),
 +            },
 +            (&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
 +            (&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
-             (&Self::Vec(ref l), &Self::Vec(ref r)) => {
-                 let cmp_type = match *cmp_type.kind() {
-                     ty::Array(ty, _) | ty::Slice(ty) => ty,
-                     _ => return None,
++            (Self::Bool(l), Self::Bool(r)) => Some(l.cmp(r)),
++            (Self::Tuple(l), Self::Tuple(r)) if l.len() == r.len() => match *cmp_type.kind() {
 +                ty::Tuple(tys) if tys.len() == l.len() => l
 +                    .iter()
 +                    .zip(r)
 +                    .zip(tys)
 +                    .map(|((li, ri), cmp_type)| Self::partial_cmp(tcx, cmp_type, li, ri))
 +                    .find(|r| r.map_or(true, |o| o != Ordering::Equal))
 +                    .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
 +                _ => None,
 +            },
-             (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
++            (Self::Vec(l), Self::Vec(r)) => {
++                let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else {
++                    return None
 +                };
 +                iter::zip(l, r)
 +                    .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
 +                    .find(|r| r.map_or(true, |o| o != Ordering::Equal))
 +                    .unwrap_or_else(|| Some(l.len().cmp(&r.len())))
 +            },
-             (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(
++            (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => {
 +                match Self::partial_cmp(
 +                    tcx,
 +                    match *cmp_type.kind() {
 +                        ty::Array(ty, _) => ty,
 +                        _ => return None,
 +                    },
 +                    lv,
 +                    rv,
 +                ) {
 +                    Some(Equal) => Some(ls.cmp(rs)),
 +                    x => x,
 +                }
 +            },
-                 let ity = match *ty.kind() {
-                     ty::Int(ity) => ity,
-                     _ => return None,
-                 };
++            (Self::Ref(lb), Self::Ref(rb)) => Self::partial_cmp(
 +                tcx,
 +                match *cmp_type.kind() {
 +                    ty::Ref(_, ty, _) => ty,
 +                    _ => return None,
 +                },
 +                lb,
 +                rb,
 +            ),
 +            // TODO: are there any useful inter-type orderings?
 +            _ => None,
 +        }
 +    }
 +
 +    /// Returns the integer value or `None` if `self` or `val_type` is not integer type.
 +    pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
 +        if let Constant::Int(const_int) = *self {
 +            match *val_type.kind() {
 +                ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
 +                ty::Uint(_) => Some(FullInt::U(const_int)),
 +                _ => None,
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn peel_refs(mut self) -> Self {
 +        while let Constant::Ref(r) = self {
 +            self = *r;
 +        }
 +        self
 +    }
 +}
 +
 +/// Parses a `LitKind` to a `Constant`.
 +pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
 +    match *lit {
 +        LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
 +        LitKind::Byte(b) => Constant::Int(u128::from(b)),
 +        LitKind::ByteStr(ref s) => Constant::Binary(Lrc::clone(s)),
 +        LitKind::Char(c) => Constant::Char(c),
 +        LitKind::Int(n, _) => Constant::Int(n),
 +        LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
 +            ast::FloatTy::F32 => Constant::F32(is.as_str().parse().unwrap()),
 +            ast::FloatTy::F64 => Constant::F64(is.as_str().parse().unwrap()),
 +        },
 +        LitKind::Float(ref is, LitFloatType::Unsuffixed) => match ty.expect("type of float is known").kind() {
 +            ty::Float(FloatTy::F32) => Constant::F32(is.as_str().parse().unwrap()),
 +            ty::Float(FloatTy::F64) => Constant::F64(is.as_str().parse().unwrap()),
 +            _ => bug!(),
 +        },
 +        LitKind::Bool(b) => Constant::Bool(b),
 +        LitKind::Err => Constant::Err,
 +    }
 +}
 +
 +pub fn constant<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<(Constant, bool)> {
 +    let mut cx = ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    };
 +    cx.expr(e).map(|cst| (cst, cx.needed_resolution))
 +}
 +
 +pub fn constant_simple<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<Constant> {
 +    constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) })
 +}
 +
 +pub fn constant_full_int<'tcx>(
 +    lcx: &LateContext<'tcx>,
 +    typeck_results: &ty::TypeckResults<'tcx>,
 +    e: &Expr<'_>,
 +) -> Option<FullInt> {
 +    constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
 +}
 +
 +#[derive(Copy, Clone, Debug, Eq)]
 +pub enum FullInt {
 +    S(i128),
 +    U(u128),
 +}
 +
 +impl PartialEq for FullInt {
 +    #[must_use]
 +    fn eq(&self, other: &Self) -> bool {
 +        self.cmp(other) == Ordering::Equal
 +    }
 +}
 +
 +impl PartialOrd for FullInt {
 +    #[must_use]
 +    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 +        Some(self.cmp(other))
 +    }
 +}
 +
 +impl Ord for FullInt {
 +    #[must_use]
 +    fn cmp(&self, other: &Self) -> Ordering {
 +        use FullInt::{S, U};
 +
 +        fn cmp_s_u(s: i128, u: u128) -> Ordering {
 +            u128::try_from(s).map_or(Ordering::Less, |x| x.cmp(&u))
 +        }
 +
 +        match (*self, *other) {
 +            (S(s), S(o)) => s.cmp(&o),
 +            (U(s), U(o)) => s.cmp(&o),
 +            (S(s), U(o)) => cmp_s_u(s, o),
 +            (U(s), S(o)) => cmp_s_u(o, s).reverse(),
 +        }
 +    }
 +}
 +
 +/// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`.
 +pub fn constant_context<'a, 'tcx>(
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +) -> ConstEvalLateContext<'a, 'tcx> {
 +    ConstEvalLateContext {
 +        lcx,
 +        typeck_results,
 +        param_env: lcx.param_env,
 +        needed_resolution: false,
 +        substs: lcx.tcx.intern_substs(&[]),
 +    }
 +}
 +
 +pub struct ConstEvalLateContext<'a, 'tcx> {
 +    lcx: &'a LateContext<'tcx>,
 +    typeck_results: &'a ty::TypeckResults<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    needed_resolution: bool,
 +    substs: SubstsRef<'tcx>,
 +}
 +
 +impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 +    /// Simple constant folding: Insert an expression, get a constant or none.
 +    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
 +        match e.kind {
 +            ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
 +            ExprKind::Block(block, _) => self.block(block),
 +            ExprKind::Lit(ref lit) => {
 +                if is_direct_expn_of(e.span, "cfg").is_some() {
 +                    None
 +                } else {
 +                    Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
 +                }
 +            },
 +            ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
 +            ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
 +            ExprKind::Repeat(value, _) => {
 +                let n = match self.typeck_results.expr_ty(e).kind() {
 +                    ty::Array(_, n) => n.try_eval_usize(self.lcx.tcx, self.lcx.param_env)?,
 +                    _ => span_bug!(e.span, "typeck error"),
 +                };
 +                self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
 +            },
 +            ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
 +                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
 +                UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
 +            }),
 +            ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
 +            ExprKind::Binary(op, left, right) => self.binop(op, left, right),
 +            ExprKind::Call(callee, args) => {
 +                // We only handle a few const functions for now.
 +                if_chain! {
 +                    if args.is_empty();
 +                    if let ExprKind::Path(qpath) = &callee.kind;
 +                    let res = self.typeck_results.qpath_res(qpath, callee.hir_id);
 +                    if let Some(def_id) = res.opt_def_id();
 +                    let def_path = self.lcx.get_def_path(def_id);
 +                    let def_path: Vec<&str> = def_path.iter().take(4).map(Symbol::as_str).collect();
 +                    if let ["core", "num", int_impl, "max_value"] = *def_path;
 +                    then {
 +                        let value = match int_impl {
 +                            "<impl i8>" => i8::MAX as u128,
 +                            "<impl i16>" => i16::MAX as u128,
 +                            "<impl i32>" => i32::MAX as u128,
 +                            "<impl i64>" => i64::MAX as u128,
 +                            "<impl i128>" => i128::MAX as u128,
 +                            _ => return None,
 +                        };
 +                        Some(Constant::Int(value))
 +                    } else {
 +                        None
 +                    }
 +                }
 +            },
 +            ExprKind::Index(arr, index) => self.index(arr, index),
 +            ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
 +            // TODO: add other expressions.
 +            _ => None,
 +        }
 +    }
 +
 +    #[expect(clippy::cast_possible_wrap)]
 +    fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Bool, Int};
 +        match *o {
 +            Bool(b) => Some(Bool(!b)),
 +            Int(value) => {
 +                let value = !value;
 +                match *ty.kind() {
 +                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
 +                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
 +                    _ => None,
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
 +        use self::Constant::{Int, F32, F64};
 +        match *o {
 +            Int(value) => {
++                let ty::Int(ity) = *ty.kind() else { return None };
 +                // sign extend
 +                let value = sext(self.lcx.tcx, value, ity);
 +                let value = value.checked_neg()?;
 +                // clear unused bits
 +                Some(Int(unsext(self.lcx.tcx, value, ity)))
 +            },
 +            F32(f) => Some(F32(-f)),
 +            F64(f) => Some(F64(-f)),
 +            _ => None,
 +        }
 +    }
 +
 +    /// Create `Some(Vec![..])` of all constants, unless there is any
 +    /// non-constant part.
 +    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
 +        vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
 +    }
 +
 +    /// Lookup a possibly constant expression from an `ExprKind::Path`.
 +    fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
 +        let res = self.typeck_results.qpath_res(qpath, id);
 +        match res {
 +            Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
 +                // Check if this constant is based on `cfg!(..)`,
 +                // which is NOT constant for our purposes.
 +                if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
 +                let Node::Item(&Item {
 +                    kind: ItemKind::Const(_, body_id),
 +                    ..
 +                }) = node &&
 +                let Node::Expr(&Expr {
 +                    kind: ExprKind::Lit(_),
 +                    span,
 +                    ..
 +                }) = self.lcx.tcx.hir().get(body_id.hir_id) &&
 +                is_direct_expn_of(span, "cfg").is_some() {
 +                    return None;
 +                }
 +
 +                let substs = self.typeck_results.node_substs(id);
 +                let substs = if self.substs.is_empty() {
 +                    substs
 +                } else {
 +                    EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
 +                };
 +
 +                let result = self
 +                    .lcx
 +                    .tcx
 +                    .const_eval_resolve(
 +                        self.param_env,
 +                        mir::UnevaluatedConst::new(ty::WithOptConstParam::unknown(def_id), substs),
 +                        None,
 +                    )
 +                    .ok()
 +                    .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
 +                let result = miri_to_const(self.lcx.tcx, result);
 +                if result.is_some() {
 +                    self.needed_resolution = true;
 +                }
 +                result
 +            },
 +            // FIXME: cover all usable cases.
 +            _ => None,
 +        }
 +    }
 +
 +    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
 +        let lhs = self.expr(lhs);
 +        let index = self.expr(index);
 +
 +        match (lhs, index) {
 +            (Some(Constant::Vec(vec)), Some(Constant::Int(index))) => match vec.get(index as usize) {
 +                Some(Constant::F32(x)) => Some(Constant::F32(*x)),
 +                Some(Constant::F64(x)) => Some(Constant::F64(*x)),
 +                _ => None,
 +            },
 +            (Some(Constant::Vec(vec)), _) => {
 +                if !vec.is_empty() && vec.iter().all(|x| *x == vec[0]) {
 +                    match vec.get(0) {
 +                        Some(Constant::F32(x)) => Some(Constant::F32(*x)),
 +                        Some(Constant::F64(x)) => Some(Constant::F64(*x)),
 +                        _ => None,
 +                    }
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        }
 +    }
 +
 +    /// A block can only yield a constant if it only has one constant expression.
 +    fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
 +        if block.stmts.is_empty() {
 +            block.expr.as_ref().and_then(|b| self.expr(b))
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
 +        if let Some(Constant::Bool(b)) = self.expr(cond) {
 +            if b {
 +                self.expr(then)
 +            } else {
 +                otherwise.as_ref().and_then(|expr| self.expr(expr))
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
 +        let l = self.expr(left)?;
 +        let r = self.expr(right);
 +        match (l, r) {
 +            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
 +                ty::Int(ity) => {
 +                    let l = sext(self.lcx.tcx, l, ity);
 +                    let r = sext(self.lcx.tcx, r, ity);
 +                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
 +                    match op.node {
 +                        BinOpKind::Add => l.checked_add(r).map(zext),
 +                        BinOpKind::Sub => l.checked_sub(r).map(zext),
 +                        BinOpKind::Mul => l.checked_mul(r).map(zext),
 +                        BinOpKind::Div if r != 0 => l.checked_div(r).map(zext),
 +                        BinOpKind::Rem if r != 0 => l.checked_rem(r).map(zext),
 +                        BinOpKind::Shr => l.checked_shr(r.try_into().ok()?).map(zext),
 +                        BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).map(zext),
 +                        BinOpKind::BitXor => Some(zext(l ^ r)),
 +                        BinOpKind::BitOr => Some(zext(l | r)),
 +                        BinOpKind::BitAnd => Some(zext(l & r)),
 +                        BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                        BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                        BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                        BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                        BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                        BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                        _ => None,
 +                    }
 +                },
 +                ty::Uint(_) => match op.node {
 +                    BinOpKind::Add => l.checked_add(r).map(Constant::Int),
 +                    BinOpKind::Sub => l.checked_sub(r).map(Constant::Int),
 +                    BinOpKind::Mul => l.checked_mul(r).map(Constant::Int),
 +                    BinOpKind::Div => l.checked_div(r).map(Constant::Int),
 +                    BinOpKind::Rem => l.checked_rem(r).map(Constant::Int),
 +                    BinOpKind::Shr => l.checked_shr(r.try_into().ok()?).map(Constant::Int),
 +                    BinOpKind::Shl => l.checked_shl(r.try_into().ok()?).map(Constant::Int),
 +                    BinOpKind::BitXor => Some(Constant::Int(l ^ r)),
 +                    BinOpKind::BitOr => Some(Constant::Int(l | r)),
 +                    BinOpKind::BitAnd => Some(Constant::Int(l & r)),
 +                    BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                    BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                    BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                    BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                    BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                    BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                    _ => None,
 +                },
 +                _ => None,
 +            },
 +            (Constant::F32(l), Some(Constant::F32(r))) => match op.node {
 +                BinOpKind::Add => Some(Constant::F32(l + r)),
 +                BinOpKind::Sub => Some(Constant::F32(l - r)),
 +                BinOpKind::Mul => Some(Constant::F32(l * r)),
 +                BinOpKind::Div => Some(Constant::F32(l / r)),
 +                BinOpKind::Rem => Some(Constant::F32(l % r)),
 +                BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                _ => None,
 +            },
 +            (Constant::F64(l), Some(Constant::F64(r))) => match op.node {
 +                BinOpKind::Add => Some(Constant::F64(l + r)),
 +                BinOpKind::Sub => Some(Constant::F64(l - r)),
 +                BinOpKind::Mul => Some(Constant::F64(l * r)),
 +                BinOpKind::Div => Some(Constant::F64(l / r)),
 +                BinOpKind::Rem => Some(Constant::F64(l % r)),
 +                BinOpKind::Eq => Some(Constant::Bool(l == r)),
 +                BinOpKind::Ne => Some(Constant::Bool(l != r)),
 +                BinOpKind::Lt => Some(Constant::Bool(l < r)),
 +                BinOpKind::Le => Some(Constant::Bool(l <= r)),
 +                BinOpKind::Ge => Some(Constant::Bool(l >= r)),
 +                BinOpKind::Gt => Some(Constant::Bool(l > r)),
 +                _ => None,
 +            },
 +            (l, r) => match (op.node, l, r) {
 +                (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
 +                (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
 +                (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
 +                    Some(r)
 +                },
 +                (BinOpKind::BitXor, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l ^ r)),
 +                (BinOpKind::BitAnd, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l & r)),
 +                (BinOpKind::BitOr, Constant::Bool(l), Some(Constant::Bool(r))) => Some(Constant::Bool(l | r)),
 +                _ => None,
 +            },
 +        }
 +    }
 +}
 +
 +pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant> {
 +    use rustc_middle::mir::interpret::ConstValue;
 +    match result {
 +        mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => {
 +            match result.ty().kind() {
 +                ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
 +                ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
 +                ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
 +                    int.try_into().expect("invalid f32 bit representation"),
 +                ))),
 +                ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
 +                    int.try_into().expect("invalid f64 bit representation"),
 +                ))),
 +                ty::RawPtr(type_and_mut) => {
 +                    if let ty::Uint(_) = type_and_mut.ty.kind() {
 +                        return Some(Constant::RawPtr(int.assert_bits(int.size())));
 +                    }
 +                    None
 +                },
 +                // FIXME: implement other conversions.
 +                _ => None,
 +            }
 +        },
 +        mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
 +            ty::Ref(_, tam, _) => match tam.kind() {
 +                ty::Str => String::from_utf8(
 +                    data.inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(start..end)
 +                        .to_owned(),
 +                )
 +                .ok()
 +                .map(Constant::Str),
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
 +            ty::Array(sub_type, len) => match sub_type.kind() {
 +                ty::Float(FloatTy::F32) => match len.kind().try_to_machine_usize(tcx) {
 +                    Some(len) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
 +                        .to_owned()
 +                        .array_chunks::<4>()
 +                        .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                ty::Float(FloatTy::F64) => match len.kind().try_to_machine_usize(tcx) {
 +                    Some(len) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
 +                        .to_owned()
 +                        .array_chunks::<8>()
 +                        .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                // FIXME: implement other array type conversions.
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        // FIXME: implement other conversions.
 +        _ => None,
 +    }
 +}
index 78f93755b72d7b5e864651d9ed9aab3caff732d0,0000000000000000000000000000000000000000..16b160b6fd27ef688b888d8e9fbd9cee5d2da35b
mode 100644,000000..100644
--- /dev/null
@@@ -1,236 -1,0 +1,236 @@@
- pub fn span_lint_and_help<'a, T: LintContext>(
-     cx: &'a T,
 +//! Clippy wrappers around rustc's diagnostic functions.
 +//!
 +//! These functions are used by the `INTERNAL_METADATA_COLLECTOR` lint to collect the corresponding
 +//! lint applicability. Please make sure that you update the `LINT_EMISSION_FUNCTIONS` variable in
 +//! `clippy_lints::utils::internal_lints::metadata_collector` when a new function is added
 +//! or renamed.
 +//!
 +//! Thank you!
 +//! ~The `INTERNAL_METADATA_COLLECTOR` lint
 +
 +use rustc_errors::{Applicability, Diagnostic, MultiSpan};
 +use rustc_hir::HirId;
 +use rustc_lint::{LateContext, Lint, LintContext};
 +use rustc_span::source_map::Span;
 +use std::env;
 +
 +fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) {
 +    if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
 +        if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
 +            diag.help(&format!(
 +                "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}",
 +                &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
 +                    // extract just major + minor version and ignore patch versions
 +                    format!("rust-{}", n.rsplit_once('.').unwrap().1)
 +                })
 +            ));
 +        }
 +    }
 +}
 +
 +/// Emit a basic lint message with a `msg` and a `span`.
 +///
 +/// This is the most primitive of our lint emission methods and can
 +/// be a good way to get a new lint started.
 +///
 +/// Usually it's nicer to provide more context for lint messages.
 +/// Be sure the output is understandable when you use this method.
 +///
 +/// # Example
 +///
 +/// ```ignore
 +/// error: usage of mem::forget on Drop type
 +///   --> $DIR/mem_forget.rs:17:5
 +///    |
 +/// 17 |     std::mem::forget(seven);
 +///    |     ^^^^^^^^^^^^^^^^^^^^^^^
 +/// ```
 +pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
 +    cx.struct_span_lint(lint, sp, msg, |diag| {
 +        docs_link(diag, lint);
 +        diag
 +    });
 +}
 +
 +/// Same as `span_lint` but with an extra `help` message.
 +///
 +/// Use this if you want to provide some general help but
 +/// can't provide a specific machine applicable suggestion.
 +///
 +/// The `help` message can be optionally attached to a `Span`.
 +///
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +///
 +/// # Example
 +///
 +/// ```text
 +/// error: constant division of 0.0 with 0.0 will always result in NaN
 +///   --> $DIR/zero_div_zero.rs:6:25
 +///    |
 +/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
 +///    |                         ^^^^^^^^^^^^
 +///    |
 +///    = help: consider using `f64::NAN` if you would like a constant representing NaN
 +/// ```
- pub fn span_lint_and_note<'a, T: LintContext>(
-     cx: &'a T,
++pub fn span_lint_and_help<T: LintContext>(
++    cx: &T,
 +    lint: &'static Lint,
 +    span: impl Into<MultiSpan>,
 +    msg: &str,
 +    help_span: Option<Span>,
 +    help: &str,
 +) {
 +    cx.struct_span_lint(lint, span, msg, |diag| {
 +        if let Some(help_span) = help_span {
 +            diag.span_help(help_span, help);
 +        } else {
 +            diag.help(help);
 +        }
 +        docs_link(diag, lint);
 +        diag
 +    });
 +}
 +
 +/// Like `span_lint` but with a `note` section instead of a `help` message.
 +///
 +/// The `note` message is presented separately from the main lint message
 +/// and is attached to a specific span:
 +///
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +///
 +/// # Example
 +///
 +/// ```text
 +/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
 +///   --> $DIR/drop_forget_ref.rs:10:5
 +///    |
 +/// 10 |     forget(&SomeStruct);
 +///    |     ^^^^^^^^^^^^^^^^^^^
 +///    |
 +///    = note: `-D clippy::forget-ref` implied by `-D warnings`
 +/// note: argument has type &SomeStruct
 +///   --> $DIR/drop_forget_ref.rs:10:12
 +///    |
 +/// 10 |     forget(&SomeStruct);
 +///    |            ^^^^^^^^^^^
 +/// ```
- pub fn span_lint_and_sugg<'a, T: LintContext>(
-     cx: &'a T,
++pub fn span_lint_and_note<T: LintContext>(
++    cx: &T,
 +    lint: &'static Lint,
 +    span: impl Into<MultiSpan>,
 +    msg: &str,
 +    note_span: Option<Span>,
 +    note: &str,
 +) {
 +    cx.struct_span_lint(lint, span, msg, |diag| {
 +        if let Some(note_span) = note_span {
 +            diag.span_note(note_span, note);
 +        } else {
 +            diag.note(note);
 +        }
 +        docs_link(diag, lint);
 +        diag
 +    });
 +}
 +
 +/// Like `span_lint` but allows to add notes, help and suggestions using a closure.
 +///
 +/// If you need to customize your lint output a lot, use this function.
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +pub fn span_lint_and_then<C, S, F>(cx: &C, lint: &'static Lint, sp: S, msg: &str, f: F)
 +where
 +    C: LintContext,
 +    S: Into<MultiSpan>,
 +    F: FnOnce(&mut Diagnostic),
 +{
 +    cx.struct_span_lint(lint, sp, msg, |diag| {
 +        f(diag);
 +        docs_link(diag, lint);
 +        diag
 +    });
 +}
 +
 +pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
 +    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| {
 +        docs_link(diag, lint);
 +        diag
 +    });
 +}
 +
 +pub fn span_lint_hir_and_then(
 +    cx: &LateContext<'_>,
 +    lint: &'static Lint,
 +    hir_id: HirId,
 +    sp: impl Into<MultiSpan>,
 +    msg: &str,
 +    f: impl FnOnce(&mut Diagnostic),
 +) {
 +    cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| {
 +        f(diag);
 +        docs_link(diag, lint);
 +        diag
 +    });
 +}
 +
 +/// Add a span lint with a suggestion on how to fix it.
 +///
 +/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
 +/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
 +/// 2)"`.
 +///
 +/// If you change the signature, remember to update the internal lint `CollapsibleCalls`
 +///
 +/// # Example
 +///
 +/// ```text
 +/// error: This `.fold` can be more succinctly expressed as `.any`
 +/// --> $DIR/methods.rs:390:13
 +///     |
 +/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
 +///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
 +///     |
 +///     = note: `-D fold-any` implied by `-D warnings`
 +/// ```
 +#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))]
++pub fn span_lint_and_sugg<T: LintContext>(
++    cx: &T,
 +    lint: &'static Lint,
 +    sp: Span,
 +    msg: &str,
 +    help: &str,
 +    sugg: String,
 +    applicability: Applicability,
 +) {
 +    span_lint_and_then(cx, lint, sp, msg, |diag| {
 +        diag.span_suggestion(sp, help, sugg, applicability);
 +    });
 +}
 +
 +/// Create a suggestion made from several `span → replacement`.
 +///
 +/// Note: in the JSON format (used by `compiletest_rs`), the help message will
 +/// appear once per
 +/// replacement. In human-readable format though, it only appears once before
 +/// the whole suggestion.
 +pub fn multispan_sugg<I>(diag: &mut Diagnostic, help_msg: &str, sugg: I)
 +where
 +    I: IntoIterator<Item = (Span, String)>,
 +{
 +    multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg);
 +}
 +
 +/// Create a suggestion made from several `span → replacement`.
 +///
 +/// rustfix currently doesn't support the automatic application of suggestions with
 +/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141).
 +/// Suggestions with multiple spans will be silently ignored.
 +pub fn multispan_sugg_with_applicability<I>(
 +    diag: &mut Diagnostic,
 +    help_msg: &str,
 +    applicability: Applicability,
 +    sugg: I,
 +) where
 +    I: IntoIterator<Item = (Span, String)>,
 +{
 +    diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
 +}
index cf24ec8b67b9004dc7af3056b5d2cb36026dcd9e,0000000000000000000000000000000000000000..0231a51adf48286103381c147be10b9d9c457502
mode 100644,000000..100644
--- /dev/null
@@@ -1,1040 -1,0 +1,1043 @@@
-     PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
 +use crate::consts::constant_simple;
 +use crate::macros::macro_backtrace;
 +use crate::source::snippet_opt;
 +use rustc_ast::ast::InlineAsmTemplatePiece;
 +use rustc_data_structures::fx::FxHasher;
 +use rustc_hir::def::Res;
 +use rustc_hir::HirIdMap;
 +use rustc_hir::{
 +    ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
 +    GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path,
-                 let (left, right) = match (
++    PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::{sym, Symbol};
 +use std::hash::{Hash, Hasher};
 +
 +/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
 +/// other conditions would make them equal.
 +type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
 +
 +/// Type used to check whether two ast are the same. This is different from the
 +/// operator `==` on ast types as this operator would compare true equality with
 +/// ID and span.
 +///
 +/// Note that some expressions kinds are not considered but could be added.
 +pub struct SpanlessEq<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
 +    allow_side_effects: bool,
 +    expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
 +}
 +
 +impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
 +            allow_side_effects: true,
 +            expr_fallback: None,
 +        }
 +    }
 +
 +    /// Consider expressions containing potential side effects as not equal.
 +    #[must_use]
 +    pub fn deny_side_effects(self) -> Self {
 +        Self {
 +            allow_side_effects: false,
 +            ..self
 +        }
 +    }
 +
 +    #[must_use]
 +    pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
 +        Self {
 +            expr_fallback: Some(Box::new(expr_fallback)),
 +            ..self
 +        }
 +    }
 +
 +    /// Use this method to wrap comparisons that may involve inter-expression context.
 +    /// See `self.locals`.
 +    pub fn inter_expr(&mut self) -> HirEqInterExpr<'_, 'a, 'tcx> {
 +        HirEqInterExpr {
 +            inner: self,
 +            locals: HirIdMap::default(),
 +        }
 +    }
 +
 +    pub fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        self.inter_expr().eq_block(left, right)
 +    }
 +
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        self.inter_expr().eq_expr(left, right)
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        self.inter_expr().eq_path(left, right)
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        self.inter_expr().eq_path_segment(left, right)
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        self.inter_expr().eq_path_segments(left, right)
 +    }
 +}
 +
 +pub struct HirEqInterExpr<'a, 'b, 'tcx> {
 +    inner: &'a mut SpanlessEq<'b, 'tcx>,
 +
 +    // When binding are declared, the binding ID in the left expression is mapped to the one on the
 +    // right. For example, when comparing `{ let x = 1; x + 2 }` and `{ let y = 1; y + 2 }`,
 +    // these blocks are considered equal since `x` is mapped to `y`.
 +    pub locals: HirIdMap<HirId>,
 +}
 +
 +impl HirEqInterExpr<'_, '_, '_> {
 +    pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&StmtKind::Local(l), &StmtKind::Local(r)) => {
 +                // This additional check ensures that the type of the locals are equivalent even if the init
 +                // expression or type have some inferred parts.
 +                if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +                    let l_ty = typeck_lhs.pat_ty(l.pat);
 +                    let r_ty = typeck_rhs.pat_ty(r.pat);
 +                    if l_ty != r_ty {
 +                        return false;
 +                    }
 +                }
 +
 +                // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
 +                // these only get added if the init and type is equal.
 +                both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
 +                    && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
 +                    && both(&l.els, &r.els, |l, r| self.eq_block(l, r))
 +                    && self.eq_pat(l.pat, r.pat)
 +            },
 +            (&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
 +            _ => false,
 +        }
 +    }
 +
 +    /// Checks whether two blocks are the same.
 +    fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
 +        match (left.stmts, left.expr, right.stmts, right.expr) {
 +            ([], None, [], None) => {
 +                // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
 +                // expanded to nothing, or the cfg attribute was used.
-                 ) {
-                     (Some(left), Some(right)) => (left, right),
-                     _ => return true,
-                 };
++                let (Some(left), Some(right)) = (
 +                    snippet_opt(self.inner.cx, left.span),
 +                    snippet_opt(self.inner.cx, right.span),
-             (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
++                ) else { return true };
 +                let mut left_pos = 0;
 +                let left = tokenize(&left)
 +                    .map(|t| {
 +                        let end = left_pos + t.len as usize;
 +                        let s = &left[left_pos..end];
 +                        left_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                let mut right_pos = 0;
 +                let right = tokenize(&right)
 +                    .map(|t| {
 +                        let end = right_pos + t.len as usize;
 +                        let s = &right[right_pos..end];
 +                        right_pos = end;
 +                        (t, s)
 +                    })
 +                    .filter(|(t, _)| {
 +                        !matches!(
 +                            t.kind,
 +                            TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                        )
 +                    })
 +                    .map(|(_, s)| s);
 +                left.eq(right)
 +            },
 +            _ => {
 +                over(left.stmts, right.stmts, |l, r| self.eq_stmt(l, r))
 +                    && both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
 +            },
 +        }
 +    }
 +
 +    fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
 +        macro_backtrace(expr.span).last().map_or(false, |macro_call| {
 +            matches!(
 +                &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
 +                Some(sym::todo_macro | sym::unimplemented_macro)
 +            )
 +        })
 +    }
 +
 +    pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
 +        match (left, right) {
 +            (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
 +            (ArrayLen::Body(l_ct), ArrayLen::Body(r_ct)) => self.eq_body(l_ct.body, r_ct.body),
 +            (_, _) => false,
 +        }
 +    }
 +
 +    pub fn eq_body(&mut self, left: BodyId, right: BodyId) -> bool {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.inner.maybe_typeck_results.replace((
 +            self.inner.cx.tcx.typeck_body(left),
 +            self.inner.cx.tcx.typeck_body(right),
 +        ));
 +        let res = self.eq_expr(
 +            self.inner.cx.tcx.hir().body(left).value,
 +            self.inner.cx.tcx.hir().body(right).value,
 +        );
 +        self.inner.maybe_typeck_results = old_maybe_typeck_results;
 +        res
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +        if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
 +            return false;
 +        }
 +
 +        if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
 +            if let (Some(l), Some(r)) = (
 +                constant_simple(self.inner.cx, typeck_lhs, left),
 +                constant_simple(self.inner.cx, typeck_rhs, right),
 +            ) {
 +                if l == r {
 +                    return true;
 +                }
 +            }
 +        }
 +
 +        let is_eq = match (
 +            reduce_exprkind(self.inner.cx, &left.kind),
 +            reduce_exprkind(self.inner.cx, &right.kind),
 +        ) {
 +            (&ExprKind::AddrOf(lb, l_mut, le), &ExprKind::AddrOf(rb, r_mut, re)) => {
 +                lb == rb && l_mut == r_mut && self.eq_expr(le, re)
 +            },
 +            (&ExprKind::Continue(li), &ExprKind::Continue(ri)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Assign(ll, lr, _), &ExprKind::Assign(rl, rr, _)) => {
 +                self.inner.allow_side_effects && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::AssignOp(ref lo, ll, lr), &ExprKind::AssignOp(ref ro, rl, rr)) => {
 +                self.inner.allow_side_effects && lo.node == ro.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +            },
 +            (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
 +            (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
 +                l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
 +                        l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
 +                    })
 +            },
 +            (&ExprKind::Break(li, ref le), &ExprKind::Break(ri, ref re)) => {
 +                both(&li.label, &ri.label, |l, r| l.ident.name == r.ident.name)
 +                    && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Box(l), &ExprKind::Box(r)) => self.eq_expr(l, r),
 +            (&ExprKind::Call(l_fun, l_args), &ExprKind::Call(r_fun, r_args)) => {
 +                self.inner.allow_side_effects && self.eq_expr(l_fun, r_fun) && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Cast(lx, lt), &ExprKind::Cast(rx, rt)) | (&ExprKind::Type(lx, lt), &ExprKind::Type(rx, rt)) => {
 +                self.eq_expr(lx, rx) && self.eq_ty(lt, rt)
 +            },
 +            (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => {
 +                l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp)
 +            },
 +            (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri),
 +            (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
 +                self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r))
 +            },
 +            (&ExprKind::Let(l), &ExprKind::Let(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
-             (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
-             (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
++            (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node,
 +            (&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
 +                lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
 +            },
 +            (&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
 +                ls == rs
 +                    && self.eq_expr(le, re)
 +                    && over(la, ra, |l, r| {
 +                        self.eq_pat(l.pat, r.pat)
 +                            && both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
 +                            && self.eq_expr(l.body, r.body)
 +                    })
 +            },
 +            (
 +                &ExprKind::MethodCall(l_path, l_receiver, l_args, _),
 +                &ExprKind::MethodCall(r_path, r_receiver, r_args, _),
 +            ) => {
 +                self.inner.allow_side_effects
 +                    && self.eq_path_segment(l_path, r_path)
 +                    && self.eq_expr(l_receiver, r_receiver)
 +                    && self.eq_exprs(l_args, r_args)
 +            },
 +            (&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
 +                self.eq_expr(le, re) && self.eq_array_length(ll, rl)
 +            },
-             (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
++            (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)),
++            (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r),
 +            (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
 +                self.eq_qpath(l_path, r_path)
 +                    && both(lo, ro, |l, r| self.eq_expr(l, r))
 +                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
 +            },
 +            (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
 +            (&ExprKind::Unary(l_op, le), &ExprKind::Unary(r_op, re)) => l_op == r_op && self.eq_expr(le, re),
 +            (&ExprKind::Array(l), &ExprKind::Array(r)) => self.eq_exprs(l, r),
 +            (&ExprKind::DropTemps(le), &ExprKind::DropTemps(re)) => self.eq_expr(le, re),
 +            _ => false,
 +        };
 +        (is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
 +            || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
 +    }
 +
 +    fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
 +        over(left, right, |l, r| self.eq_expr(l, r))
 +    }
 +
 +    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
 +    }
 +
 +    fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
 +        match (left, right) {
 +            (Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
 +            (Guard::IfLet(l), Guard::IfLet(r)) => {
 +                self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
 +            },
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
 +        match (left, right) {
 +            (GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
 +            (GenericArg::Lifetime(l_lt), GenericArg::Lifetime(r_lt)) => Self::eq_lifetime(l_lt, r_lt),
 +            (GenericArg::Type(l_ty), GenericArg::Type(r_ty)) => self.eq_ty(l_ty, r_ty),
 +            (GenericArg::Infer(l_inf), GenericArg::Infer(r_inf)) => self.eq_ty(&l_inf.to_ty(), &r_inf.to_ty()),
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
 +        left.name == right.name
 +    }
 +
 +    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
 +        let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
 +        li.name == ri.name && self.eq_pat(lp, rp)
 +    }
 +
 +    /// Checks whether two patterns are the same.
 +    fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&PatKind::Box(l), &PatKind::Box(r)) => self.eq_pat(l, r),
 +            (&PatKind::Struct(ref lp, la, ..), &PatKind::Struct(ref rp, ra, ..)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
 +            },
 +            (&PatKind::TupleStruct(ref lp, la, ls), &PatKind::TupleStruct(ref rp, ra, rs)) => {
 +                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
 +            },
 +            (&PatKind::Binding(lb, li, _, ref lp), &PatKind::Binding(rb, ri, _, ref rp)) => {
 +                let eq = lb == rb && both(lp, rp, |l, r| self.eq_pat(l, r));
 +                if eq {
 +                    self.locals.insert(li, ri);
 +                }
 +                eq
 +            },
-             (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
-                 l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty)
-             },
-             (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
++            (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r),
 +            (&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
 +            (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
 +            (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
 +                both(ls, rs, |a, b| self.eq_expr(a, b)) && both(le, re, |a, b| self.eq_expr(a, b)) && (li == ri)
 +            },
 +            (&PatKind::Ref(le, ref lm), &PatKind::Ref(re, ref rm)) => lm == rm && self.eq_pat(le, re),
 +            (&PatKind::Slice(ls, ref li, le), &PatKind::Slice(rs, ref ri, re)) => {
 +                over(ls, rs, |l, r| self.eq_pat(l, r))
 +                    && over(le, re, |l, r| self.eq_pat(l, r))
 +                    && both(li, ri, |l, r| self.eq_pat(l, r))
 +            },
 +            (&PatKind::Wild, &PatKind::Wild) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    #[expect(clippy::similar_names)]
 +    fn eq_qpath(&mut self, left: &QPath<'_>, right: &QPath<'_>) -> bool {
 +        match (left, right) {
 +            (&QPath::Resolved(ref lty, lpath), &QPath::Resolved(ref rty, rpath)) => {
 +                both(lty, rty, |l, r| self.eq_ty(l, r)) && self.eq_path(lpath, rpath)
 +            },
 +            (&QPath::TypeRelative(lty, lseg), &QPath::TypeRelative(rty, rseg)) => {
 +                self.eq_ty(lty, rty) && self.eq_path_segment(lseg, rseg)
 +            },
 +            (&QPath::LangItem(llang_item, ..), &QPath::LangItem(rlang_item, ..)) => llang_item == rlang_item,
 +            _ => false,
 +        }
 +    }
 +
 +    pub fn eq_path(&mut self, left: &Path<'_>, right: &Path<'_>) -> bool {
 +        match (left.res, right.res) {
 +            (Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
 +            (Res::Local(_), _) | (_, Res::Local(_)) => false,
 +            _ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
 +        }
 +    }
 +
 +    fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
 +        if !(left.parenthesized || right.parenthesized) {
 +            over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
 +                && over(left.bindings, right.bindings, |l, r| self.eq_type_binding(l, r))
 +        } else if left.parenthesized && right.parenthesized {
 +            over(left.inputs(), right.inputs(), |l, r| self.eq_ty(l, r))
 +                && both(&Some(&left.bindings[0].ty()), &Some(&right.bindings[0].ty()), |l, r| {
 +                    self.eq_ty(l, r)
 +                })
 +        } else {
 +            false
 +        }
 +    }
 +
 +    pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
 +        left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
 +    }
 +
 +    pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
 +        // The == of idents doesn't work with different contexts,
 +        // we have to be explicit about hygiene
 +        left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
 +    }
 +
 +    pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
 +        match (&left.kind, &right.kind) {
 +            (&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
 +            (&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
-             (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
++            (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty),
++            (TyKind::Rptr(_, l_rmut), TyKind::Rptr(_, r_rmut)) => {
 +                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
 +            },
++            (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
 +            (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
 +            (&TyKind::Infer, &TyKind::Infer) => true,
 +            _ => false,
 +        }
 +    }
 +
 +    fn eq_type_binding(&mut self, left: &TypeBinding<'_>, right: &TypeBinding<'_>) -> bool {
 +        left.ident.name == right.ident.name && self.eq_ty(left.ty(), right.ty())
 +    }
 +}
 +
 +/// Some simple reductions like `{ return }` => `return`
 +fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
 +    if let ExprKind::Block(block, _) = kind {
 +        match (block.stmts, block.expr) {
 +            // From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
 +            // block with an empty span.
 +            ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
 +            // `{}` => `()`
 +            ([], None) => match snippet_opt(cx, block.span) {
 +                // Don't reduce if there are any tokens contained in the braces
 +                Some(snip)
 +                    if tokenize(&snip)
 +                        .map(|t| t.kind)
 +                        .filter(|t| {
 +                            !matches!(
 +                                t,
 +                                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
 +                            )
 +                        })
 +                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
 +                {
 +                    kind
 +                },
 +                _ => &ExprKind::Tup(&[]),
 +            },
 +            ([], Some(expr)) => match expr.kind {
 +                // `{ return .. }` => `return ..`
 +                ExprKind::Ret(..) => &expr.kind,
 +                _ => kind,
 +            },
 +            ([stmt], None) => match stmt.kind {
 +                StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
 +                    // `{ return ..; }` => `return ..`
 +                    ExprKind::Ret(..) => &expr.kind,
 +                    _ => kind,
 +                },
 +                _ => kind,
 +            },
 +            _ => kind,
 +        }
 +    } else {
 +        kind
 +    }
 +}
 +
 +fn swap_binop<'a>(
 +    binop: BinOpKind,
 +    lhs: &'a Expr<'a>,
 +    rhs: &'a Expr<'a>,
 +) -> Option<(BinOpKind, &'a Expr<'a>, &'a Expr<'a>)> {
 +    match binop {
 +        BinOpKind::Add | BinOpKind::Eq | BinOpKind::Ne | BinOpKind::BitAnd | BinOpKind::BitXor | BinOpKind::BitOr => {
 +            Some((binop, rhs, lhs))
 +        },
 +        BinOpKind::Lt => Some((BinOpKind::Gt, rhs, lhs)),
 +        BinOpKind::Le => Some((BinOpKind::Ge, rhs, lhs)),
 +        BinOpKind::Ge => Some((BinOpKind::Le, rhs, lhs)),
 +        BinOpKind::Gt => Some((BinOpKind::Lt, rhs, lhs)),
 +        BinOpKind::Mul // Not always commutative, e.g. with matrices. See issue #5698
 +        | BinOpKind::Shl
 +        | BinOpKind::Shr
 +        | BinOpKind::Rem
 +        | BinOpKind::Sub
 +        | BinOpKind::Div
 +        | BinOpKind::And
 +        | BinOpKind::Or => None,
 +    }
 +}
 +
 +/// Checks if the two `Option`s are both `None` or some equal values as per
 +/// `eq_fn`.
 +pub fn both<X>(l: &Option<X>, r: &Option<X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    l.as_ref()
 +        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
 +}
 +
 +/// Checks if two slices are equal as per `eq_fn`.
 +pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
 +    left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 +}
 +
 +/// Counts how many elements of the slices are equal as per `eq_fn`.
 +pub fn count_eq<X: Sized>(
 +    left: &mut dyn Iterator<Item = X>,
 +    right: &mut dyn Iterator<Item = X>,
 +    mut eq_fn: impl FnMut(&X, &X) -> bool,
 +) -> usize {
 +    left.zip(right).take_while(|(l, r)| eq_fn(l, r)).count()
 +}
 +
 +/// Checks if two expressions evaluate to the same value, and don't contain any side effects.
 +pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
 +}
 +
 +/// Type used to hash an ast element. This is different from the `Hash` trait
 +/// on ast types as this
 +/// trait would consider IDs and spans.
 +///
 +/// All expressions kind are hashed, but some might have a weaker hash.
 +pub struct SpanlessHash<'a, 'tcx> {
 +    /// Context used to evaluate constant expressions.
 +    cx: &'a LateContext<'tcx>,
 +    maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
 +    s: FxHasher,
 +}
 +
 +impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 +    pub fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            maybe_typeck_results: cx.maybe_typeck_results(),
 +            s: FxHasher::default(),
 +        }
 +    }
 +
 +    pub fn finish(self) -> u64 {
 +        self.s.finish()
 +    }
 +
 +    pub fn hash_block(&mut self, b: &Block<'_>) {
 +        for s in b.stmts {
 +            self.hash_stmt(s);
 +        }
 +
 +        if let Some(e) = b.expr {
 +            self.hash_expr(e);
 +        }
 +
 +        std::mem::discriminant(&b.rules).hash(&mut self.s);
 +    }
 +
 +    #[expect(clippy::too_many_lines)]
 +    pub fn hash_expr(&mut self, e: &Expr<'_>) {
 +        let simple_const = self
 +            .maybe_typeck_results
 +            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
 +
 +        // const hashing may result in the same hash as some unrelated node, so add a sort of
 +        // discriminant depending on which path we're choosing next
 +        simple_const.hash(&mut self.s);
 +        if simple_const.is_some() {
 +            return;
 +        }
 +
 +        std::mem::discriminant(&e.kind).hash(&mut self.s);
 +
 +        match e.kind {
 +            ExprKind::AddrOf(kind, m, e) => {
 +                std::mem::discriminant(&kind).hash(&mut self.s);
 +                m.hash(&mut self.s);
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Continue(i) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::Assign(l, r, _) => {
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::AssignOp(ref o, l, r) => {
 +                std::mem::discriminant(&o.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Block(b, _) => {
 +                self.hash_block(b);
 +            },
 +            ExprKind::Binary(op, l, r) => {
 +                std::mem::discriminant(&op.node).hash(&mut self.s);
 +                self.hash_expr(l);
 +                self.hash_expr(r);
 +            },
 +            ExprKind::Break(i, ref j) => {
 +                if let Some(i) = i.label {
 +                    self.hash_name(i.ident.name);
 +                }
 +                if let Some(j) = *j {
 +                    self.hash_expr(j);
 +                }
 +            },
 +            ExprKind::Box(e) | ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
 +                self.hash_expr(e);
 +            },
 +            ExprKind::Call(fun, args) => {
 +                self.hash_expr(fun);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::Cast(e, ty) | ExprKind::Type(e, ty) => {
 +                self.hash_expr(e);
 +                self.hash_ty(ty);
 +            },
 +            ExprKind::Closure(&Closure {
 +                capture_clause, body, ..
 +            }) => {
 +                std::mem::discriminant(&capture_clause).hash(&mut self.s);
 +                // closures inherit TypeckResults
 +                self.hash_expr(self.cx.tcx.hir().body(body).value);
 +            },
 +            ExprKind::Field(e, ref f) => {
 +                self.hash_expr(e);
 +                self.hash_name(f.name);
 +            },
 +            ExprKind::Index(a, i) => {
 +                self.hash_expr(a);
 +                self.hash_expr(i);
 +            },
 +            ExprKind::InlineAsm(asm) => {
 +                for piece in asm.template {
 +                    match piece {
 +                        InlineAsmTemplatePiece::String(s) => s.hash(&mut self.s),
 +                        InlineAsmTemplatePiece::Placeholder {
 +                            operand_idx,
 +                            modifier,
 +                            span: _,
 +                        } => {
 +                            operand_idx.hash(&mut self.s);
 +                            modifier.hash(&mut self.s);
 +                        },
 +                    }
 +                }
 +                asm.options.hash(&mut self.s);
 +                for (op, _op_sp) in asm.operands {
 +                    match op {
 +                        InlineAsmOperand::In { reg, expr } => {
 +                            reg.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::Out { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            if let Some(expr) = expr {
 +                                self.hash_expr(expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::InOut { reg, late, expr } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(expr);
 +                        },
 +                        InlineAsmOperand::SplitInOut {
 +                            reg,
 +                            late,
 +                            in_expr,
 +                            out_expr,
 +                        } => {
 +                            reg.hash(&mut self.s);
 +                            late.hash(&mut self.s);
 +                            self.hash_expr(in_expr);
 +                            if let Some(out_expr) = out_expr {
 +                                self.hash_expr(out_expr);
 +                            }
 +                        },
 +                        InlineAsmOperand::Const { anon_const } | InlineAsmOperand::SymFn { anon_const } => {
 +                            self.hash_body(anon_const.body);
 +                        },
 +                        InlineAsmOperand::SymStatic { path, def_id: _ } => self.hash_qpath(path),
 +                    }
 +                }
 +            },
 +            ExprKind::Let(Let { pat, init, ty, .. }) => {
 +                self.hash_expr(init);
 +                if let Some(ty) = ty {
 +                    self.hash_ty(ty);
 +                }
 +                self.hash_pat(pat);
 +            },
 +            ExprKind::Err => {},
 +            ExprKind::Lit(ref l) => {
 +                l.node.hash(&mut self.s);
 +            },
 +            ExprKind::Loop(b, ref i, ..) => {
 +                self.hash_block(b);
 +                if let Some(i) = *i {
 +                    self.hash_name(i.ident.name);
 +                }
 +            },
 +            ExprKind::If(cond, then, ref else_opt) => {
 +                self.hash_expr(cond);
 +                self.hash_expr(then);
 +                if let Some(e) = *else_opt {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Match(e, arms, ref s) => {
 +                self.hash_expr(e);
 +
 +                for arm in arms {
 +                    self.hash_pat(arm.pat);
 +                    if let Some(ref e) = arm.guard {
 +                        self.hash_guard(e);
 +                    }
 +                    self.hash_expr(arm.body);
 +                }
 +
 +                s.hash(&mut self.s);
 +            },
 +            ExprKind::MethodCall(path, receiver, args, ref _fn_span) => {
 +                self.hash_name(path.ident.name);
 +                self.hash_expr(receiver);
 +                self.hash_exprs(args);
 +            },
 +            ExprKind::ConstBlock(ref l_id) => {
 +                self.hash_body(l_id.body);
 +            },
 +            ExprKind::Repeat(e, len) => {
 +                self.hash_expr(e);
 +                self.hash_array_length(len);
 +            },
 +            ExprKind::Ret(ref e) => {
 +                if let Some(e) = *e {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Path(ref qpath) => {
 +                self.hash_qpath(qpath);
 +            },
 +            ExprKind::Struct(path, fields, ref expr) => {
 +                self.hash_qpath(path);
 +
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_expr(f.expr);
 +                }
 +
 +                if let Some(e) = *expr {
 +                    self.hash_expr(e);
 +                }
 +            },
 +            ExprKind::Tup(tup) => {
 +                self.hash_exprs(tup);
 +            },
 +            ExprKind::Array(v) => {
 +                self.hash_exprs(v);
 +            },
 +            ExprKind::Unary(lop, le) => {
 +                std::mem::discriminant(&lop).hash(&mut self.s);
 +                self.hash_expr(le);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_exprs(&mut self, e: &[Expr<'_>]) {
 +        for e in e {
 +            self.hash_expr(e);
 +        }
 +    }
 +
 +    pub fn hash_name(&mut self, n: Symbol) {
 +        n.hash(&mut self.s);
 +    }
 +
 +    pub fn hash_qpath(&mut self, p: &QPath<'_>) {
 +        match *p {
 +            QPath::Resolved(_, path) => {
 +                self.hash_path(path);
 +            },
 +            QPath::TypeRelative(_, path) => {
 +                self.hash_name(path.ident.name);
 +            },
 +            QPath::LangItem(lang_item, ..) => {
 +                std::mem::discriminant(&lang_item).hash(&mut self.s);
 +            },
 +        }
 +        // self.maybe_typeck_results.unwrap().qpath_res(p, id).hash(&mut self.s);
 +    }
 +
 +    pub fn hash_pat(&mut self, pat: &Pat<'_>) {
 +        std::mem::discriminant(&pat.kind).hash(&mut self.s);
 +        match pat.kind {
 +            PatKind::Binding(BindingAnnotation(by_ref, mutability), _, _, pat) => {
 +                std::mem::discriminant(&by_ref).hash(&mut self.s);
 +                std::mem::discriminant(&mutability).hash(&mut self.s);
 +                if let Some(pat) = pat {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Box(pat) => self.hash_pat(pat),
 +            PatKind::Lit(expr) => self.hash_expr(expr),
 +            PatKind::Or(pats) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            PatKind::Range(s, e, i) => {
 +                if let Some(s) = s {
 +                    self.hash_expr(s);
 +                }
 +                if let Some(e) = e {
 +                    self.hash_expr(e);
 +                }
 +                std::mem::discriminant(&i).hash(&mut self.s);
 +            },
 +            PatKind::Ref(pat, mu) => {
 +                self.hash_pat(pat);
 +                std::mem::discriminant(&mu).hash(&mut self.s);
 +            },
 +            PatKind::Slice(l, m, r) => {
 +                for pat in l {
 +                    self.hash_pat(pat);
 +                }
 +                if let Some(pat) = m {
 +                    self.hash_pat(pat);
 +                }
 +                for pat in r {
 +                    self.hash_pat(pat);
 +                }
 +            },
 +            PatKind::Struct(ref qpath, fields, e) => {
 +                self.hash_qpath(qpath);
 +                for f in fields {
 +                    self.hash_name(f.ident.name);
 +                    self.hash_pat(f.pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Tuple(pats, e) => {
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::TupleStruct(ref qpath, pats, e) => {
 +                self.hash_qpath(qpath);
 +                for pat in pats {
 +                    self.hash_pat(pat);
 +                }
 +                e.hash(&mut self.s);
 +            },
 +            PatKind::Wild => {},
 +        }
 +    }
 +
 +    pub fn hash_path(&mut self, path: &Path<'_>) {
 +        match path.res {
 +            // constant hash since equality is dependant on inter-expression context
 +            // e.g. The expressions `if let Some(x) = foo() {}` and `if let Some(y) = foo() {}` are considered equal
 +            // even though the binding names are different and they have different `HirId`s.
 +            Res::Local(_) => 1_usize.hash(&mut self.s),
 +            _ => {
 +                for seg in path.segments {
 +                    self.hash_name(seg.ident.name);
 +                    self.hash_generic_args(seg.args().args);
 +                }
 +            },
 +        }
 +    }
 +
 +    pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
 +        std::mem::discriminant(&b.kind).hash(&mut self.s);
 +
 +        match &b.kind {
 +            StmtKind::Local(local) => {
 +                self.hash_pat(local.pat);
 +                if let Some(init) = local.init {
 +                    self.hash_expr(init);
 +                }
 +                if let Some(els) = local.els {
 +                    self.hash_block(els);
 +                }
 +            },
 +            StmtKind::Item(..) => {},
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_guard(&mut self, g: &Guard<'_>) {
 +        match g {
 +            Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => {
 +                self.hash_expr(expr);
 +            },
 +        }
 +    }
 +
 +    pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
 +        std::mem::discriminant(&lifetime.name).hash(&mut self.s);
 +        if let LifetimeName::Param(param_id, ref name) = lifetime.name {
 +            std::mem::discriminant(name).hash(&mut self.s);
 +            param_id.hash(&mut self.s);
 +            match name {
 +                ParamName::Plain(ref ident) => {
 +                    ident.name.hash(&mut self.s);
 +                },
 +                ParamName::Fresh | ParamName::Error => {},
 +            }
 +        }
 +    }
 +
 +    pub fn hash_ty(&mut self, ty: &Ty<'_>) {
 +        std::mem::discriminant(&ty.kind).hash(&mut self.s);
 +        self.hash_tykind(&ty.kind);
 +    }
 +
 +    pub fn hash_tykind(&mut self, ty: &TyKind<'_>) {
 +        match ty {
 +            TyKind::Slice(ty) => {
 +                self.hash_ty(ty);
 +            },
 +            &TyKind::Array(ty, len) => {
 +                self.hash_ty(ty);
 +                self.hash_array_length(len);
 +            },
 +            TyKind::Ptr(ref mut_ty) => {
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::Rptr(lifetime, ref mut_ty) => {
 +                self.hash_lifetime(lifetime);
 +                self.hash_ty(mut_ty.ty);
 +                mut_ty.mutbl.hash(&mut self.s);
 +            },
 +            TyKind::BareFn(bfn) => {
 +                bfn.unsafety.hash(&mut self.s);
 +                bfn.abi.hash(&mut self.s);
 +                for arg in bfn.decl.inputs {
 +                    self.hash_ty(arg);
 +                }
 +                std::mem::discriminant(&bfn.decl.output).hash(&mut self.s);
 +                match bfn.decl.output {
 +                    FnRetTy::DefaultReturn(_) => {},
 +                    FnRetTy::Return(ty) => {
 +                        self.hash_ty(ty);
 +                    },
 +                }
 +                bfn.decl.c_variadic.hash(&mut self.s);
 +            },
 +            TyKind::Tup(ty_list) => {
 +                for ty in *ty_list {
 +                    self.hash_ty(ty);
 +                }
 +            },
 +            TyKind::Path(ref qpath) => self.hash_qpath(qpath),
 +            TyKind::OpaqueDef(_, arg_list, in_trait) => {
 +                self.hash_generic_args(arg_list);
 +                in_trait.hash(&mut self.s);
 +            },
 +            TyKind::TraitObject(_, lifetime, _) => {
 +                self.hash_lifetime(lifetime);
 +            },
 +            TyKind::Typeof(anon_const) => {
 +                self.hash_body(anon_const.body);
 +            },
 +            TyKind::Err | TyKind::Infer | TyKind::Never => {},
 +        }
 +    }
 +
 +    pub fn hash_array_length(&mut self, length: ArrayLen) {
 +        match length {
 +            ArrayLen::Infer(..) => {},
 +            ArrayLen::Body(anon_const) => self.hash_body(anon_const.body),
 +        }
 +    }
 +
 +    pub fn hash_body(&mut self, body_id: BodyId) {
 +        // swap out TypeckResults when hashing a body
 +        let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
 +        self.hash_expr(self.cx.tcx.hir().body(body_id).value);
 +        self.maybe_typeck_results = old_maybe_typeck_results;
 +    }
 +
 +    fn hash_generic_args(&mut self, arg_list: &[GenericArg<'_>]) {
 +        for arg in arg_list {
 +            match *arg {
 +                GenericArg::Lifetime(l) => self.hash_lifetime(l),
 +                GenericArg::Type(ty) => self.hash_ty(ty),
 +                GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
 +                GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
 +            }
 +        }
 +    }
 +}
 +
 +pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
 +    let mut h = SpanlessHash::new(cx);
 +    h.hash_stmt(s);
 +    h.finish()
 +}
 +
++pub fn is_bool(ty: &Ty<'_>) -> bool {
++    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
++        matches!(path.res, Res::PrimTy(PrimTy::Bool))
++    } else {
++        false
++    }
++}
++
 +pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
 +    let mut h = SpanlessHash::new(cx);
 +    h.hash_expr(e);
 +    h.finish()
 +}
index 3f93b9b491d4cb71e28ffedd0b470baea01a7b88,0000000000000000000000000000000000000000..9e2682925a22116c71acdb6847f1572207f84d74
mode 100644,000000..100644
--- /dev/null
@@@ -1,2478 -1,0 +1,2515 @@@
-     both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
 +#![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::{
- use rustc_hir as hir;
- use rustc_hir::def::{DefKind, Namespace, Res};
- use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
++    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;
-     def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness,
-     Destination, Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind,
-     LangItem, Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy,
-     QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
++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::{
- use rustc_span::source_map::original_sp;
++    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};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
- use rustc_span::symbol::{kw, Symbol};
- use rustc_span::{Span, DUMMY_SP};
 +use rustc_span::source_map::SourceMap;
 +use rustc_span::sym;
- pub fn is_path_lang_item<'tcx>(
-     cx: &LateContext<'_>,
-     maybe_path: &impl MaybePath<'tcx>,
-     lang_item: LangItem,
- ) -> bool {
++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;
 +
 +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
 +    if let Ok(version) = RustcVersion::parse(msrv) {
 +        return Some(version);
 +    } else if let Some(sess) = sess {
 +        if let Some(span) = span {
 +            sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
 +        }
 +    }
 +    None
 +}
 +
 +pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
 +    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
 +}
 +
 +#[macro_export]
 +macro_rules! extract_msrv_attr {
 +    ($context:ident) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
 +            let sess = rustc_lint::LintContext::sess(cx);
 +            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
 +                    } else {
 +                        sess.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// 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.
- fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
-     let single = |ty| tcx.incoherent_impls(ty).iter().copied();
-     let empty = || [].iter().copied();
-     match name {
-         "bool" => single(BoolSimplifiedType),
-         "char" => single(CharSimplifiedType),
-         "str" => single(StrSimplifiedType),
-         "array" => single(ArraySimplifiedType),
-         "slice" => single(SliceSimplifiedType),
++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()
 +}
 +
-         "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
-         "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
-         "isize" => single(IntSimplifiedType(IntTy::Isize)),
-         "i8" => single(IntSimplifiedType(IntTy::I8)),
-         "i16" => single(IntSimplifiedType(IntTy::I16)),
-         "i32" => single(IntSimplifiedType(IntTy::I32)),
-         "i64" => single(IntSimplifiedType(IntTy::I64)),
-         "i128" => single(IntSimplifiedType(IntTy::I128)),
-         "usize" => single(UintSimplifiedType(UintTy::Usize)),
-         "u8" => single(UintSimplifiedType(UintTy::U8)),
-         "u16" => single(UintSimplifiedType(UintTy::U16)),
-         "u32" => single(UintSimplifiedType(UintTy::U32)),
-         "u64" => single(UintSimplifiedType(UintTy::U64)),
-         "u128" => single(UintSimplifiedType(UintTy::U128)),
-         "f32" => single(FloatSimplifiedType(FloatTy::F32)),
-         "f64" => single(FloatSimplifiedType(FloatTy::F64)),
-         _ => empty(),
-     }
- }
- /// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate
- /// between `std::vec` the module and `std::vec` the macro
- ///
- /// This function is expensive and should be used sparingly.
- pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option<Namespace>) -> Res {
-     fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option<Res> {
-         match tcx.def_kind(def_id) {
-             DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
-                 .module_children(def_id)
-                 .iter()
-                 .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local()))
-                 .map(|child| child.res.expect_non_local()),
-             DefKind::Impl => tcx
-                 .associated_item_def_ids(def_id)
-                 .iter()
-                 .copied()
-                 .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
-                 .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
-             DefKind::Struct | DefKind::Union => tcx
-                 .adt_def(def_id)
-                 .non_enum_variant()
-                 .fields
-                 .iter()
-                 .find(|f| f.name.as_str() == name)
-                 .map(|f| Res::Def(DefKind::Field, f.did)),
-             _ => None,
++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.
-     fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
++        "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)
 +    }
++}
 +
-             .find(|&num| tcx.crate_name(num).as_str() == 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()
-     let (base, path) = match *path {
++            .filter(move |&num| tcx.crate_name(num) == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
-             return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
++    let tcx = cx.tcx;
++
++    let (base, mut path) = match *path {
 +        [primitive] => {
-         _ => return Res::Err,
++            return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
 +        },
 +        [base, ref path @ ..] => (base, path),
-     let tcx = cx.tcx;
-     let starts = find_primitive(tcx, base)
-         .chain(find_crate(tcx, base))
++        _ => return Vec::new(),
 +    };
-     for first in starts {
-         let last = path
-             .iter()
-             .copied()
-             .enumerate()
-             // for each segment, find the child item
-             .try_fold(first, |res, (idx, segment)| {
-                 let matches_ns = |res: Res| {
-                     // If at the last segment in the path, respect the namespace hint
-                     if idx == path.len() - 1 {
-                         match namespace_hint {
-                             Some(ns) => res.matches_ns(ns),
-                             None => true,
-                         }
-                     } else {
-                         res.matches_ns(Namespace::TypeNS)
-                     }
-                 };
-                 let def_id = res.def_id();
-                 if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) {
-                     Some(item)
-                 } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
-                     // it is not a child item so check inherent impl items
-                     tcx.inherent_impls(def_id)
-                         .iter()
-                         .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns))
-                 } else {
-                     None
-                 }
-             });
++
++    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));
 +
-         if let Some(last) = last {
-             return last;
-         }
++    let mut resolutions: Vec<Res> = starts.collect();
 +
-     Res::Err
++    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();
 +    }
 +
-     match def_path_res(cx, path, Some(Namespace::TypeNS)) {
++    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,
-                     return std_types_symbols
-                         .iter()
-                         .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string());
++    })
 +}
 +
 +/// 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() {
- pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
++                    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).
- /// 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)
- }
++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()
 +}
 +
- pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +/// 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.
-     matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness.is_async())
++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 8b843732a236b1eb326ac672e0bfa1d2873169db,0000000000000000000000000000000000000000..79b19e6fb3eb051eff7a865ac44ea944e4d9cd88
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,42 @@@
-     1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
 +use rustc_semver::RustcVersion;
 +
 +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,47,0 { TAU }
++    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 }
 +}
index bc851473430454d8b1dc0f4bdf7dece213606e4c,0000000000000000000000000000000000000000..6c09c146082ab41e673ea06b142afb334fea833c
mode 100644,000000..100644
--- /dev/null
@@@ -1,157 -1,0 +1,160 @@@
 +//! 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"];
 +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"];
 +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 +pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 +pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 +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"];
 +#[cfg(feature = "internal")]
 +pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 +pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
 +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 45b63a4aa5df8bd79c27b32755b32e30ebfaf909,0000000000000000000000000000000000000000..65722f142aa69c4c8b5055e325b32b60ff3adb17
mode 100644,000000..100644
--- /dev/null
@@@ -1,397 -1,0 +1,397 @@@
- pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a 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 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>)>;
 +
- fn check_terminator<'a, 'tcx>(
++pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: Option<RustcVersion>) -> 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::RegionOutlives(_)
 +                | ty::PredicateKind::TypeOutlives(_)
 +                | ty::PredicateKind::WellFormed(_)
 +                | ty::PredicateKind::Projection(_)
 +                | ty::PredicateKind::ConstEvaluatable(..)
 +                | ty::PredicateKind::ConstEquate(..)
 +                | ty::PredicateKind::Trait(..)
 +                | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
 +                ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"),
 +                ty::PredicateKind::Coerce(_) => panic!("coerce 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(())
 +}
 +
-     body: &'a Body<'tcx>,
++fn check_terminator<'tcx>(
 +    tcx: TyCtxt<'tcx>,
++    body: &Body<'tcx>,
 +    terminator: &Terminator<'tcx>,
 +    msrv: Option<RustcVersion>,
 +) -> 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())),
 +    }
 +}
 +
 +fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> 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);
 +
 +                crate::meets_msrv(
 +                    msrv,
 +                    RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
 +                        panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
 +                    }),
 +                )
 +            } else {
 +                // Unstable const fn with the feature enabled.
 +                msrv.is_none()
 +            }
 +        })
 +}
index d28bd92d708baae0ef57d351e8b3c7295503ec8c,0000000000000000000000000000000000000000..eacfa91ba556d41d454f2b3a63c5a325ae4311cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,500 -1,0 +1,516 @@@
- use crate::line_span;
 +//! Utils for extracting, inspecting or transforming source code
 +
 +#![allow(clippy::module_name_repetitions)]
 +
- use rustc_span::source_map::SourceMap;
- use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LintContext};
 +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> {
 +    if *applicability != Applicability::Unspecified && span.from_expansion() {
 +        *applicability = Applicability::MaybeIncorrect;
 +    }
 +    snippet_opt(cx, 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_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 +    cx.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`.
 +pub fn snippet_block_with_applicability<'a, T: LintContext>(
 +    cx: &T,
 +    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>(
 +    cx: &LateContext<'_>,
 +    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(cx, 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 eefba8cd29c41268c3abb3dcb252e7f0b426ae10,0000000000000000000000000000000000000000..3cacdb493772138594a03114722fc2b10c9c74ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,1110 -1,0 +1,1110 @@@
- pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
 +//! 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.
 +    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
 +        use rustc_ast::ast::RangeLimits;
 +
 +        let snippet_without_expansion = |cx, span: Span, default| {
 +            if span.from_expansion() {
 +                snippet_with_macro_callsite(cx, span, default)
 +            } else {
 +                snippet(cx, span, default)
 +            }
 +        };
 +
 +        match expr.kind {
 +            ast::ExprKind::AddrOf(..)
 +            | ast::ExprKind::Box(..)
 +            | ast::ExprKind::Closure { .. }
 +            | ast::ExprKind::If(..)
 +            | ast::ExprKind::Let(..)
 +            | ast::ExprKind::Unary(..)
 +            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
 +            ast::ExprKind::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(..)
 +            | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
 +            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)),
 +            ),
 +            ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
 +                AssocOp::DotDotEq,
 +                lhs.as_ref()
 +                    .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
 +                rhs.as_ref()
 +                    .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
 +            ),
 +            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),
 +            ),
 +            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),
 +            ),
 +            ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
 +                AssocOp::from_ast_binop(op.node),
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(cx, rhs.span, default),
 +            ),
 +            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),
 +            ),
 +            ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
 +                AssocOp::Colon,
 +                snippet_without_expansion(cx, lhs.span, default),
 +                snippet_without_expansion(cx, ty.span, default),
 +            ),
 +        }
 +    }
 +
 +    /// 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 3a144c2bb2239e17cf93b14a7ab36b4b125ef53a,0000000000000000000000000000000000000000..897edfc5495f45b87f9f04d6f042fa85c01303cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,878 -1,0 +1,1096 @@@
-     self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy,
-     Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
 +//! 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};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::{ConstValue, Scalar};
 +use rustc_middle::ty::{
- /// Attempts to read the given constant as though it were an an enum value.
++    self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, GenericParamDefKind, 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};
 +use rustc_target::abi::{Size, VariantIdx};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::AtExt;
 +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::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::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| get_associated_type(cx, ty, iter_did, "Item"))
 +}
 +
 +/// Returns the associated type `name` for `ty` as an implementation of `trait_id`.
 +/// Do not invoke without first verifying that the type implements the trait.
 +pub fn get_associated_type<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    name: &str,
 +) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .associated_items(trait_id)
 +        .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
 +        .and_then(|assoc| {
 +            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
 +            cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok()
 +        })
 +}
 +
 +/// 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 {
 +    implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params)
 +}
 +
 +/// 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: &[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 ty_params = tcx.mk_substs(ty_params.iter());
 +    let infcx = tcx.infer_ctxt().build();
 +    infcx
 +        .type_implements_trait(trait_id, ty, 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::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).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::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::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::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);
 +            },
 +            PredicateKind::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::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(),
++                "wrong number of substs for `{:?}`: found `{}` expected `{}`.\n\
++                    note: the expected parameters are: {:#?}\n\
++                    the given arguments are: `{:#?}`",
++                assoc_item.def_id,
++                substs.len(),
++                generic_count,
++                params.map(GenericParamDefKind::descr).collect::<Vec<_>>(),
++                substs,
++            );
++
++            if let Some((idx, (param, arg))) = params
++                .clone()
++                .zip(substs.iter().map(GenericArg::unpack))
++                .enumerate()
++                .find(|(_, (param, arg))| {
++                    !matches!(
++                        (param, arg),
++                        (GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
++                            | (GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
++                            | (GenericParamDefKind::Const { .. }, GenericArgKind::Const(_))
++                    )
++                })
++            {
++                debug_assert!(
++                    false,
++                    "mismatched subst type at index {}: expected a {}, found `{:?}`\n\
++                        note: the expected parameters are {:#?}\n\
++                        the given arguments are {:#?}",
++                    idx,
++                    param.descr(),
++                    arg,
++                    params.map(GenericParamDefKind::descr).collect::<Vec<_>>(),
++                    substs,
++                );
++            }
++        }
++
++        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 000fb51c018579f44e8091f97df9c7ecdc541c7b,0000000000000000000000000000000000000000..797722cfc1fcc5c0a60b74cf9178037c4a0ed686
mode 100644,000000..100644
--- /dev/null
@@@ -1,203 -1,0 +1,198 @@@
-     fn fake_read(
-         &mut self,
-         _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
-         _: FakeReadCause,
-         _: HirId,
-     ) {}
 +use crate as utils;
 +use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend};
 +use core::ops::ControlFlow;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::HirIdSet;
 +use rustc_hir::{Expr, ExprKind, HirId, Node};
 +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::FakeReadCause;
 +use rustc_middle::ty;
 +
 +/// Returns a set of mutated local variable IDs, or `None` if mutations could not be determined.
 +pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> Option<HirIdSet> {
 +    let mut delegate = MutVarsDelegate {
 +        used_mutably: HirIdSet::default(),
 +        skip: false,
 +    };
 +    let infcx = cx.tcx.infer_ctxt().build();
 +    ExprUseVisitor::new(
 +        &mut delegate,
 +        &infcx,
 +        expr.hir_id.owner.def_id,
 +        cx.param_env,
 +        cx.typeck_results(),
 +    )
 +    .walk_expr(expr);
 +
 +    if delegate.skip {
 +        return None;
 +    }
 +    Some(delegate.used_mutably)
 +}
 +
 +pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
 +    mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
 +}
 +
 +struct MutVarsDelegate {
 +    used_mutably: HirIdSet,
 +    skip: bool,
 +}
 +
 +impl<'tcx> MutVarsDelegate {
 +    fn update(&mut self, cat: &PlaceWithHirId<'tcx>) {
 +        match cat.place.base {
 +            PlaceBase::Local(id) => {
 +                self.used_mutably.insert(id);
 +            },
 +            PlaceBase::Upvar(_) => {
 +                //FIXME: This causes false negatives. We can't get the `NodeId` from
 +                //`Categorization::Upvar(_)`. So we search for any `Upvar`s in the
 +                //`while`-body, not just the ones in the condition.
 +                self.skip = true;
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
 +    fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
 +        if bk == ty::BorrowKind::MutBorrow {
 +            self.update(cmt);
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        self.update(cmt);
 +    }
 +
++    fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +pub struct ParamBindingIdCollector {
 +    pub binding_hir_ids: Vec<hir::HirId>,
 +}
 +impl<'tcx> ParamBindingIdCollector {
 +    fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
 +        let mut hir_ids: Vec<hir::HirId> = Vec::new();
 +        for param in body.params.iter() {
 +            let mut finder = ParamBindingIdCollector {
 +                binding_hir_ids: Vec::new(),
 +            };
 +            finder.visit_param(param);
 +            for hir_id in &finder.binding_hir_ids {
 +                hir_ids.push(*hir_id);
 +            }
 +        }
 +        hir_ids
 +    }
 +}
 +impl<'tcx> intravisit::Visitor<'tcx> for ParamBindingIdCollector {
 +    fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) {
 +        if let hir::PatKind::Binding(_, hir_id, ..) = pat.kind {
 +            self.binding_hir_ids.push(hir_id);
 +        }
 +        intravisit::walk_pat(self, pat);
 +    }
 +}
 +
 +pub struct BindingUsageFinder<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    binding_ids: Vec<hir::HirId>,
 +    usage_found: bool,
 +}
 +impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
 +    pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
 +        let mut finder = BindingUsageFinder {
 +            cx,
 +            binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
 +            usage_found: false,
 +        };
 +        finder.visit_body(body);
 +        finder.usage_found
 +    }
 +}
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
 +        if !self.usage_found {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
 +        if let hir::def::Res::Local(id) = path.res {
 +            if self.binding_ids.contains(&id) {
 +                self.usage_found = true;
 +            }
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
 +    for_each_expr(expression, |e| {
 +        match e.kind {
 +            ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()),
 +            // Something special could be done here to handle while or for loop
 +            // desugaring, as this will detect a break if there's a while loop
 +            // or a for loop inside the expression.
 +            _ if e.span.from_expansion() => ControlFlow::Break(()),
 +            _ => ControlFlow::Continue(()),
 +        }
 +    })
 +    .is_some()
 +}
 +
 +pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
 +    let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
 +
 +    // for _ in 1..3 {
 +    //    local
 +    // }
 +    //
 +    // let closure = || local;
 +    // closure();
 +    // closure();
 +    let in_loop_or_closure = cx
 +        .tcx
 +        .hir()
 +        .parent_iter(after.hir_id)
 +        .take_while(|&(id, _)| id != block.hir_id)
 +        .any(|(_, node)| {
 +            matches!(
 +                node,
 +                Node::Expr(Expr {
 +                    kind: ExprKind::Loop(..) | ExprKind::Closure { .. },
 +                    ..
 +                })
 +            )
 +        });
 +    if in_loop_or_closure {
 +        return true;
 +    }
 +
 +    let mut past_expr = false;
 +    for_each_expr_with_closures(cx, block, |e| {
 +        if e.hir_id == after.hir_id {
 +            past_expr = true;
 +            ControlFlow::Continue(Descend::No)
 +        } else if past_expr && utils::path_to_local_id(e, local_id) {
 +            ControlFlow::Break(())
 +        } else {
 +            ControlFlow::Continue(Descend::Yes)
 +        }
 +    })
 +    .is_some()
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..578109840fb70c44fd456aacd4105889c868bcc5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++[package]
++name = "declare_clippy_lint"
++version = "0.1.67"
++edition = "2021"
++publish = false
++
++[lib]
++proc-macro = true
++
++[dependencies]
++itertools = "0.10.1"
++quote = "1.0.21"
++syn = "1.0.100"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..962766916dd1f1fe8b9762b57fcb8cc5ed7bd4cd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,173 @@@
++#![feature(let_chains)]
++#![cfg_attr(feature = "deny-warnings", deny(warnings))]
++
++use proc_macro::TokenStream;
++use quote::{format_ident, quote};
++use syn::parse::{Parse, ParseStream};
++use syn::{parse_macro_input, Attribute, Error, Ident, Lit, LitStr, Meta, Result, Token};
++
++fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) -> Option<LitStr> {
++    if let Meta::NameValue(name_value) = attr.parse_meta().ok()? {
++        let path_idents = name_value.path.segments.iter().map(|segment| &segment.ident);
++
++        if itertools::equal(path_idents, path)
++            && let Lit::Str(lit) = name_value.lit
++        {
++            return Some(lit);
++        }
++    }
++
++    None
++}
++
++struct ClippyLint {
++    attrs: Vec<Attribute>,
++    explanation: String,
++    name: Ident,
++    category: Ident,
++    description: LitStr,
++}
++
++impl Parse for ClippyLint {
++    fn parse(input: ParseStream) -> Result<Self> {
++        let attrs = input.call(Attribute::parse_outer)?;
++
++        let mut in_code = false;
++        let mut explanation = String::new();
++        let mut version = None;
++        for attr in &attrs {
++            if let Some(lit) = parse_attr(["doc"], attr) {
++                let value = lit.value();
++                let line = value.strip_prefix(' ').unwrap_or(&value);
++
++                if line.starts_with("```") {
++                    explanation += "```\n";
++                    in_code = !in_code;
++                } else if !(in_code && line.starts_with("# ")) {
++                    explanation += line;
++                    explanation.push('\n');
++                }
++            } else if let Some(lit) = parse_attr(["clippy", "version"], attr) {
++                if let Some(duplicate) = version.replace(lit) {
++                    return Err(Error::new_spanned(duplicate, "duplicate clippy::version"));
++                }
++            } else {
++                return Err(Error::new_spanned(attr, "unexpected attribute"));
++            }
++        }
++
++        input.parse::<Token![pub]>()?;
++        let name = input.parse()?;
++        input.parse::<Token![,]>()?;
++
++        let category = input.parse()?;
++        input.parse::<Token![,]>()?;
++
++        let description = input.parse()?;
++
++        Ok(Self {
++            attrs,
++            explanation,
++            name,
++            category,
++            description,
++        })
++    }
++}
++
++/// Macro used to declare a Clippy lint.
++///
++/// Every lint declaration consists of 4 parts:
++///
++/// 1. The documentation, which is used for the website and `cargo clippy --explain`
++/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
++/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
++///    `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
++/// 4. The `description` that contains a short explanation on what's wrong with code where the
++///    lint is triggered.
++///
++/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
++/// enabled by default. As said in the README.md of this repository, if the lint level mapping
++/// changes, please update README.md.
++///
++/// # Example
++///
++/// ```
++/// use rustc_session::declare_tool_lint;
++///
++/// 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
++///     /// Insert a short example of code that triggers the lint
++///     /// ```
++///     ///
++///     /// Use instead:
++///     /// ```rust
++///     /// Insert a short example of improved code that doesn't trigger the lint
++///     /// ```
++///     #[clippy::version = "1.65.0"]
++///     pub LINT_NAME,
++///     pedantic,
++///     "description"
++/// }
++/// ```
++/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
++#[proc_macro]
++pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
++    let ClippyLint {
++        attrs,
++        explanation,
++        name,
++        category,
++        description,
++    } = parse_macro_input!(input as ClippyLint);
++
++    let mut category = category.to_string();
++
++    let level = format_ident!(
++        "{}",
++        match category.as_str() {
++            "correctness" => "Deny",
++            "style" | "suspicious" | "complexity" | "perf" | "internal_warn" => "Warn",
++            "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow",
++            _ => panic!("unknown category {category}"),
++        },
++    );
++
++    let info = if category == "internal_warn" {
++        None
++    } else {
++        let info_name = format_ident!("{name}_INFO");
++
++        (&mut category[0..1]).make_ascii_uppercase();
++        let category_variant = format_ident!("{category}");
++
++        Some(quote! {
++            pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo {
++                lint: &#name,
++                category: crate::LintCategory::#category_variant,
++                explanation: #explanation,
++            };
++        })
++    };
++
++    let output = quote! {
++        declare_tool_lint! {
++            #(#attrs)*
++            pub clippy::#name,
++            #level,
++            #description,
++            report_in_external_macro: true
++        }
++
++        #info
++    };
++
++    TokenStream::from(output)
++}
index 54c1b80c42dbf0fd320aa06677770d42db78ddf9,0000000000000000000000000000000000000000..ee8ab7c1d7cbbc60395caddeb15bc8d3dcf24d58
mode 100644,000000..100644
--- /dev/null
@@@ -1,874 -1,0 +1,829 @@@
- /// check if the latest modification of the logfile is older than the modification date of the
- /// clippy binary, if this is true, we should clean the lintchec shared target directory and recheck
- fn lintcheck_needs_rerun(lintcheck_logs_path: &Path, paths: [&Path; 2]) -> bool {
-     if !lintcheck_logs_path.exists() {
-         return true;
-     }
-     let clippy_modified: std::time::SystemTime = {
-         let [cargo, driver] = paths.map(|p| {
-             std::fs::metadata(p)
-                 .expect("failed to get metadata of file")
-                 .modified()
-                 .expect("failed to get modification date")
-         });
-         // the oldest modification of either of the binaries
-         std::cmp::max(cargo, driver)
-     };
-     let logs_modified: std::time::SystemTime = std::fs::metadata(lintcheck_logs_path)
-         .expect("failed to get metadata of file")
-         .modified()
-         .expect("failed to get modification date");
-     // time is represented in seconds since X
-     // logs_modified 2 and clippy_modified 5 means clippy binary is older and we need to recheck
-     logs_modified < clippy_modified
- }
 +// 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!(
 +                "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 {} {}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version
 +            );
 +        } else {
 +            println!(
 +                "{}/{} {}% Linting {} {} in target dir {:?}",
 +                index, total_crates_to_lint, perc, &self.name, &self.version, thread_index
 +            );
 +        }
 +
 +        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{:?}\ncargo_clippy_path: {}\ncrate path:{}\n",
 +                    error,
 +                    &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)
 +}
 +
-     // if the clippy bin is newer than our logs, throw away target dirs to force clippy to
-     // refresh the logs
-     if lintcheck_needs_rerun(
-         &config.lintcheck_results_path,
-         [&cargo_clippy_path, &clippy_driver_path],
-     ) {
-         let shared_target_dir = "target/lintcheck/shared_target_dir";
-         // if we get an Err here, the shared target dir probably does simply not exist
-         if let Ok(metadata) = std::fs::metadata(shared_target_dir) {
-             if metadata.is_dir() {
-                 println!("Clippy is newer than lint check logs, clearing lintcheck shared target dir...");
-                 std::fs::remove_dir_all(shared_target_dir)
-                     .expect("failed to remove target/lintcheck/shared_target_dir");
-             }
-         }
-     }
 +#[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();
 +
-         fs::remove_dir_all("target/lintcheck/shared_target_dir/recursive").unwrap_or_default();
 +    // 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 748d8a317160f415ec61252dd5f92e316dabaecc,0000000000000000000000000000000000000000..a806c1564796ae2a5bb3b5fd5b0fde0659e17d9e
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-10-20"
- components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 +[toolchain]
++channel = "nightly-2022-11-21"
++components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index f24d3507823ecaf9d8ae0e32e9124bdbb0ffb320,0000000000000000000000000000000000000000..ee2a3ad20d3e5b0d77b0819ddd44bd2271c376ec
mode 100644,000000..100644
--- /dev/null
@@@ -1,285 -1,0 +1,332 @@@
-             let conf = clippy_lints::read_conf(sess);
 +#![feature(rustc_private)]
++#![feature(let_chains)]
 +#![feature(once_cell)]
 +#![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
++    if cfg!(debug_assertions)
++        && 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);
 +            }
 +
-             return rustc_driver::RunCompiler::new(&orig_args, &mut DefaultCallbacks).run();
++            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();
++            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 {
-             rustc_driver::RunCompiler::new(&orig_args, &mut RustcCallbacks { clippy_args_var }).run()
 +            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 fce3cdfc462e07e613fba96ba2049c2955ad2dfb,0000000000000000000000000000000000000000..d418d2daa313e75d69fb6454fc12a8e59f7e37f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,207 -1,0 +1,205 @@@
- mod docs;
 +#![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)]
 +
 +use rustc_tools_util::VersionInfo;
 +use std::env;
 +use std::path::PathBuf;
 +use std::process::{self, Command};
 +
-             docs::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_"));
 +const CARGO_CLIPPY_HELP: &str = r#"Checks a package to catch common mistakes and improve your Rust code.
 +
 +Usage:
 +    cargo clippy [options] [--] [<opts>...]
 +
 +Common options:
 +    --no-deps                Run Clippy only on the given crate, without linting the dependencies
 +    --fix                    Automatically apply lint suggestions. This flag implies `--no-deps`
 +    -h, --help               Print this message
 +    -V, --version            Print version info and exit
 +    --explain LINT           Print the documentation for a given lint
 +
 +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)]
 +"#;
 +
 +fn show_help() {
 +    println!("{CARGO_CLIPPY_HELP}");
 +}
 +
 +fn show_version() {
 +    let version_info = rustc_tools_util::get_version_info!();
 +    println!("{version_info}");
 +}
 +
 +pub fn main() {
 +    // Check for version and help flags even when invoked as 'cargo-clippy'
 +    if env::args().any(|a| a == "--help" || a == "-h") {
 +        show_help();
 +        return;
 +    }
 +
 +    if env::args().any(|a| a == "--version" || a == "-V") {
 +        show_version();
 +        return;
 +    }
 +
 +    if let Some(pos) = env::args().position(|a| a == "--explain") {
 +        if let Some(mut lint) = env::args().nth(pos + 1) {
 +            lint.make_ascii_lowercase();
++            clippy_lints::explain(&lint.strip_prefix("clippy::").unwrap_or(&lint).replace('-', "_"));
 +        } else {
 +            show_help();
 +        }
 +        return;
 +    }
 +
 +    if let Err(code) = process(env::args().skip(2)) {
 +        process::exit(code);
 +    }
 +}
 +
 +struct ClippyCmd {
 +    cargo_subcommand: &'static str,
 +    args: Vec<String>,
 +    clippy_args: Vec<String>,
 +}
 +
 +impl ClippyCmd {
 +    fn new<I>(mut old_args: I) -> Self
 +    where
 +        I: Iterator<Item = String>,
 +    {
 +        let mut cargo_subcommand = "check";
 +        let mut args = vec![];
 +        let mut clippy_args: Vec<String> = vec![];
 +
 +        for arg in old_args.by_ref() {
 +            match arg.as_str() {
 +                "--fix" => {
 +                    cargo_subcommand = "fix";
 +                    continue;
 +                },
 +                "--no-deps" => {
 +                    clippy_args.push("--no-deps".into());
 +                    continue;
 +                },
 +                "--" => break,
 +                _ => {},
 +            }
 +
 +            args.push(arg);
 +        }
 +
 +        clippy_args.append(&mut (old_args.collect()));
 +        if cargo_subcommand == "fix" && !clippy_args.iter().any(|arg| arg == "--no-deps") {
 +            clippy_args.push("--no-deps".into());
 +        }
 +
 +        Self {
 +            cargo_subcommand,
 +            args,
 +            clippy_args,
 +        }
 +    }
 +
 +    fn path() -> PathBuf {
 +        let mut path = env::current_exe()
 +            .expect("current executable path invalid")
 +            .with_file_name("clippy-driver");
 +
 +        if cfg!(windows) {
 +            path.set_extension("exe");
 +        }
 +
 +        path
 +    }
 +
 +    fn into_std_cmd(self) -> Command {
 +        let mut cmd = Command::new("cargo");
 +        let clippy_args: String = self
 +            .clippy_args
 +            .iter()
 +            .map(|arg| format!("{arg}__CLIPPY_HACKERY__"))
 +            .collect();
 +
 +        // Currently, `CLIPPY_TERMINAL_WIDTH` is used only to format "unknown field" error messages.
 +        let terminal_width = termize::dimensions().map_or(0, |(w, _)| w);
 +
 +        cmd.env("RUSTC_WORKSPACE_WRAPPER", Self::path())
 +            .env("CLIPPY_ARGS", clippy_args)
 +            .env("CLIPPY_TERMINAL_WIDTH", terminal_width.to_string())
 +            .arg(self.cargo_subcommand)
 +            .args(&self.args);
 +
 +        cmd
 +    }
 +}
 +
 +fn process<I>(old_args: I) -> Result<(), i32>
 +where
 +    I: Iterator<Item = String>,
 +{
 +    let cmd = ClippyCmd::new(old_args);
 +
 +    let mut cmd = cmd.into_std_cmd();
 +
 +    let exit_status = cmd
 +        .spawn()
 +        .expect("could not run cargo")
 +        .wait()
 +        .expect("failed to wait for cargo?");
 +
 +    if exit_status.success() {
 +        Ok(())
 +    } else {
 +        Err(exit_status.code().unwrap_or(-1))
 +    }
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::ClippyCmd;
 +
 +    #[test]
 +    fn fix() {
 +        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!("fix", cmd.cargo_subcommand);
 +        assert!(!cmd.args.iter().any(|arg| arg.ends_with("unstable-options")));
 +    }
 +
 +    #[test]
 +    fn fix_implies_no_deps() {
 +        let args = "cargo clippy --fix".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert!(cmd.clippy_args.iter().any(|arg| arg == "--no-deps"));
 +    }
 +
 +    #[test]
 +    fn no_deps_not_duplicated_with_fix() {
 +        let args = "cargo clippy --fix -- --no-deps"
 +            .split_whitespace()
 +            .map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!(cmd.clippy_args.iter().filter(|arg| *arg == "--no-deps").count(), 1);
 +    }
 +
 +    #[test]
 +    fn check() {
 +        let args = "cargo clippy".split_whitespace().map(ToString::to_string);
 +        let cmd = ClippyCmd::new(args);
 +        assert_eq!("check", cmd.cargo_subcommand);
 +    }
 +}
index 9a7d802dc6d3a64ae041532679d4d6b0f1c7f520,0000000000000000000000000000000000000000..163f8bb35e79bef6831b8c2a5ca1201444b58355
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,22 @@@
- error: aborting due to previous error; 1 warning emitted
 +warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.59.0` from `clippy.toml`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/main.rs:6:21
 +   |
 +LL |     pub fn bar() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/main.rs:1:9
 +   |
 +LL | #![deny(clippy::use_self)]
 +   |         ^^^^^^^^^^^^^^^^
 +
++error: unnecessary structure name repetition
++  --> $DIR/main.rs:7:9
++   |
++LL |         Foo
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 2 previous errors; 1 warning emitted
 +
index a280e1bacdfdca063dae06c0edd6cf8a2f0ea52b,0000000000000000000000000000000000000000..259d39b12526cef2c87c2b6c9b5b46b28483d873
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,20 @@@
- error: aborting due to previous error
 +error: unnecessary structure name repetition
 +  --> $DIR/main.rs:6:21
 +   |
 +LL |     pub fn bar() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/main.rs:1:9
 +   |
 +LL | #![deny(clippy::use_self)]
 +   |         ^^^^^^^^^^^^^^^^
 +
++error: unnecessary structure name repetition
++  --> $DIR/main.rs:7:9
++   |
++LL |         Foo
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 2 previous errors
 +
index a280e1bacdfdca063dae06c0edd6cf8a2f0ea52b,0000000000000000000000000000000000000000..259d39b12526cef2c87c2b6c9b5b46b28483d873
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,20 @@@
- error: aborting due to previous error
 +error: unnecessary structure name repetition
 +  --> $DIR/main.rs:6:21
 +   |
 +LL |     pub fn bar() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/main.rs:1:9
 +   |
 +LL | #![deny(clippy::use_self)]
 +   |         ^^^^^^^^^^^^^^^^
 +
++error: unnecessary structure name repetition
++  --> $DIR/main.rs:7:9
++   |
++LL |         Foo
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 2 previous errors
 +
index a280e1bacdfdca063dae06c0edd6cf8a2f0ea52b,0000000000000000000000000000000000000000..259d39b12526cef2c87c2b6c9b5b46b28483d873
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,20 @@@
- error: aborting due to previous error
 +error: unnecessary structure name repetition
 +  --> $DIR/main.rs:6:21
 +   |
 +LL |     pub fn bar() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/main.rs:1:9
 +   |
 +LL | #![deny(clippy::use_self)]
 +   |         ^^^^^^^^^^^^^^^^
 +
++error: unnecessary structure name repetition
++  --> $DIR/main.rs:7:9
++   |
++LL |         Foo
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 2 previous errors
 +
index 88f6e00922bc4875acfdb9f199daa4951d9a5ffc,0000000000000000000000000000000000000000..97e6c3d5a55928d2db3a6ec727e3e9f712bd2ebf
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,20 @@@
- error: aborting due to previous error
 +error: unnecessary structure name repetition
 +  --> $DIR/main.rs:11:21
 +   |
 +LL |     pub fn bar() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/main.rs:6:9
 +   |
 +LL | #![deny(clippy::use_self)]
 +   |         ^^^^^^^^^^^^^^^^
 +
++error: unnecessary structure name repetition
++  --> $DIR/main.rs:12:9
++   |
++LL |         Foo
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 2 previous errors
 +
index 5057a018300e98519207fed44c21d3e883b79f1c,0000000000000000000000000000000000000000..4be04f77f5bdcb7f497d89df1cbbac1ca87d88d0
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,12 @@@
 +// rustc-env:RUST_BACKTRACE=0
 +// normalize-stderr-test: "Clippy version: .*" -> "Clippy version: foo"
 +// normalize-stderr-test: "internal_lints.rs:\d*:\d*" -> "internal_lints.rs"
 +// normalize-stderr-test: "', .*clippy_lints" -> "', clippy_lints"
++// normalize-stderr-test: "'rustc'" -> "'<unnamed>'"
 +
 +#![deny(clippy::internal)]
 +#![allow(clippy::missing_clippy_version_attribute)]
 +
 +fn it_looks_like_you_are_trying_to_kill_clippy() {}
 +
 +fn main() {}
index 07c5941013c1af5d6b89a661100c9b2a42e01801,0000000000000000000000000000000000000000..2ba5890660fc97a774f67f81aa9ab0d44387d2a6
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
- thread 'rustc' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9
++thread '<unnamed>' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs:28:9
 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 +
 +error: internal compiler error: unexpected panic
 +
 +note: the compiler unexpectedly panicked. this is a bug.
 +
 +note: we would appreciate a bug report: https://github.com/rust-lang/rust-clippy/issues/new
 +
 +note: Clippy version: foo
 +
 +query stack during panic:
 +end of query stack
index 2a240cc249b0c768f084dab5b6f220ae53a59399,0000000000000000000000000000000000000000..8bfc060e991064d98905105f93fa9a398a709351
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
-    = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
++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`
- 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: 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: aborting due to 3 previous errors
 +
index 1aed09b7c7bd4bc7dcdb4462191df436c0ba52ee,0000000000000000000000000000000000000000..e8a023ab17643514d08d08c803d51beea762610f
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,33 @@@
- use core::ops::Add;
 +#![warn(clippy::arithmetic_side_effects)]
 +
++use core::ops::{Add, Neg};
 +
 +#[derive(Clone, Copy)]
 +struct Point {
 +    x: i32,
 +    y: i32,
 +}
 +
 +impl Add for Point {
 +    type Output = Self;
 +
 +    fn add(self, other: Self) -> Self {
 +        todo!()
 +    }
 +}
 +
++impl Neg for Point {
++    type Output = Self;
++
++    fn neg(self) -> Self::Output {
++        todo!()
++    }
++}
++
 +fn main() {
 +    let _ = Point { x: 1, y: 0 } + Point { x: 2, y: 3 };
 +
 +    let point: Point = Point { x: 1, y: 0 };
 +    let _ = point + point;
++    let _ = -point;
 +}
index 4c75998437fd5c256ed6789de53cbf68706825c3,0000000000000000000000000000000000000000..825aa1487e7a3f682ed07a2f133ee2cf3f3a6df7
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,25 @@@
-    = note: strings are bad
 +error: `std::string::String` may not be held across an `await` point per `clippy.toml`
 +  --> $DIR/await_holding_invalid_type.rs:5:9
 +   |
 +LL |     let _x = String::from("hello");
 +   |         ^^
 +   |
-    = note: strings are bad
++   = note: strings are bad (from clippy.toml)
 +   = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
 +
 +error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
 +  --> $DIR/await_holding_invalid_type.rs:10:9
 +   |
 +LL |     let _x = Ipv4Addr::new(127, 0, 0, 1);
 +   |         ^^
 +
 +error: `std::string::String` may not be held across an `await` point per `clippy.toml`
 +  --> $DIR/await_holding_invalid_type.rs:31:13
 +   |
 +LL |         let _x = String::from("hi!");
 +   |             ^^
 +   |
++   = note: strings are bad (from clippy.toml)
 +
 +error: aborting due to 3 previous errors
 +
index 22dcd3ae9d697a6b9ef2ac140c560f662350c551,0000000000000000000000000000000000000000..bff97d97df77aa38f87d49dec1c752110e7fba1f
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,34 @@@
- #[test]
- fn test_expect_option() {
-     let opt = Some(0);
-     let _ = opt.expect("");
- }
 +// compile-flags: --test
 +#![warn(clippy::expect_used)]
 +
 +fn expect_option() {
 +    let opt = Some(0);
 +    let _ = opt.expect("");
 +}
 +
 +fn expect_result() {
 +    let res: Result<u8, ()> = Ok(0);
 +    let _ = res.expect("");
 +}
 +
 +fn main() {
 +    expect_option();
 +    expect_result();
 +}
 +
- #[test]
- fn test_expect_result() {
-     let res: Result<u8, ()> = Ok(0);
-     let _ = res.expect("");
++#[cfg(test)]
++mod issue9612 {
++    // should not lint in `#[cfg(test)]` modules
++    #[test]
++    fn test_fn() {
++        let _a: u8 = 2.try_into().unwrap();
++        let _a: u8 = 3.try_into().expect("");
 +
++        util();
++    }
++
++    fn util() {
++        let _a: u8 = 4.try_into().unwrap();
++        let _a: u8 = 5.try_into().expect("");
++    }
 +}
index 28a08599c67c6878d5c8868b25c46d429c7cecfd,0000000000000000000000000000000000000000..1e9bb48c333ca91c79b5a49611cbd1e849d19ed9
mode 100644,000000..100644
--- /dev/null
@@@ -1,19 -1,0 +1,19 @@@
- error: used `expect()` on `an Option` value
++error: used `expect()` on an `Option` value
 +  --> $DIR/expect_used.rs:6:13
 +   |
 +LL |     let _ = opt.expect("");
 +   |             ^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is `None`, it will panic
 +   = note: `-D clippy::expect-used` implied by `-D warnings`
 +
- error: used `expect()` on `a Result` value
++error: used `expect()` on a `Result` value
 +  --> $DIR/expect_used.rs:11:13
 +   |
 +LL |     let _ = res.expect("");
 +   |             ^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
 +error: aborting due to 2 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6d33e192ee893e6e4d75204a17686f471236500e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++ignore-interior-mutability = ["mut_key::Counted"]
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..667c51cb4a3f348ad202c5111e1609e2565ead55
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,53 @@@
++// compile-flags: --crate-name mut_key
++
++#![warn(clippy::mutable_key_type)]
++
++use std::cmp::{Eq, PartialEq};
++use std::collections::{HashMap, HashSet};
++use std::hash::{Hash, Hasher};
++use std::ops::Deref;
++use std::sync::atomic::{AtomicUsize, Ordering};
++
++struct Counted<T> {
++    count: AtomicUsize,
++    val: T,
++}
++
++impl<T: Clone> Clone for Counted<T> {
++    fn clone(&self) -> Self {
++        Self {
++            count: AtomicUsize::new(0),
++            val: self.val.clone(),
++        }
++    }
++}
++
++impl<T: PartialEq> PartialEq for Counted<T> {
++    fn eq(&self, other: &Self) -> bool {
++        self.val == other.val
++    }
++}
++impl<T: PartialEq + Eq> Eq for Counted<T> {}
++
++impl<T: Hash> Hash for Counted<T> {
++    fn hash<H: Hasher>(&self, state: &mut H) {
++        self.val.hash(state);
++    }
++}
++
++impl<T> Deref for Counted<T> {
++    type Target = T;
++
++    fn deref(&self) -> &T {
++        self.count.fetch_add(1, Ordering::AcqRel);
++        &self.val
++    }
++}
++
++// This is not linted because `"mut_key::Counted"` is in
++// `arc_like_types` in `clippy.toml`
++fn should_not_take_this_arg(_v: HashSet<Counted<String>>) {}
++
++fn main() {
++    should_not_take_this_arg(HashSet::new());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..40b1dda5b2ea7896464b582b9edc7470606a2ddb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++allow-print-in-tests = true
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5aefb6a6b4d467031c25d37ca0599ba5b4cb23a0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++// compile-flags: --test
++#![warn(clippy::print_stdout)]
++#![warn(clippy::print_stderr)]
++
++fn foo(n: u32) {
++    print!("{n}");
++    eprint!("{n}");
++}
++
++#[test]
++pub fn foo1() {
++    print!("{}", 1);
++    eprint!("{}", 1);
++}
++
++#[cfg(test)]
++fn foo3() {
++    print!("{}", 1);
++    eprint!("{}", 1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d4b1ae84fd7dca431c88afbab3f7d197c357bae8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++error: use of `print!`
++  --> $DIR/print_macro.rs:6:5
++   |
++LL |     print!("{n}");
++   |     ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::print-stdout` implied by `-D warnings`
++
++error: use of `eprint!`
++  --> $DIR/print_macro.rs:7:5
++   |
++LL |     eprint!("{n}");
++   |     ^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::print-stderr` implied by `-D warnings`
++
++error: aborting due to 2 previous errors
++
index 28774db625bb77ff5d72e051850b076686dd7393,0000000000000000000000000000000000000000..41dbd5068479bc59f26985531e1212ebab9d1d5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,17 @@@
 +disallowed-methods = [
 +    # just a string is shorthand for path only
 +    "std::iter::Iterator::sum",
 +    "f32::clamp",
 +    "slice::sort_unstable",
 +    "futures::stream::select_all",
 +    # can give path and reason with an inline table
 +    { path = "regex::Regex::is_match", reason = "no matching allowed" },
 +    # can use an inline table but omit reason
 +    { path = "regex::Regex::new" },
++    # local paths
++    "conf_disallowed_methods::local_fn",
++    "conf_disallowed_methods::local_mod::f",
++    "conf_disallowed_methods::Struct::method",
++    "conf_disallowed_methods::Trait::provided_method",
++    "conf_disallowed_methods::Trait::implemented_method",
 +]
index b483f160028976db51616dffefa214f4af61e588,0000000000000000000000000000000000000000..2f3160c833833314263414c602108d2ec5753b60
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,59 @@@
++// compile-flags: --crate-name conf_disallowed_methods
++
 +#![warn(clippy::disallowed_methods)]
 +
 +extern crate futures;
 +extern crate regex;
 +
 +use futures::stream::{empty, select_all};
 +use regex::Regex;
 +
++fn local_fn() {}
++
++struct Struct;
++
++impl Struct {
++    fn method(&self) {}
++}
++
++trait Trait {
++    fn provided_method(&self) {}
++    fn implemented_method(&self);
++}
++
++impl Trait for Struct {
++    fn implemented_method(&self) {}
++}
++
++mod local_mod {
++    pub fn f() {}
++}
++
 +fn main() {
 +    let re = Regex::new(r"ab.*c").unwrap();
 +    re.is_match("abc");
 +
 +    let mut a = vec![1, 2, 3, 4];
 +    a.iter().sum::<i32>();
 +
 +    a.sort_unstable();
 +
 +    let _ = 2.0f32.clamp(3.0f32, 4.0f32);
 +    let _ = 2.0f64.clamp(3.0f64, 4.0f64);
 +
 +    let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
 +    let re = indirect(".").unwrap();
 +
 +    let in_call = Box::new(f32::clamp);
 +    let in_method_call = ["^", "$"].into_iter().map(Regex::new);
 +
 +    // resolve ambiguity between `futures::stream::select_all` the module and the function
 +    let same_name_as_module = select_all(vec![empty::<()>()]);
++
++    local_fn();
++    local_mod::f();
++    let s = Struct;
++    s.method();
++    s.provided_method();
++    s.implemented_method();
 +}
index 6d78c32e127ee9ba8cb13e7079cfc4539af45361,0000000000000000000000000000000000000000..148d1cae51f1082cc89ab3c832e738cb5e5fc7d5
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,90 @@@
-   --> $DIR/conf_disallowed_methods.rs:10:14
 +error: use of a disallowed method `regex::Regex::new`
-   --> $DIR/conf_disallowed_methods.rs:11:5
++  --> $DIR/conf_disallowed_methods.rs:33:14
 +   |
 +LL |     let re = Regex::new(r"ab.*c").unwrap();
 +   |              ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::disallowed-methods` implied by `-D warnings`
 +
 +error: use of a disallowed method `regex::Regex::is_match`
-   --> $DIR/conf_disallowed_methods.rs:14:5
++  --> $DIR/conf_disallowed_methods.rs:34:5
 +   |
 +LL |     re.is_match("abc");
 +   |     ^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: no matching allowed (from clippy.toml)
 +
 +error: use of a disallowed method `std::iter::Iterator::sum`
-   --> $DIR/conf_disallowed_methods.rs:16:5
++  --> $DIR/conf_disallowed_methods.rs:37:5
 +   |
 +LL |     a.iter().sum::<i32>();
 +   |     ^^^^^^^^^^^^^^^^^^^^^
 +
 +error: use of a disallowed method `slice::sort_unstable`
-   --> $DIR/conf_disallowed_methods.rs:18:13
++  --> $DIR/conf_disallowed_methods.rs:39:5
 +   |
 +LL |     a.sort_unstable();
 +   |     ^^^^^^^^^^^^^^^^^
 +
 +error: use of a disallowed method `f32::clamp`
-   --> $DIR/conf_disallowed_methods.rs:21:61
++  --> $DIR/conf_disallowed_methods.rs:41:13
 +   |
 +LL |     let _ = 2.0f32.clamp(3.0f32, 4.0f32);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: use of a disallowed method `regex::Regex::new`
-   --> $DIR/conf_disallowed_methods.rs:24:28
++  --> $DIR/conf_disallowed_methods.rs:44:61
 +   |
 +LL |     let indirect: fn(&str) -> Result<Regex, regex::Error> = Regex::new;
 +   |                                                             ^^^^^^^^^^
 +
 +error: use of a disallowed method `f32::clamp`
-   --> $DIR/conf_disallowed_methods.rs:25:53
++  --> $DIR/conf_disallowed_methods.rs:47:28
 +   |
 +LL |     let in_call = Box::new(f32::clamp);
 +   |                            ^^^^^^^^^^
 +
 +error: use of a disallowed method `regex::Regex::new`
-   --> $DIR/conf_disallowed_methods.rs:28:31
++  --> $DIR/conf_disallowed_methods.rs:48:53
 +   |
 +LL |     let in_method_call = ["^", "$"].into_iter().map(Regex::new);
 +   |                                                     ^^^^^^^^^^
 +
 +error: use of a disallowed method `futures::stream::select_all`
- error: aborting due to 9 previous errors
++  --> $DIR/conf_disallowed_methods.rs:51:31
 +   |
 +LL |     let same_name_as_module = select_all(vec![empty::<()>()]);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: use of a disallowed method `conf_disallowed_methods::local_fn`
++  --> $DIR/conf_disallowed_methods.rs:53:5
++   |
++LL |     local_fn();
++   |     ^^^^^^^^^^
++
++error: use of a disallowed method `conf_disallowed_methods::local_mod::f`
++  --> $DIR/conf_disallowed_methods.rs:54:5
++   |
++LL |     local_mod::f();
++   |     ^^^^^^^^^^^^^^
++
++error: use of a disallowed method `conf_disallowed_methods::Struct::method`
++  --> $DIR/conf_disallowed_methods.rs:56:5
++   |
++LL |     s.method();
++   |     ^^^^^^^^^^
++
++error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method`
++  --> $DIR/conf_disallowed_methods.rs:57:5
++   |
++LL |     s.provided_method();
++   |     ^^^^^^^^^^^^^^^^^^^
++
++error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method`
++  --> $DIR/conf_disallowed_methods.rs:58:5
++   |
++LL |     s.implemented_method();
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 14 previous errors
 +
index 82ee8054132172a40b3323c5f99bf133df166a1c,0000000000000000000000000000000000000000..f91d285c2e0eefcc3d06aea471a6f5b59a14caa2
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,51 @@@
 +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-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 0e82fb20e45585817de8323ec873f8ad73623d1a,0000000000000000000000000000000000000000..bc8e8c1f0703a3c9c9b0827b739006c5ef3903f0
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,86 @@@
- #[test]
- fn test() {
-     let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
-     let _ = boxed_slice.get(1).unwrap();
 +// compile-flags: --test
 +
 +#![allow(unused_mut, clippy::get_first, clippy::from_iter_instead_of_collect)]
 +#![warn(clippy::unwrap_used)]
 +#![deny(clippy::get_unwrap)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::collections::VecDeque;
 +
 +struct GetFalsePositive {
 +    arr: [u32; 3],
 +}
 +
 +impl GetFalsePositive {
 +    fn get(&self, pos: usize) -> Option<&u32> {
 +        self.arr.get(pos)
 +    }
 +    fn get_mut(&mut self, pos: usize) -> Option<&mut u32> {
 +        self.arr.get_mut(pos)
 +    }
 +}
 +
 +fn main() {
 +    let mut boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
 +    let mut some_slice = &mut [0, 1, 2, 3];
 +    let mut some_vec = vec![0, 1, 2, 3];
 +    let mut some_vecdeque: VecDeque<_> = some_vec.iter().cloned().collect();
 +    let mut some_hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(1, 'a'), (2, 'b')]);
 +    let mut some_btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(1, 'a'), (2, 'b')]);
 +    let mut false_positive = GetFalsePositive { arr: [0, 1, 2] };
 +
 +    {
 +        // Test `get().unwrap()`
 +        let _ = boxed_slice.get(1).unwrap();
 +        let _ = some_slice.get(0).unwrap();
 +        let _ = some_vec.get(0).unwrap();
 +        let _ = some_vecdeque.get(0).unwrap();
 +        let _ = some_hashmap.get(&1).unwrap();
 +        let _ = some_btreemap.get(&1).unwrap();
 +        #[allow(clippy::unwrap_used)]
 +        let _ = false_positive.get(0).unwrap();
 +        // Test with deref
 +        let _: u8 = *boxed_slice.get(1).unwrap();
 +    }
 +
 +    {
 +        // Test `get_mut().unwrap()`
 +        *boxed_slice.get_mut(0).unwrap() = 1;
 +        *some_slice.get_mut(0).unwrap() = 1;
 +        *some_vec.get_mut(0).unwrap() = 1;
 +        *some_vecdeque.get_mut(0).unwrap() = 1;
 +        // Check false positives
 +        #[allow(clippy::unwrap_used)]
 +        {
 +            *some_hashmap.get_mut(&1).unwrap() = 'b';
 +            *some_btreemap.get_mut(&1).unwrap() = 'b';
 +            *false_positive.get_mut(0).unwrap() = 1;
 +        }
 +    }
 +
 +    {
 +        // Test `get().unwrap().foo()` and `get_mut().unwrap().bar()`
 +        let _ = some_vec.get(0..1).unwrap().to_vec();
 +        let _ = some_vec.get_mut(0..1).unwrap().to_vec();
 +    }
 +}
 +
++#[cfg(test)]
++mod issue9612 {
++    // should not lint in `#[cfg(test)]` modules
++    #[test]
++    fn test_fn() {
++        let _a: u8 = 2.try_into().unwrap();
++        let _a: u8 = 3.try_into().expect("");
++
++        util();
++    }
++
++    fn util() {
++        let _a: u8 = 4.try_into().unwrap();
++        let _a: u8 = 5.try_into().expect("");
++        // should still warn
++        let _ = Box::new([0]).get(1).unwrap();
++    }
 +}
index 681b5eaf54dba0cc9fe634c15c5fdb3304317614,0000000000000000000000000000000000000000..94b5ef663add9404af3375ed13d58ac4cb1b65c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,197 -1,0 +1,197 @@@
- error: used `unwrap()` on `an Option` value
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:35:17
 +   |
 +LL |         let _ = boxed_slice.get(1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/unwrap_used.rs:5:9
 +   |
 +LL | #![deny(clippy::get_unwrap)]
 +   |         ^^^^^^^^^^^^^^^^^^
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:35:17
 +   |
 +LL |         let _ = boxed_slice.get(1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +   = note: `-D clippy::unwrap-used` implied by `-D warnings`
 +
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:36:17
 +   |
 +LL |         let _ = some_slice.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:36:17
 +   |
 +LL |         let _ = some_slice.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:37:17
 +   |
 +LL |         let _ = some_vec.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:37:17
 +   |
 +LL |         let _ = some_vec.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:38:17
 +   |
 +LL |         let _ = some_vecdeque.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:38:17
 +   |
 +LL |         let _ = some_vecdeque.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:39:17
 +   |
 +LL |         let _ = some_hashmap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:39:17
 +   |
 +LL |         let _ = some_hashmap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:40:17
 +   |
 +LL |         let _ = some_btreemap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:40:17
 +   |
 +LL |         let _ = some_btreemap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:44:21
 +   |
 +LL |         let _: u8 = *boxed_slice.get(1).unwrap();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:44:22
 +   |
 +LL |         let _: u8 = *boxed_slice.get(1).unwrap();
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:49:9
 +   |
 +LL |         *boxed_slice.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:49:10
 +   |
 +LL |         *boxed_slice.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:50:9
 +   |
 +LL |         *some_slice.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:50:10
 +   |
 +LL |         *some_slice.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:51:9
 +   |
 +LL |         *some_vec.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:51:10
 +   |
 +LL |         *some_vec.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:52:9
 +   |
 +LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:52:10
 +   |
 +LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:64:17
 +   |
 +LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:64:17
 +   |
 +LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/unwrap_used.rs:65:17
 +   |
 +LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
 +
-   --> $DIR/unwrap_used.rs:72:13
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_used.rs:65:17
 +   |
 +LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
- LL |     let _ = boxed_slice.get(1).unwrap();
-    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
++  --> $DIR/unwrap_used.rs:84:17
 +   |
++LL |         let _ = Box::new([0]).get(1).unwrap();
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&Box::new([0])[1]`
 +
 +error: aborting due to 27 previous errors
 +
index bf04bee16373c3c9e22f09bc896632540e6705f5,0000000000000000000000000000000000000000..4c46deb585b6c26297d3f931b6ce21f90d3f4ee8
mode 100644,000000..100644
--- /dev/null
@@@ -1,15 -1,0 +1,16 @@@
- struct Bar(Vec<Box<u32>>);
- struct Baz(Vec<Box<(u32, u32)>>);
 +struct S {
 +    x: u64,
 +}
 +
 +struct C {
 +    y: u16,
 +}
 +
 +struct Foo(Vec<Box<u8>>);
++struct Bar(Vec<Box<u16>>);
++struct Quux(Vec<Box<u32>>);
++struct Baz(Vec<Box<(u16, u16)>>);
 +struct BarBaz(Vec<Box<S>>);
 +struct FooBarBaz(Vec<Box<C>>);
 +
 +fn main() {}
index cf194de3c55375f2aba1422fcf45527846678180,0000000000000000000000000000000000000000..55de68f8ecf47b21cc90489ea7f8c4ec06b21db8
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
- LL | struct Bar(Vec<Box<u32>>);
-    |            ^^^^^^^^^^^^^ help: try: `Vec<u32>`
 +error: `Vec<T>` is already on the heap, the boxing is unnecessary
 +  --> $DIR/test.rs:9:12
 +   |
 +LL | struct Foo(Vec<Box<u8>>);
 +   |            ^^^^^^^^^^^^ help: try: `Vec<u8>`
 +   |
 +   = note: `-D clippy::vec-box` implied by `-D warnings`
 +
 +error: `Vec<T>` is already on the heap, the boxing is unnecessary
 +  --> $DIR/test.rs:10:12
 +   |
-   --> $DIR/test.rs:13:18
++LL | struct Bar(Vec<Box<u16>>);
++   |            ^^^^^^^^^^^^^ help: try: `Vec<u16>`
 +
 +error: `Vec<T>` is already on the heap, the boxing is unnecessary
++  --> $DIR/test.rs:14:18
 +   |
 +LL | struct FooBarBaz(Vec<Box<C>>);
 +   |                  ^^^^^^^^^^^ help: try: `Vec<C>`
 +
 +error: aborting due to 3 previous errors
 +
index b25e68f13061b9f6f13eb6646818c41519f85f9b,0000000000000000000000000000000000000000..b5ed8988a518ff9299bc679ce8a5c0d9efb4f104
mode 100644,000000..100644
--- /dev/null
@@@ -1,224 -1,0 +1,228 @@@
-     _n = -1;
-     _n = -(-1);
 +#![allow(
 +    clippy::assign_op_pattern,
 +    clippy::erasing_op,
 +    clippy::identity_op,
 +    clippy::op_ref,
 +    clippy::unnecessary_owned_empty_strings,
 +    arithmetic_overflow,
 +    unconditional_panic
 +)]
 +#![feature(const_mut_refs, inline_const, saturating_int_impl)]
 +#![warn(clippy::arithmetic_side_effects)]
 +
 +use core::num::{Saturating, Wrapping};
 +
 +pub struct Custom;
 +
 +macro_rules! impl_arith {
 +    ( $( $_trait:ident, $ty:ty, $method:ident; )* ) => {
 +        $(
 +            impl core::ops::$_trait<$ty> for Custom {
 +                type Output = Self;
 +                fn $method(self, _: $ty) -> Self::Output { Self }
 +            }
 +        )*
 +    }
 +}
 +
 +impl_arith!(
 +    Add, i32, add;
 +    Div, i32, div;
 +    Mul, i32, mul;
 +    Sub, i32, sub;
 +
 +    Add, f64, add;
 +    Div, f64, div;
 +    Mul, f64, mul;
 +    Sub, f64, sub;
 +);
 +
 +pub fn association_with_structures_should_not_trigger_the_lint() {
 +    enum Foo {
 +        Bar = -2,
 +    }
 +
 +    impl Trait for Foo {
 +        const ASSOC: i32 = {
 +            let _: [i32; 1 + 1];
 +            fn foo() {}
 +            1 + 1
 +        };
 +    }
 +
 +    struct Baz([i32; 1 + 1]);
 +
 +    trait Trait {
 +        const ASSOC: i32 = 1 + 1;
 +    }
 +
 +    type Alias = [i32; 1 + 1];
 +
 +    union Qux {
 +        field: [i32; 1 + 1],
 +    }
 +
 +    let _: [i32; 1 + 1] = [0, 0];
 +
 +    let _: [i32; 1 + 1] = {
 +        let a: [i32; 1 + 1] = [0, 0];
 +        a
 +    };
 +}
 +
 +pub fn hard_coded_allowed() {
 +    let _ = 1f32 + 1f32;
 +    let _ = 1f64 + 1f64;
 +
 +    let _ = Saturating(0u32) + Saturating(0u32);
 +    let _ = String::new() + "";
 +    let _ = Wrapping(0u32) + Wrapping(0u32);
 +
 +    let saturating: Saturating<u32> = Saturating(0u32);
 +    let string: String = String::new();
 +    let wrapping: Wrapping<u32> = Wrapping(0u32);
 +
 +    let inferred_saturating = saturating + saturating;
 +    let inferred_string = string + "";
 +    let inferred_wrapping = wrapping + wrapping;
 +
 +    let _ = inferred_saturating + inferred_saturating;
 +    let _ = inferred_string + "";
 +    let _ = inferred_wrapping + inferred_wrapping;
 +}
 +
 +#[rustfmt::skip]
 +pub fn const_ops_should_not_trigger_the_lint() {
 +    const _: i32 = { let mut n = 1; n += 1; n };
 +    let _ = const { let mut n = 1; n += 1; n };
 +
 +    const _: i32 = { let mut n = 1; n = n + 1; n };
 +    let _ = const { let mut n = 1; n = n + 1; n };
 +
 +    const _: i32 = { let mut n = 1; n = 1 + n; n };
 +    let _ = const { let mut n = 1; n = 1 + n; n };
 +
 +    const _: i32 = 1 + 1;
 +    let _ = const { 1 + 1 };
 +
 +    const _: i32 = { let mut n = 1; n = -1; n = -(-1); n = -n; n };
 +    let _ = const { let mut n = 1; n = -1; n = -(-1); n = -n; n };
 +}
 +
 +pub fn non_overflowing_ops_or_ops_already_handled_by_the_compiler_should_not_trigger_the_lint() {
 +    let mut _n = i32::MAX;
 +
 +    // Assign
 +    _n += 0;
 +    _n += &0;
 +    _n -= 0;
 +    _n -= &0;
 +    _n /= 99;
 +    _n /= &99;
 +    _n %= 99;
 +    _n %= &99;
 +    _n *= 0;
 +    _n *= &0;
 +    _n *= 1;
 +    _n *= &1;
 +
 +    // Binary
 +    _n = _n + 0;
 +    _n = _n + &0;
 +    _n = 0 + _n;
 +    _n = &0 + _n;
 +    _n = _n - 0;
 +    _n = _n - &0;
 +    _n = 0 - _n;
 +    _n = &0 - _n;
 +    _n = _n / 99;
 +    _n = _n / &99;
 +    _n = _n % 99;
 +    _n = _n % &99;
 +    _n = _n * 0;
 +    _n = _n * &0;
 +    _n = 0 * _n;
 +    _n = &0 * _n;
 +    _n = _n * 1;
 +    _n = _n * &1;
 +    _n = 1 * _n;
 +    _n = &1 * _n;
 +    _n = 23 + 85;
 +
 +    // Unary
++    _n = -2147483647;
++    _n = -i32::MAX;
++    _n = -i32::MIN;
++    _n = -&2147483647;
++    _n = -&i32::MAX;
++    _n = -&i32::MIN;
 +}
 +
 +pub fn runtime_ops() {
 +    let mut _n = i32::MAX;
 +
 +    // Assign
 +    _n += 1;
 +    _n += &1;
 +    _n -= 1;
 +    _n -= &1;
 +    _n /= 0;
 +    _n /= &0;
 +    _n %= 0;
 +    _n %= &0;
 +    _n *= 2;
 +    _n *= &2;
 +
 +    // Binary
 +    _n = _n + 1;
 +    _n = _n + &1;
 +    _n = 1 + _n;
 +    _n = &1 + _n;
 +    _n = _n - 1;
 +    _n = _n - &1;
 +    _n = 1 - _n;
 +    _n = &1 - _n;
 +    _n = _n / 0;
 +    _n = _n / &0;
 +    _n = _n % 0;
 +    _n = _n % &0;
 +    _n = _n * 2;
 +    _n = _n * &2;
 +    _n = 2 * _n;
 +    _n = &2 * _n;
 +    _n = 23 + &85;
 +    _n = &23 + 85;
 +    _n = &23 + &85;
 +
 +    // Custom
 +    let _ = Custom + 0;
 +    let _ = Custom + 1;
 +    let _ = Custom + 2;
 +    let _ = Custom + 0.0;
 +    let _ = Custom + 1.0;
 +    let _ = Custom + 2.0;
 +    let _ = Custom - 0;
 +    let _ = Custom - 1;
 +    let _ = Custom - 2;
 +    let _ = Custom - 0.0;
 +    let _ = Custom - 1.0;
 +    let _ = Custom - 2.0;
 +    let _ = Custom / 0;
 +    let _ = Custom / 1;
 +    let _ = Custom / 2;
 +    let _ = Custom / 0.0;
 +    let _ = Custom / 1.0;
 +    let _ = Custom / 2.0;
 +    let _ = Custom * 0;
 +    let _ = Custom * 1;
 +    let _ = Custom * 2;
 +    let _ = Custom * 0.0;
 +    let _ = Custom * 1.0;
 +    let _ = Custom * 2.0;
 +
 +    // Unary
 +    _n = -_n;
 +    _n = -&_n;
 +}
 +
 +fn main() {}
index 0f06e22bae96b5973c99ed8a30859e89d3bfdc3f,0000000000000000000000000000000000000000..0259a0824e794cc7ba5a0f957f0130f6b3c49a0e
mode 100644,000000..100644
--- /dev/null
@@@ -1,352 -1,0 +1,352 @@@
-   --> $DIR/arithmetic_side_effects.rs:161:5
 +error: arithmetic operation that can potentially result in unexpected side-effects
 +  --> $DIR/arithmetic_side_effects.rs:78:13
 +   |
 +LL |     let _ = String::new() + "";
 +   |             ^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::arithmetic-side-effects` implied by `-D warnings`
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
 +  --> $DIR/arithmetic_side_effects.rs:86:27
 +   |
 +LL |     let inferred_string = string + "";
 +   |                           ^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
 +  --> $DIR/arithmetic_side_effects.rs:90:13
 +   |
 +LL |     let _ = inferred_string + "";
 +   |             ^^^^^^^^^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:162:5
++  --> $DIR/arithmetic_side_effects.rs:165:5
 +   |
 +LL |     _n += 1;
 +   |     ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:163:5
++  --> $DIR/arithmetic_side_effects.rs:166:5
 +   |
 +LL |     _n += &1;
 +   |     ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:164:5
++  --> $DIR/arithmetic_side_effects.rs:167:5
 +   |
 +LL |     _n -= 1;
 +   |     ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:165:5
++  --> $DIR/arithmetic_side_effects.rs:168:5
 +   |
 +LL |     _n -= &1;
 +   |     ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:166:5
++  --> $DIR/arithmetic_side_effects.rs:169:5
 +   |
 +LL |     _n /= 0;
 +   |     ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:167:5
++  --> $DIR/arithmetic_side_effects.rs:170:5
 +   |
 +LL |     _n /= &0;
 +   |     ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:168:5
++  --> $DIR/arithmetic_side_effects.rs:171:5
 +   |
 +LL |     _n %= 0;
 +   |     ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:169:5
++  --> $DIR/arithmetic_side_effects.rs:172:5
 +   |
 +LL |     _n %= &0;
 +   |     ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:170:5
++  --> $DIR/arithmetic_side_effects.rs:173:5
 +   |
 +LL |     _n *= 2;
 +   |     ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:173:10
++  --> $DIR/arithmetic_side_effects.rs:174:5
 +   |
 +LL |     _n *= &2;
 +   |     ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:174:10
++  --> $DIR/arithmetic_side_effects.rs:177:10
 +   |
 +LL |     _n = _n + 1;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:175:10
++  --> $DIR/arithmetic_side_effects.rs:178:10
 +   |
 +LL |     _n = _n + &1;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:176:10
++  --> $DIR/arithmetic_side_effects.rs:179:10
 +   |
 +LL |     _n = 1 + _n;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:177:10
++  --> $DIR/arithmetic_side_effects.rs:180:10
 +   |
 +LL |     _n = &1 + _n;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:178:10
++  --> $DIR/arithmetic_side_effects.rs:181:10
 +   |
 +LL |     _n = _n - 1;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:179:10
++  --> $DIR/arithmetic_side_effects.rs:182:10
 +   |
 +LL |     _n = _n - &1;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:180:10
++  --> $DIR/arithmetic_side_effects.rs:183:10
 +   |
 +LL |     _n = 1 - _n;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:181:10
++  --> $DIR/arithmetic_side_effects.rs:184:10
 +   |
 +LL |     _n = &1 - _n;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:182:10
++  --> $DIR/arithmetic_side_effects.rs:185:10
 +   |
 +LL |     _n = _n / 0;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:183:10
++  --> $DIR/arithmetic_side_effects.rs:186:10
 +   |
 +LL |     _n = _n / &0;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:184:10
++  --> $DIR/arithmetic_side_effects.rs:187:10
 +   |
 +LL |     _n = _n % 0;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:185:10
++  --> $DIR/arithmetic_side_effects.rs:188:10
 +   |
 +LL |     _n = _n % &0;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:186:10
++  --> $DIR/arithmetic_side_effects.rs:189:10
 +   |
 +LL |     _n = _n * 2;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:187:10
++  --> $DIR/arithmetic_side_effects.rs:190:10
 +   |
 +LL |     _n = _n * &2;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:188:10
++  --> $DIR/arithmetic_side_effects.rs:191:10
 +   |
 +LL |     _n = 2 * _n;
 +   |          ^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:189:10
++  --> $DIR/arithmetic_side_effects.rs:192:10
 +   |
 +LL |     _n = &2 * _n;
 +   |          ^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:190:10
++  --> $DIR/arithmetic_side_effects.rs:193:10
 +   |
 +LL |     _n = 23 + &85;
 +   |          ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:191:10
++  --> $DIR/arithmetic_side_effects.rs:194:10
 +   |
 +LL |     _n = &23 + 85;
 +   |          ^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:194:13
++  --> $DIR/arithmetic_side_effects.rs:195:10
 +   |
 +LL |     _n = &23 + &85;
 +   |          ^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:195:13
++  --> $DIR/arithmetic_side_effects.rs:198:13
 +   |
 +LL |     let _ = Custom + 0;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:196:13
++  --> $DIR/arithmetic_side_effects.rs:199:13
 +   |
 +LL |     let _ = Custom + 1;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:197:13
++  --> $DIR/arithmetic_side_effects.rs:200:13
 +   |
 +LL |     let _ = Custom + 2;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:198:13
++  --> $DIR/arithmetic_side_effects.rs:201:13
 +   |
 +LL |     let _ = Custom + 0.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:199:13
++  --> $DIR/arithmetic_side_effects.rs:202:13
 +   |
 +LL |     let _ = Custom + 1.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:200:13
++  --> $DIR/arithmetic_side_effects.rs:203:13
 +   |
 +LL |     let _ = Custom + 2.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:201:13
++  --> $DIR/arithmetic_side_effects.rs:204:13
 +   |
 +LL |     let _ = Custom - 0;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:202:13
++  --> $DIR/arithmetic_side_effects.rs:205:13
 +   |
 +LL |     let _ = Custom - 1;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:203:13
++  --> $DIR/arithmetic_side_effects.rs:206:13
 +   |
 +LL |     let _ = Custom - 2;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:204:13
++  --> $DIR/arithmetic_side_effects.rs:207:13
 +   |
 +LL |     let _ = Custom - 0.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:205:13
++  --> $DIR/arithmetic_side_effects.rs:208:13
 +   |
 +LL |     let _ = Custom - 1.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:206:13
++  --> $DIR/arithmetic_side_effects.rs:209:13
 +   |
 +LL |     let _ = Custom - 2.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:207:13
++  --> $DIR/arithmetic_side_effects.rs:210:13
 +   |
 +LL |     let _ = Custom / 0;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:208:13
++  --> $DIR/arithmetic_side_effects.rs:211:13
 +   |
 +LL |     let _ = Custom / 1;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:209:13
++  --> $DIR/arithmetic_side_effects.rs:212:13
 +   |
 +LL |     let _ = Custom / 2;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:210:13
++  --> $DIR/arithmetic_side_effects.rs:213:13
 +   |
 +LL |     let _ = Custom / 0.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:211:13
++  --> $DIR/arithmetic_side_effects.rs:214:13
 +   |
 +LL |     let _ = Custom / 1.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:212:13
++  --> $DIR/arithmetic_side_effects.rs:215:13
 +   |
 +LL |     let _ = Custom / 2.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:213:13
++  --> $DIR/arithmetic_side_effects.rs:216:13
 +   |
 +LL |     let _ = Custom * 0;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:214:13
++  --> $DIR/arithmetic_side_effects.rs:217:13
 +   |
 +LL |     let _ = Custom * 1;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:215:13
++  --> $DIR/arithmetic_side_effects.rs:218:13
 +   |
 +LL |     let _ = Custom * 2;
 +   |             ^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:216:13
++  --> $DIR/arithmetic_side_effects.rs:219:13
 +   |
 +LL |     let _ = Custom * 0.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:217:13
++  --> $DIR/arithmetic_side_effects.rs:220:13
 +   |
 +LL |     let _ = Custom * 1.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:220:10
++  --> $DIR/arithmetic_side_effects.rs:221:13
 +   |
 +LL |     let _ = Custom * 2.0;
 +   |             ^^^^^^^^^^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
-   --> $DIR/arithmetic_side_effects.rs:221:10
++  --> $DIR/arithmetic_side_effects.rs:224:10
 +   |
 +LL |     _n = -_n;
 +   |          ^^^
 +
 +error: arithmetic operation that can potentially result in unexpected side-effects
++  --> $DIR/arithmetic_side_effects.rs:225:10
 +   |
 +LL |     _n = -&_n;
 +   |          ^^^^
 +
 +error: aborting due to 58 previous errors
 +
index 869672d1eda5e7c549105fb7dd9d42404865935c,0000000000000000000000000000000000000000..3d917e3dc75e9211a5ca7f5a52de06d569792acb
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,16 @@@
 +#[macro_export]
 +macro_rules! undocd_unsafe {
 +    () => {
 +        pub unsafe fn oy_vey() {
 +            unimplemented!();
 +        }
 +    };
 +}
++#[macro_export]
++macro_rules! undocd_safe {
++    () => {
++        pub fn vey_oy() {
++            unimplemented!();
++        }
++    };
++}
index d055f17526b9b079db3a9b06271c3f04cfd665b6,0000000000000000000000000000000000000000..554745368bca4aa7f6862bcd66f37739d482ae9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,10 @@@
++// compile-flags: -W clippy::restriction
++
 +#![warn(clippy::blanket_clippy_restriction_lints)]
 +
 +//! Test that the whole restriction group is not enabled
 +#![warn(clippy::restriction)]
 +#![deny(clippy::restriction)]
 +#![forbid(clippy::restriction)]
 +
 +fn main() {}
index e83eb4d605aa73141f066b88c0de467dbbf02c9f,0000000000000000000000000000000000000000..2bf89ab69a40cdd75ed1bdc421858e722387ba9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,32 @@@
- error: restriction lints are not meant to be all enabled
-   --> $DIR/blanket_clippy_restriction_lints.rs:4:9
++error: `clippy::restriction` is not meant to be enabled as a group
++   |
++   = note: because of the command line `--warn clippy::restriction`
++   = help: enable the restriction lints you need individually
++   = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
++
++error: `clippy::restriction` is not meant to be enabled as a group
++  --> $DIR/blanket_clippy_restriction_lints.rs:6:9
 +   |
 +LL | #![warn(clippy::restriction)]
 +   |         ^^^^^^^^^^^^^^^^^^^
 +   |
-    = help: try enabling only the lints you really need
-    = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
++   = help: enable the restriction lints you need individually
 +
- error: restriction lints are not meant to be all enabled
-   --> $DIR/blanket_clippy_restriction_lints.rs:5:9
++error: `clippy::restriction` is not meant to be enabled as a group
++  --> $DIR/blanket_clippy_restriction_lints.rs:7:9
 +   |
 +LL | #![deny(clippy::restriction)]
 +   |         ^^^^^^^^^^^^^^^^^^^
 +   |
-    = help: try enabling only the lints you really need
++   = help: enable the restriction lints you need individually
 +
- error: restriction lints are not meant to be all enabled
-   --> $DIR/blanket_clippy_restriction_lints.rs:6:11
++error: `clippy::restriction` is not meant to be enabled as a group
++  --> $DIR/blanket_clippy_restriction_lints.rs:8:11
 +   |
 +LL | #![forbid(clippy::restriction)]
 +   |           ^^^^^^^^^^^^^^^^^^^
 +   |
-    = help: try enabling only the lints you really need
++   = help: enable the restriction lints you need individually
 +
- error: aborting due to 3 previous errors
++error: aborting due to 4 previous errors
 +
index 2c8339cdd7f8a0fa97d0c3c495fc9d90cfb611f3,0000000000000000000000000000000000000000..37d3e3286a4b899772341fedfa0b24638b140b2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,113 @@@
 +// run-rustfix
 +
++#![feature(let_chains)]
 +#![warn(clippy::bool_to_int_with_if)]
 +#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)]
 +
 +fn main() {
 +    let a = true;
 +    let b = false;
 +
 +    let x = 1;
 +    let y = 2;
 +
 +    // Should lint
 +    // precedence
 +    i32::from(a);
 +    i32::from(!a);
 +    i32::from(!a);
 +    i32::from(a || b);
 +    i32::from(cond(a, b));
 +    i32::from(x + y < 4);
 +
 +    // if else if
 +    if a {
 +        123
 +    } else { i32::from(b) };
 +
 +    // if else if inverted
 +    if a {
 +        123
 +    } else { i32::from(!b) };
 +
 +    // Shouldn't lint
 +
 +    if a {
 +        1
 +    } else if b {
 +        0
 +    } else {
 +        3
 +    };
 +
 +    if a {
 +        3
 +    } else if b {
 +        1
 +    } else {
 +        -2
 +    };
 +
 +    if a {
 +        3
 +    } else {
 +        0
 +    };
 +    if a {
 +        side_effect();
 +        1
 +    } else {
 +        0
 +    };
 +    if a {
 +        1
 +    } else {
 +        side_effect();
 +        0
 +    };
 +
 +    // multiple else ifs
 +    if a {
 +        123
 +    } else if b {
 +        1
 +    } else if a | b {
 +        0
 +    } else {
 +        123
 +    };
 +
++    pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 };
++
 +    some_fn(a);
 +}
 +
 +// Lint returns and type inference
 +fn some_fn(a: bool) -> u8 {
 +    u8::from(a)
 +}
 +
 +fn side_effect() {}
 +
 +fn cond(a: bool, b: bool) -> bool {
 +    a || b
 +}
++
++enum Enum {
++    A,
++    B,
++}
++
++fn if_let(a: Enum, b: Enum) {
++    if let Enum::A = a {
++        1
++    } else {
++        0
++    };
++
++    if let Enum::A = a && let Enum::B = b {
++        1
++    } else {
++        0
++    };
++}
index 5d9496f01775f84505c823fa594c21c43ae78b6b,0000000000000000000000000000000000000000..ebdf86fd185607529a6564951fee9a1ff284e60a
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,145 @@@
 +// run-rustfix
 +
++#![feature(let_chains)]
 +#![warn(clippy::bool_to_int_with_if)]
 +#![allow(unused, dead_code, clippy::unnecessary_operation, clippy::no_effect)]
 +
 +fn main() {
 +    let a = true;
 +    let b = false;
 +
 +    let x = 1;
 +    let y = 2;
 +
 +    // Should lint
 +    // precedence
 +    if a {
 +        1
 +    } else {
 +        0
 +    };
 +    if a {
 +        0
 +    } else {
 +        1
 +    };
 +    if !a {
 +        1
 +    } else {
 +        0
 +    };
 +    if a || b {
 +        1
 +    } else {
 +        0
 +    };
 +    if cond(a, b) {
 +        1
 +    } else {
 +        0
 +    };
 +    if x + y < 4 {
 +        1
 +    } else {
 +        0
 +    };
 +
 +    // if else if
 +    if a {
 +        123
 +    } else if b {
 +        1
 +    } else {
 +        0
 +    };
 +
 +    // if else if inverted
 +    if a {
 +        123
 +    } else if b {
 +        0
 +    } else {
 +        1
 +    };
 +
 +    // Shouldn't lint
 +
 +    if a {
 +        1
 +    } else if b {
 +        0
 +    } else {
 +        3
 +    };
 +
 +    if a {
 +        3
 +    } else if b {
 +        1
 +    } else {
 +        -2
 +    };
 +
 +    if a {
 +        3
 +    } else {
 +        0
 +    };
 +    if a {
 +        side_effect();
 +        1
 +    } else {
 +        0
 +    };
 +    if a {
 +        1
 +    } else {
 +        side_effect();
 +        0
 +    };
 +
 +    // multiple else ifs
 +    if a {
 +        123
 +    } else if b {
 +        1
 +    } else if a | b {
 +        0
 +    } else {
 +        123
 +    };
 +
++    pub const SHOULD_NOT_LINT: usize = if true { 1 } else { 0 };
++
 +    some_fn(a);
 +}
 +
 +// Lint returns and type inference
 +fn some_fn(a: bool) -> u8 {
 +    if a { 1 } else { 0 }
 +}
 +
 +fn side_effect() {}
 +
 +fn cond(a: bool, b: bool) -> bool {
 +    a || b
 +}
++
++enum Enum {
++    A,
++    B,
++}
++
++fn if_let(a: Enum, b: Enum) {
++    if let Enum::A = a {
++        1
++    } else {
++        0
++    };
++
++    if let Enum::A = a && let Enum::B = b {
++        1
++    } else {
++        0
++    };
++}
index 4cb5531bef6a1a7f7f24035a7bc2b0e9b38a11e8,0000000000000000000000000000000000000000..5cfb75cc0dfcb2bc92741849e88a96d27cc73a99
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,109 @@@
-   --> $DIR/bool_to_int_with_if.rs:15:5
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:20:5
++  --> $DIR/bool_to_int_with_if.rs:16:5
 +   |
 +LL | /     if a {
 +LL | |         1
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^ help: replace with from: `i32::from(a)`
 +   |
 +   = note: `a as i32` or `a.into()` can also be valid options
 +   = note: `-D clippy::bool-to-int-with-if` implied by `-D warnings`
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:25:5
++  --> $DIR/bool_to_int_with_if.rs:21:5
 +   |
 +LL | /     if a {
 +LL | |         0
 +LL | |     } else {
 +LL | |         1
 +LL | |     };
 +   | |_____^ help: replace with from: `i32::from(!a)`
 +   |
 +   = note: `!a as i32` or `(!a).into()` can also be valid options
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:30:5
++  --> $DIR/bool_to_int_with_if.rs:26:5
 +   |
 +LL | /     if !a {
 +LL | |         1
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^ help: replace with from: `i32::from(!a)`
 +   |
 +   = note: `!a as i32` or `(!a).into()` can also be valid options
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:35:5
++  --> $DIR/bool_to_int_with_if.rs:31:5
 +   |
 +LL | /     if a || b {
 +LL | |         1
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^ help: replace with from: `i32::from(a || b)`
 +   |
 +   = note: `(a || b) as i32` or `(a || b).into()` can also be valid options
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:40:5
++  --> $DIR/bool_to_int_with_if.rs:36:5
 +   |
 +LL | /     if cond(a, b) {
 +LL | |         1
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^ help: replace with from: `i32::from(cond(a, b))`
 +   |
 +   = note: `cond(a, b) as i32` or `cond(a, b).into()` can also be valid options
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:49:12
++  --> $DIR/bool_to_int_with_if.rs:41:5
 +   |
 +LL | /     if x + y < 4 {
 +LL | |         1
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^ help: replace with from: `i32::from(x + y < 4)`
 +   |
 +   = note: `(x + y < 4) as i32` or `(x + y < 4).into()` can also be valid options
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:58:12
++  --> $DIR/bool_to_int_with_if.rs:50:12
 +   |
 +LL |       } else if b {
 +   |  ____________^
 +LL | |         1
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^ help: replace with from: `{ i32::from(b) }`
 +   |
 +   = note: `b as i32` or `b.into()` can also be valid options
 +
 +error: boolean to int conversion using if
-   --> $DIR/bool_to_int_with_if.rs:116:5
++  --> $DIR/bool_to_int_with_if.rs:59:12
 +   |
 +LL |       } else if b {
 +   |  ____________^
 +LL | |         0
 +LL | |     } else {
 +LL | |         1
 +LL | |     };
 +   | |_____^ help: replace with from: `{ i32::from(!b) }`
 +   |
 +   = note: `!b as i32` or `(!b).into()` can also be valid options
 +
 +error: boolean to int conversion using if
++  --> $DIR/bool_to_int_with_if.rs:119:5
 +   |
 +LL |     if a { 1 } else { 0 }
 +   |     ^^^^^^^^^^^^^^^^^^^^^ help: replace with from: `u8::from(a)`
 +   |
 +   = note: `a as u8` or `a.into()` can also be valid options
 +
 +error: aborting due to 9 previous errors
 +
index 912e6788afddcacce8f7f3b2887ebe54ca85377a,0000000000000000000000000000000000000000..07bdaff00dc417766306a171f586c78f4d0faffb
mode 100644,000000..100644
--- /dev/null
@@@ -1,395 -1,0 +1,411 @@@
 +#![allow(clippy::all)]
 +#![warn(clippy::cognitive_complexity)]
 +#![allow(unused, unused_crate_dependencies)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +    if true {
 +        println!("a");
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn kaboom() {
 +    let n = 0;
 +    'a: for i in 0..20 {
 +        'b: for j in i..20 {
 +            for k in j..20 {
 +                if k == 5 {
 +                    break 'b;
 +                }
 +                if j == 3 && k == 6 {
 +                    continue 'a;
 +                }
 +                if k == j {
 +                    continue;
 +                }
 +                println!("bake");
 +            }
 +        }
 +        println!("cake");
 +    }
 +}
 +
 +fn bloo() {
 +    match 42 {
 +        0 => println!("hi"),
 +        1 => println!("hai"),
 +        2 => println!("hey"),
 +        3 => println!("hallo"),
 +        4 => println!("hello"),
 +        5 => println!("salut"),
 +        6 => println!("good morning"),
 +        7 => println!("good evening"),
 +        8 => println!("good afternoon"),
 +        9 => println!("good night"),
 +        10 => println!("bonjour"),
 +        11 => println!("hej"),
 +        12 => println!("hej hej"),
 +        13 => println!("greetings earthling"),
 +        14 => println!("take us to you leader"),
 +        15 | 17 | 19 | 21 | 23 | 25 | 27 | 29 | 31 | 33 => println!("take us to you leader"),
 +        35 | 37 | 39 | 41 | 43 | 45 | 47 | 49 | 51 | 53 => println!("there is no undefined behavior"),
 +        55 | 57 | 59 | 61 | 63 | 65 | 67 | 69 | 71 | 73 => println!("I know borrow-fu"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +// Short circuiting operations don't increase the complexity of a function.
 +// Note that the minimum complexity of a function is 1.
 +#[clippy::cognitive_complexity = "1"]
 +fn lots_of_short_circuits() -> bool {
 +    true && false && true && false && true && false && true
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn lots_of_short_circuits2() -> bool {
 +    true || false || true || false || true || false || true
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn baa() {
 +    let x = || match 99 {
 +        0 => 0,
 +        1 => 1,
 +        2 => 2,
 +        4 => 4,
 +        6 => 6,
 +        9 => 9,
 +        _ => 42,
 +    };
 +    if x() == 42 {
 +        println!("x");
 +    } else {
 +        println!("not x");
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn bar() {
 +    match 99 {
 +        0 => println!("hi"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[test]
 +#[clippy::cognitive_complexity = "1"]
 +/// Tests are usually complex but simple at the same time. `clippy::cognitive_complexity` used to
 +/// give lots of false-positives in tests.
 +fn dont_warn_on_tests() {
 +    match 99 {
 +        0 => println!("hi"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn barr() {
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => println!("bla"),
 +        2 | 3 => println!("blub"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn barr2() {
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => println!("bla"),
 +        2 | 3 => println!("blub"),
 +        _ => println!("bye"),
 +    }
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => println!("bla"),
 +        2 | 3 => println!("blub"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn barrr() {
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => panic!("bla"),
 +        2 | 3 => println!("blub"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn barrr2() {
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => panic!("bla"),
 +        2 | 3 => println!("blub"),
 +        _ => println!("bye"),
 +    }
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => panic!("bla"),
 +        2 | 3 => println!("blub"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn barrrr() {
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => println!("bla"),
 +        2 | 3 => panic!("blub"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn barrrr2() {
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => println!("bla"),
 +        2 | 3 => panic!("blub"),
 +        _ => println!("bye"),
 +    }
 +    match 99 {
 +        0 => println!("hi"),
 +        1 => println!("bla"),
 +        2 | 3 => panic!("blub"),
 +        _ => println!("bye"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn cake() {
 +    if 4 == 5 {
 +        println!("yea");
 +    } else {
 +        panic!("meh");
 +    }
 +    println!("whee");
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +pub fn read_file(input_path: &str) -> String {
 +    use std::fs::File;
 +    use std::io::{Read, Write};
 +    use std::path::Path;
 +    let mut file = match File::open(&Path::new(input_path)) {
 +        Ok(f) => f,
 +        Err(err) => {
 +            panic!("Can't open {}: {}", input_path, err);
 +        },
 +    };
 +
 +    let mut bytes = Vec::new();
 +
 +    match file.read_to_end(&mut bytes) {
 +        Ok(..) => {},
 +        Err(_) => {
 +            panic!("Can't read {}", input_path);
 +        },
 +    };
 +
 +    match String::from_utf8(bytes) {
 +        Ok(contents) => contents,
 +        Err(_) => {
 +            panic!("{} is not UTF-8 encoded", input_path);
 +        },
 +    }
 +}
 +
 +enum Void {}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn void(void: Void) {
 +    if true {
 +        match void {}
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn mcarton_sees_all() {
 +    panic!("meh");
 +    panic!("möh");
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn try_() -> Result<i32, &'static str> {
 +    match 5 {
 +        5 => Ok(5),
 +        _ => return Err("bla"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn try_again() -> Result<i32, &'static str> {
 +    let _ = Ok(42)?;
 +    let _ = Ok(43)?;
 +    let _ = Ok(44)?;
 +    let _ = Ok(45)?;
 +    let _ = Ok(46)?;
 +    let _ = Ok(47)?;
 +    let _ = Ok(48)?;
 +    let _ = Ok(49)?;
 +    match 5 {
 +        5 => Ok(5),
 +        _ => return Err("bla"),
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn early() -> Result<i32, &'static str> {
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +    return Ok(5);
 +}
 +
 +#[rustfmt::skip]
 +#[clippy::cognitive_complexity = "1"]
 +fn early_ret() -> i32 {
 +    let a = if true { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    let a = if a < 99 { 42 } else { return 0; };
 +    match 5 {
 +        5 => 5,
 +        _ => return 6,
 +    }
 +}
 +
 +#[clippy::cognitive_complexity = "1"]
 +fn closures() {
 +    let x = |a: i32, b: i32| -> i32 {
 +        if true {
 +            println!("moo");
 +        }
 +
 +        a + b
 +    };
 +}
 +
 +struct Moo;
 +
 +#[clippy::cognitive_complexity = "1"]
 +impl Moo {
 +    fn moo(&self) {
 +        if true {
 +            println!("moo");
 +        }
 +    }
 +}
++
++#[clippy::cognitive_complexity = "1"]
++mod issue9300 {
++    async fn a() {
++        let a = 0;
++        if a == 0 {}
++    }
++
++    pub struct S;
++    impl S {
++        pub async fn async_method() {
++            let a = 0;
++            if a == 0 {}
++        }
++    }
++}
index d7f2f24e52f2b54663a2059f1ee3fedde769707f,0000000000000000000000000000000000000000..5824631fa83baabe42b09b6a0ba55995ed56ad53
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,155 @@@
- error: aborting due to 17 previous errors
 +error: the function has a cognitive complexity of (28/25)
 +  --> $DIR/cognitive_complexity.rs:6:4
 +   |
 +LL | fn main() {
 +   |    ^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +   = note: `-D clippy::cognitive-complexity` implied by `-D warnings`
 +
 +error: the function has a cognitive complexity of (7/1)
 +  --> $DIR/cognitive_complexity.rs:91:4
 +   |
 +LL | fn kaboom() {
 +   |    ^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:149:4
 +   |
 +LL | fn baa() {
 +   |    ^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:150:13
 +   |
 +LL |     let x = || match 99 {
 +   |             ^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:167:4
 +   |
 +LL | fn bar() {
 +   |    ^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:186:4
 +   |
 +LL | fn barr() {
 +   |    ^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (3/1)
 +  --> $DIR/cognitive_complexity.rs:196:4
 +   |
 +LL | fn barr2() {
 +   |    ^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:212:4
 +   |
 +LL | fn barrr() {
 +   |    ^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (3/1)
 +  --> $DIR/cognitive_complexity.rs:222:4
 +   |
 +LL | fn barrr2() {
 +   |    ^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:238:4
 +   |
 +LL | fn barrrr() {
 +   |    ^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (3/1)
 +  --> $DIR/cognitive_complexity.rs:248:4
 +   |
 +LL | fn barrrr2() {
 +   |    ^^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:264:4
 +   |
 +LL | fn cake() {
 +   |    ^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (4/1)
 +  --> $DIR/cognitive_complexity.rs:274:8
 +   |
 +LL | pub fn read_file(input_path: &str) -> String {
 +   |        ^^^^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:305:4
 +   |
 +LL | fn void(void: Void) {
 +   |    ^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (8/1)
 +  --> $DIR/cognitive_complexity.rs:356:4
 +   |
 +LL | fn early_ret() -> i32 {
 +   |    ^^^^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:377:13
 +   |
 +LL |     let x = |a: i32, b: i32| -> i32 {
 +   |             ^^^^^^^^^^^^^^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
 +error: the function has a cognitive complexity of (2/1)
 +  --> $DIR/cognitive_complexity.rs:390:8
 +   |
 +LL |     fn moo(&self) {
 +   |        ^^^
 +   |
 +   = help: you could split it up into multiple smaller functions
 +
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:399:14
++   |
++LL |     async fn a() {
++   |              ^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: the function has a cognitive complexity of (2/1)
++  --> $DIR/cognitive_complexity.rs:406:22
++   |
++LL |         pub async fn async_method() {
++   |                      ^^^^^^^^^^^^
++   |
++   = help: you could split it up into multiple smaller functions
++
++error: aborting due to 19 previous errors
 +
index 0c2d48f938fcbe516a7ab5c3b12893c36f78df02,0000000000000000000000000000000000000000..1f26c7f4db65778f29be10a4d075ee83d24d6bb5
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,10 @@@
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a
 +  --> $DIR/ice-2774.rs:15:1
 +   |
 +LL | pub fn add_barfoos_to_foos<'a>(bars: &HashSet<&'a Bar>) {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
 +
 +error: aborting due to previous error
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbd373c70bf285129cc072e8bac011d4e39b1d1d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++//! <https://github.com/rust-lang/rust-clippy/issues/9746#issuecomment-1297132880>
++
++trait Trait {}
++
++struct Struct<'a> {
++    _inner: &'a Struct<'a>,
++}
++
++impl Trait for Struct<'_> {}
++
++fn example<'a>(s: &'a Struct) -> Box<Box<dyn Trait + 'a>> {
++    Box::new(Box::new(Struct { _inner: s }))
++}
++
++fn main() {}
index d68bbe78802136ab5b103f95955729e34c0ffafb,0000000000000000000000000000000000000000..875d5ab4f21cabe8126bf60a396d4e0ad86cc66f
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,14 @@@
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a
 +  --> $DIR/needless_lifetimes_impl_trait.rs:15:5
 +   |
 +LL |     fn baz<'a>(&'a self) -> impl Foo + 'a {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/needless_lifetimes_impl_trait.rs:1:9
 +   |
 +LL | #![deny(clippy::needless_lifetimes)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to previous error
 +
index 4a5c597dda51fa3304afafe4174630eb62655db2,0000000000000000000000000000000000000000..e1c9fe30a9dfbec06b9aa1bc545df8b059ad1de9
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,32 @@@
- // ignore-windows
 +// compile-flags: -Clink-arg=-nostartfiles
 +// ignore-macos
 +
 +#![feature(lang_items, start, libc)]
 +#![no_std]
 +
 +use core::panic::PanicInfo;
 +use core::sync::atomic::{AtomicUsize, Ordering};
 +
 +static N: AtomicUsize = AtomicUsize::new(0);
 +
 +#[warn(clippy::main_recursion)]
 +#[start]
 +fn main(_argc: isize, _argv: *const *const u8) -> isize {
 +    let x = N.load(Ordering::Relaxed);
 +    N.store(x + 1, Ordering::Relaxed);
 +
 +    if x < 3 {
 +        main(_argc, _argv);
 +    }
 +
 +    0
 +}
 +
 +#[allow(clippy::empty_loop)]
 +#[panic_handler]
 +fn panic(_info: &PanicInfo) -> ! {
 +    loop {}
 +}
 +
 +#[lang = "eh_personality"]
 +extern "C" fn eh_personality() {}
index c7b616e2897087c01ee59976e37fe287afb6dbbc,0000000000000000000000000000000000000000..d74f2dbfe1baa1b463afec53091b8ac01b815511
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,46 @@@
- LL | / pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
- LL | |     unimplemented!();
- LL | | }
-    | |_^
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:7:1
 +   |
- LL | / pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
- LL | |     unimplemented!();
- LL | | }
-    | |_^
++LL | pub fn pub_fn_missing_errors_header() -> Result<(), ()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::missing-errors-doc` implied by `-D warnings`
 +
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:11:1
 +   |
- LL | / pub fn pub_fn_returning_io_result() -> io::Result<()> {
- LL | |     unimplemented!();
- LL | | }
-    | |_^
++LL | pub async fn async_pub_fn_missing_errors_header() -> Result<(), ()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:16:1
 +   |
- LL | / pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
- LL | |     unimplemented!();
- LL | | }
-    | |_^
++LL | pub fn pub_fn_returning_io_result() -> io::Result<()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:21:1
 +   |
- LL | /     pub fn pub_method_missing_errors_header() -> Result<(), ()> {
- LL | |         unimplemented!();
- LL | |     }
-    | |_____^
++LL | pub async fn async_pub_fn_returning_io_result() -> io::Result<()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:51:5
 +   |
- LL | /     pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
- LL | |         unimplemented!();
- LL | |     }
-    | |_____^
++LL |     pub fn pub_method_missing_errors_header() -> Result<(), ()> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:56:5
 +   |
++LL |     pub async fn async_pub_method_missing_errors_header() -> Result<(), ()> {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for function returning `Result` missing `# Errors` section
 +  --> $DIR/doc_errors.rs:85:5
 +   |
 +LL |     fn trait_method_missing_errors_header() -> Result<(), ()>;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 7 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d9e9363b0f4bdd17540aaa9355e67fde5b37553d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,148 @@@
++// aux-build:doc_unsafe_macros.rs
++
++#![allow(clippy::let_unit_value)]
++
++#[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..83b2efbb346be5e7207da88cccb5b08737e8a409
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/doc_unnecessary_unsafe.rs:18: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/doc_unnecessary_unsafe.rs:44:5
++   |
++LL |     pub fn republished() {
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/doc_unnecessary_unsafe.rs:57:5
++   |
++LL |     fn documented(self);
++   |     ^^^^^^^^^^^^^^^^^^^^
++
++error: docs for safe trait have unnecessary `# Safety` section
++  --> $DIR/doc_unnecessary_unsafe.rs:67:1
++   |
++LL | pub trait DocumentedSafeTrait {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/doc_unnecessary_unsafe.rs:95:5
++   |
++LL |     pub fn documented() -> Self {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: safe function's docs have unnecessary `# Safety` section
++  --> $DIR/doc_unnecessary_unsafe.rs:122: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/doc_unnecessary_unsafe.rs:146:1
++   |
++LL | pub trait DocumentedSafeTraitWithImplementationHeader {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 7 previous errors
++
index 904b88eaef62280a1a56991466904e7afd57ef9c,0000000000000000000000000000000000000000..a86e191370e3e95f5e62ad2ccccedbbd87f544c3
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,45 @@@
- LL | / pub unsafe fn destroy_the_planet() {
- LL | |     unimplemented!();
- LL | | }
-    | |_^
 +error: unsafe function's docs miss `# Safety` section
 +  --> $DIR/doc_unsafe.rs:9:1
 +   |
- LL | /     pub unsafe fn republished() {
- LL | |         unimplemented!();
- LL | |     }
-    | |_____^
++LL | pub unsafe fn destroy_the_planet() {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::missing-safety-doc` implied by `-D warnings`
 +
 +error: unsafe function's docs miss `# Safety` section
 +  --> $DIR/doc_unsafe.rs:32:5
 +   |
- LL | / pub unsafe trait UnsafeTrait {
- LL | |     fn method();
- LL | | }
-    | |_^
++LL |     pub unsafe fn republished() {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: unsafe function's docs miss `# Safety` section
 +  --> $DIR/doc_unsafe.rs:40:5
 +   |
 +LL |     unsafe fn woefully_underdocumented(self);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for unsafe trait missing `# Safety` section
 +  --> $DIR/doc_unsafe.rs:46:1
 +   |
- LL | /     pub unsafe fn more_undocumented_unsafe() -> Self {
- LL | |         unimplemented!();
- LL | |     }
-    | |_____^
++LL | pub unsafe trait UnsafeTrait {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: unsafe function's docs miss `# Safety` section
 +  --> $DIR/doc_unsafe.rs:76:5
 +   |
- LL | /         pub unsafe fn whee() {
- LL | |             unimplemented!()
- LL | |         }
-    | |_________^
++LL |     pub unsafe fn more_undocumented_unsafe() -> Self {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: unsafe function's docs miss `# Safety` section
 +  --> $DIR/doc_unsafe.rs:92:9
 +   |
- LL |   very_unsafe!();
-    |   -------------- in this macro invocation
++LL |         pub unsafe fn whee() {
++   |         ^^^^^^^^^^^^^^^^^^^^
 +...
++LL | very_unsafe!();
++   | -------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `very_unsafe` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: aborting due to 6 previous errors
 +
index 422f9486503d2396a484653531936afe7aa18814,0000000000000000000000000000000000000000..e737955026523f973ba31a9f74c5f0f54d6c7ff9
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,109 @@@
 +// compile-flags: --test
 +
 +#![warn(clippy::eq_op)]
 +#![allow(clippy::double_parens, clippy::identity_op, clippy::nonminimal_bool)]
++#![allow(clippy::suspicious_xor_used_as_pow)]
 +
 +fn main() {
 +    // simple values and comparisons
 +    let _ = 1 == 1;
 +    let _ = "no" == "no";
 +    // even though I agree that no means no ;-)
 +    let _ = false != false;
 +    let _ = 1.5 < 1.5;
 +    let _ = 1u64 >= 1u64;
 +
 +    // casts, methods, parentheses
 +    let _ = (1u32 as u64) & (1u32 as u64);
 +    #[rustfmt::skip]
 +    {
 +        let _ = 1 ^ ((((((1))))));
 +    };
 +
 +    // unary and binary operators
 +    let _ = (-(2) < -(2));
 +    let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
 +    let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
 +
 +    // various other things
 +    let _ = ([1] != [1]);
 +    let _ = ((1, 2) != (1, 2));
 +    let _ = vec![1, 2, 3] == vec![1, 2, 3]; //no error yet, as we don't match macros
 +
 +    // const folding
 +    let _ = 1 + 1 == 2;
 +    let _ = 1 - 1 == 0;
 +
 +    let _ = 1 - 1;
 +    let _ = 1 / 1;
 +    let _ = true && true;
 +
 +    let _ = true || true;
 +
 +    let a: u32 = 0;
 +    let b: u32 = 0;
 +
 +    let _ = a == b && b == a;
 +    let _ = a != b && b != a;
 +    let _ = a < b && b > a;
 +    let _ = a <= b && b >= a;
 +
 +    let mut a = vec![1];
 +    let _ = a == a;
 +    let _ = 2 * a.len() == 2 * a.len(); // ok, functions
 +    let _ = a.pop() == a.pop(); // ok, functions
 +
 +    check_ignore_macro();
 +
 +    // named constants
 +    const A: u32 = 10;
 +    const B: u32 = 10;
 +    const C: u32 = A / B; // ok, different named constants
 +    const D: u32 = A / A;
 +}
 +
 +macro_rules! check_if_named_foo {
 +    ($expression:expr) => {
 +        if stringify!($expression) == "foo" {
 +            println!("foo!");
 +        } else {
 +            println!("not foo.");
 +        }
 +    };
 +}
 +
 +macro_rules! bool_macro {
 +    ($expression:expr) => {
 +        true
 +    };
 +}
 +
 +fn check_ignore_macro() {
 +    check_if_named_foo!(foo);
 +    // checks if the lint ignores macros with `!` operator
 +    let _ = !bool_macro!(1) && !bool_macro!("");
 +}
 +
 +struct Nested {
 +    inner: ((i32,), (i32,), (i32,)),
 +}
 +
 +fn check_nested(n1: &Nested, n2: &Nested) -> bool {
 +    // `n2.inner.0.0` mistyped as `n1.inner.0.0`
 +    (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
 +}
 +
 +#[test]
 +fn eq_op_shouldnt_trigger_in_tests() {
 +    let a = 1;
 +    let result = a + 1 == 1 + a;
 +    assert!(result);
 +}
 +
 +#[test]
 +fn eq_op_macros_shouldnt_trigger_in_tests() {
 +    let a = 1;
 +    let b = 2;
 +    assert_eq!(a, a);
 +    assert_eq!(a + b, b + a);
 +}
index 313ceed2b41facad2b115d1b36d0d77bbb4693a2,0000000000000000000000000000000000000000..d365ab27edc28d5d7ebbe85836dfcf5a2fe8bf11
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,172 @@@
-   --> $DIR/eq_op.rs:8:13
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:9:13
++  --> $DIR/eq_op.rs:9:13
 +   |
 +LL |     let _ = 1 == 1;
 +   |             ^^^^^^
 +   |
 +   = note: `-D clippy::eq-op` implied by `-D warnings`
 +
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:11:13
++  --> $DIR/eq_op.rs:10:13
 +   |
 +LL |     let _ = "no" == "no";
 +   |             ^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `!=`
-   --> $DIR/eq_op.rs:12:13
++  --> $DIR/eq_op.rs:12:13
 +   |
 +LL |     let _ = false != false;
 +   |             ^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `<`
-   --> $DIR/eq_op.rs:13:13
++  --> $DIR/eq_op.rs:13:13
 +   |
 +LL |     let _ = 1.5 < 1.5;
 +   |             ^^^^^^^^^
 +
 +error: equal expressions as operands to `>=`
-   --> $DIR/eq_op.rs:16:13
++  --> $DIR/eq_op.rs:14:13
 +   |
 +LL |     let _ = 1u64 >= 1u64;
 +   |             ^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&`
-   --> $DIR/eq_op.rs:19:17
++  --> $DIR/eq_op.rs:17:13
 +   |
 +LL |     let _ = (1u32 as u64) & (1u32 as u64);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `^`
-   --> $DIR/eq_op.rs:23:13
++  --> $DIR/eq_op.rs:20:17
 +   |
 +LL |         let _ = 1 ^ ((((((1))))));
 +   |                 ^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `<`
-   --> $DIR/eq_op.rs:24:13
++  --> $DIR/eq_op.rs:24:13
 +   |
 +LL |     let _ = (-(2) < -(2));
 +   |             ^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:24:14
++  --> $DIR/eq_op.rs:25:13
 +   |
 +LL |     let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&`
-   --> $DIR/eq_op.rs:24:35
++  --> $DIR/eq_op.rs:25:14
 +   |
 +LL |     let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
 +   |              ^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&`
-   --> $DIR/eq_op.rs:25:13
++  --> $DIR/eq_op.rs:25:35
 +   |
 +LL |     let _ = ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
 +   |                                   ^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:28:13
++  --> $DIR/eq_op.rs:26:13
 +   |
 +LL |     let _ = (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `!=`
-   --> $DIR/eq_op.rs:29:13
++  --> $DIR/eq_op.rs:29:13
 +   |
 +LL |     let _ = ([1] != [1]);
 +   |             ^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `!=`
-   --> $DIR/eq_op.rs:33:13
++  --> $DIR/eq_op.rs:30:13
 +   |
 +LL |     let _ = ((1, 2) != (1, 2));
 +   |             ^^^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:34:13
++  --> $DIR/eq_op.rs:34:13
 +   |
 +LL |     let _ = 1 + 1 == 2;
 +   |             ^^^^^^^^^^
 +
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:34:13
++  --> $DIR/eq_op.rs:35:13
 +   |
 +LL |     let _ = 1 - 1 == 0;
 +   |             ^^^^^^^^^^
 +
 +error: equal expressions as operands to `-`
-   --> $DIR/eq_op.rs:36:13
++  --> $DIR/eq_op.rs:35:13
 +   |
 +LL |     let _ = 1 - 1 == 0;
 +   |             ^^^^^
 +
 +error: equal expressions as operands to `-`
-   --> $DIR/eq_op.rs:37:13
++  --> $DIR/eq_op.rs:37:13
 +   |
 +LL |     let _ = 1 - 1;
 +   |             ^^^^^
 +
 +error: equal expressions as operands to `/`
-   --> $DIR/eq_op.rs:38:13
++  --> $DIR/eq_op.rs:38:13
 +   |
 +LL |     let _ = 1 / 1;
 +   |             ^^^^^
 +
 +error: equal expressions as operands to `&&`
-   --> $DIR/eq_op.rs:40:13
++  --> $DIR/eq_op.rs:39:13
 +   |
 +LL |     let _ = true && true;
 +   |             ^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `||`
-   --> $DIR/eq_op.rs:45:13
++  --> $DIR/eq_op.rs:41:13
 +   |
 +LL |     let _ = true || true;
 +   |             ^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&&`
-   --> $DIR/eq_op.rs:46:13
++  --> $DIR/eq_op.rs:46:13
 +   |
 +LL |     let _ = a == b && b == a;
 +   |             ^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&&`
-   --> $DIR/eq_op.rs:47:13
++  --> $DIR/eq_op.rs:47:13
 +   |
 +LL |     let _ = a != b && b != a;
 +   |             ^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&&`
-   --> $DIR/eq_op.rs:48:13
++  --> $DIR/eq_op.rs:48:13
 +   |
 +LL |     let _ = a < b && b > a;
 +   |             ^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `&&`
-   --> $DIR/eq_op.rs:51:13
++  --> $DIR/eq_op.rs:49:13
 +   |
 +LL |     let _ = a <= b && b >= a;
 +   |             ^^^^^^^^^^^^^^^^
 +
 +error: equal expressions as operands to `==`
-   --> $DIR/eq_op.rs:61:20
++  --> $DIR/eq_op.rs:52:13
 +   |
 +LL |     let _ = a == a;
 +   |             ^^^^^^
 +
 +error: equal expressions as operands to `/`
-   --> $DIR/eq_op.rs:92:5
++  --> $DIR/eq_op.rs:62:20
 +   |
 +LL |     const D: u32 = A / A;
 +   |                    ^^^^^
 +
 +error: equal expressions as operands to `==`
++  --> $DIR/eq_op.rs:93:5
 +   |
 +LL |     (n1.inner.0).0 == (n1.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 28 previous errors
 +
index 687efdada6e311f4efdf629ebfd0b46ac3bfa388,0000000000000000000000000000000000000000..9af2ba9627200e69d1840214c16f132ec492c729
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,91 @@@
-     if let NotPartialEq::A = f {}
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
 +#![warn(clippy::equatable_if_let)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +use std::cmp::Ordering;
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    TupleVariant(i32, u64),
 +    RecordVariant { a: i64, b: u32 },
 +    UnitVariant,
 +    Recursive(Struct),
 +}
 +
 +#[derive(PartialEq)]
 +struct Struct {
 +    a: i32,
 +    b: bool,
 +}
 +
++struct NoPartialEqStruct {
++    a: i32,
++    b: bool,
++}
++
 +enum NotPartialEq {
 +    A,
 +    B,
 +}
 +
 +enum NotStructuralEq {
 +    A,
 +    B,
 +}
 +
 +impl PartialEq for NotStructuralEq {
 +    fn eq(&self, _: &NotStructuralEq) -> bool {
 +        false
 +    }
 +}
 +
 +fn main() {
 +    let a = 2;
 +    let b = 3;
 +    let c = Some(2);
 +    let d = Struct { a: 2, b: false };
 +    let e = Enum::UnitVariant;
 +    let f = NotPartialEq::A;
 +    let g = NotStructuralEq::A;
++    let h = NoPartialEqStruct { a: 2, b: false };
 +
 +    // true
 +
 +    if a == 2 {}
 +    if a.cmp(&b) == Ordering::Greater {}
 +    if c == Some(2) {}
 +    if d == (Struct { a: 2, b: false }) {}
 +    if e == Enum::TupleVariant(32, 64) {}
 +    if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
 +    if e == Enum::UnitVariant {}
 +    if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
 +
 +    // false
 +
 +    if let 2 | 3 = a {}
 +    if let x @ 2 = a {}
 +    if let Some(3 | 4) = c {}
 +    if let Struct { a, b: false } = d {}
 +    if let Struct { a: 2, b: x } = d {}
-     if let Some(NotPartialEq::A) = Some(f) {}
++    if matches!(f, NotPartialEq::A) {}
 +    if g == NotStructuralEq::A {}
++    if matches!(Some(f), Some(NotPartialEq::A)) {}
 +    if Some(g) == Some(NotStructuralEq::A) {}
++    if matches!(h, NoPartialEqStruct { a: 2, b: false }) {}
 +
 +    macro_rules! m1 {
 +        (x) => {
 +            "abc"
 +        };
 +    }
 +    if "abc" == m1!(x) {
 +        println!("OK");
 +    }
 +
 +    equatable_if_let!(a);
 +}
index 8c467d14d2a9fb8028406835235a34483ec02c47,0000000000000000000000000000000000000000..c3626c081dd5e37d9c9ebc5522340473562d9954
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,91 @@@
 +// run-rustfix
 +// aux-build:macro_rules.rs
 +
 +#![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
 +#![warn(clippy::equatable_if_let)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +use std::cmp::Ordering;
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    TupleVariant(i32, u64),
 +    RecordVariant { a: i64, b: u32 },
 +    UnitVariant,
 +    Recursive(Struct),
 +}
 +
 +#[derive(PartialEq)]
 +struct Struct {
 +    a: i32,
 +    b: bool,
 +}
 +
++struct NoPartialEqStruct {
++    a: i32,
++    b: bool,
++}
++
 +enum NotPartialEq {
 +    A,
 +    B,
 +}
 +
 +enum NotStructuralEq {
 +    A,
 +    B,
 +}
 +
 +impl PartialEq for NotStructuralEq {
 +    fn eq(&self, _: &NotStructuralEq) -> bool {
 +        false
 +    }
 +}
 +
 +fn main() {
 +    let a = 2;
 +    let b = 3;
 +    let c = Some(2);
 +    let d = Struct { a: 2, b: false };
 +    let e = Enum::UnitVariant;
 +    let f = NotPartialEq::A;
 +    let g = NotStructuralEq::A;
++    let h = NoPartialEqStruct { a: 2, b: false };
 +
 +    // true
 +
 +    if let 2 = a {}
 +    if let Ordering::Greater = a.cmp(&b) {}
 +    if let Some(2) = c {}
 +    if let Struct { a: 2, b: false } = d {}
 +    if let Enum::TupleVariant(32, 64) = e {}
 +    if let Enum::RecordVariant { a: 64, b: 32 } = e {}
 +    if let Enum::UnitVariant = e {}
 +    if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
 +
 +    // false
 +
 +    if let 2 | 3 = a {}
 +    if let x @ 2 = a {}
 +    if let Some(3 | 4) = c {}
 +    if let Struct { a, b: false } = d {}
 +    if let Struct { a: 2, b: x } = d {}
 +    if let NotPartialEq::A = f {}
 +    if let NotStructuralEq::A = g {}
 +    if let Some(NotPartialEq::A) = Some(f) {}
 +    if let Some(NotStructuralEq::A) = Some(g) {}
++    if let NoPartialEqStruct { a: 2, b: false } = h {}
 +
 +    macro_rules! m1 {
 +        (x) => {
 +            "abc"
 +        };
 +    }
 +    if let m1!(x) = "abc" {
 +        println!("OK");
 +    }
 +
 +    equatable_if_let!(a);
 +}
index 9c4c3cc3682e631d7930d7c9b265b6ebab8afed0,0000000000000000000000000000000000000000..40ca75b8da22cbdf0e1f03bd6635604706baf0c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,88 @@@
-   --> $DIR/equatable_if_let.rs:53:8
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:54:8
++  --> $DIR/equatable_if_let.rs:59:8
 +   |
 +LL |     if let 2 = a {}
 +   |        ^^^^^^^^^ help: try: `a == 2`
 +   |
 +   = note: `-D clippy::equatable-if-let` implied by `-D warnings`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:55:8
++  --> $DIR/equatable_if_let.rs:60:8
 +   |
 +LL |     if let Ordering::Greater = a.cmp(&b) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:56:8
++  --> $DIR/equatable_if_let.rs:61:8
 +   |
 +LL |     if let Some(2) = c {}
 +   |        ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:57:8
++  --> $DIR/equatable_if_let.rs:62:8
 +   |
 +LL |     if let Struct { a: 2, b: false } = d {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:58:8
++  --> $DIR/equatable_if_let.rs:63:8
 +   |
 +LL |     if let Enum::TupleVariant(32, 64) = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:59:8
++  --> $DIR/equatable_if_let.rs:64:8
 +   |
 +LL |     if let Enum::RecordVariant { a: 64, b: 32 } = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:60:8
++  --> $DIR/equatable_if_let.rs:65:8
 +   |
 +LL |     if let Enum::UnitVariant = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
 +
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:70:8
++  --> $DIR/equatable_if_let.rs:66:8
 +   |
 +LL |     if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
 +
++error: this pattern matching can be expressed using `matches!`
++  --> $DIR/equatable_if_let.rs:75:8
++   |
++LL |     if let NotPartialEq::A = f {}
++   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(f, NotPartialEq::A)`
++
 +error: this pattern matching can be expressed using equality
-   --> $DIR/equatable_if_let.rs:72:8
++  --> $DIR/equatable_if_let.rs:76:8
 +   |
 +LL |     if let NotStructuralEq::A = g {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
 +
++error: this pattern matching can be expressed using `matches!`
++  --> $DIR/equatable_if_let.rs:77:8
++   |
++LL |     if let Some(NotPartialEq::A) = Some(f) {}
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(Some(f), Some(NotPartialEq::A))`
++
 +error: this pattern matching can be expressed using equality
- error: this pattern matching can be expressed using equality
++  --> $DIR/equatable_if_let.rs:78:8
 +   |
 +LL |     if let Some(NotStructuralEq::A) = Some(g) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
 +
- error: aborting due to 11 previous errors
++error: this pattern matching can be expressed using `matches!`
 +  --> $DIR/equatable_if_let.rs:79:8
 +   |
++LL |     if let NoPartialEqStruct { a: 2, b: false } = h {}
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `matches!(h, NoPartialEqStruct { a: 2, b: false })`
++
++error: this pattern matching can be expressed using equality
++  --> $DIR/equatable_if_let.rs:86:8
++   |
 +LL |     if let m1!(x) = "abc" {
 +   |        ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
 +
++error: aborting due to 14 previous errors
 +
index f6738865cac12d9ef6f9750c7b2d7e989902c5e6,0000000000000000000000000000000000000000..c08e0dbbf74495d43b536e9ef7910b5cb1194241
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
- error: used `expect()` on `an Option` value
++error: used `expect()` on an `Option` value
 +  --> $DIR/expect.rs:5:13
 +   |
 +LL |     let _ = opt.expect("");
 +   |             ^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is `None`, it will panic
 +   = note: `-D clippy::expect-used` implied by `-D warnings`
 +
- error: used `expect()` on `a Result` value
++error: used `expect()` on a `Result` value
 +  --> $DIR/expect.rs:10:13
 +   |
 +LL |     let _ = res.expect("");
 +   |             ^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
- error: used `expect_err()` on `a Result` value
++error: used `expect_err()` on a `Result` value
 +  --> $DIR/expect.rs:11:13
 +   |
 +LL |     let _ = res.expect_err("");
 +   |             ^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Ok`, it will panic
 +
 +error: aborting due to 3 previous errors
 +
index d1d35e5c0eb46f493aaa3c7b07fdda965be3bef4,0000000000000000000000000000000000000000..59ff5e4040a3d6b67443d71ea52f6f8b1eb13ba4
mode 100644,000000..100644
--- /dev/null
@@@ -1,269 -1,0 +1,280 @@@
 +// 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());
 +}
index deedafad153b97ae0edd6d777637516d65c33410,0000000000000000000000000000000000000000..bcfb60c32788642fafe8e4acedbf326e2ee19c06
mode 100644,000000..100644
--- /dev/null
@@@ -1,269 -1,0 +1,280 @@@
 +// 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());
 +}
index f805bcc9ba8af503140bf2cff10433824147971b,0000000000000000000000000000000000000000..f53e531629aaef67e74b66ae674e4015f78b7061
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,53 @@@
 +#![warn(clippy::fn_params_excessive_bools)]
 +#![allow(clippy::too_many_arguments)]
 +
 +extern "C" {
++    // Should not lint, most of the time users have no control over extern function signatures
 +    fn f(_: bool, _: bool, _: bool, _: bool);
 +}
 +
 +macro_rules! foo {
 +    () => {
 +        fn fff(_: bool, _: bool, _: bool, _: bool) {}
 +    };
 +}
 +
 +foo!();
 +
 +#[no_mangle]
 +extern "C" fn k(_: bool, _: bool, _: bool, _: bool) {}
 +fn g(_: bool, _: bool, _: bool, _: bool) {}
 +fn h(_: bool, _: bool, _: bool) {}
 +fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {}
 +fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
 +
 +struct S;
 +trait Trait {
++    // should warn for trait functions with and without body
 +    fn f(_: bool, _: bool, _: bool, _: bool);
 +    fn g(_: bool, _: bool, _: bool, _: Vec<u32>);
++    #[allow(clippy::fn_params_excessive_bools)]
++    fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool);
++    fn i(_: bool, _: bool, _: bool, _: bool) {}
 +}
 +
 +impl S {
 +    fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
 +    fn g(&self, _: bool, _: bool, _: bool) {}
 +    #[no_mangle]
 +    extern "C" fn h(_: bool, _: bool, _: bool, _: bool) {}
 +}
 +
 +impl Trait for S {
++    // Should not lint because the trait might not be changeable by the user
++    // We only lint in the trait definition
 +    fn f(_: bool, _: bool, _: bool, _: bool) {}
 +    fn g(_: bool, _: bool, _: bool, _: Vec<u32>) {}
++    fn h(_: bool, _: bool, _: bool, _: bool, _: bool, _: bool) {}
 +}
 +
 +fn main() {
 +    fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
 +        fn nn(_: bool, _: bool, _: bool, _: bool) {}
 +    }
 +}
index 11627105691b7f5c5d6f2c9487f55d1aab146cc8,0000000000000000000000000000000000000000..43363b46972c345583c0929ab1638c61f36d2b78
mode 100644,000000..100644
--- /dev/null
@@@ -1,53 -1,0 +1,61 @@@
-   --> $DIR/fn_params_excessive_bools.rs:18:1
 +error: more than 3 bools in function parameters
-   --> $DIR/fn_params_excessive_bools.rs:21:1
++  --> $DIR/fn_params_excessive_bools.rs:19:1
 +   |
 +LL | fn g(_: bool, _: bool, _: bool, _: bool) {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider refactoring bools into two-variant enums
 +   = note: `-D clippy::fn-params-excessive-bools` implied by `-D warnings`
 +
 +error: more than 3 bools in function parameters
-   --> $DIR/fn_params_excessive_bools.rs:25:5
++  --> $DIR/fn_params_excessive_bools.rs:22:1
 +   |
 +LL | fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider refactoring bools into two-variant enums
 +
 +error: more than 3 bools in function parameters
-   --> $DIR/fn_params_excessive_bools.rs:30:5
++  --> $DIR/fn_params_excessive_bools.rs:27:5
 +   |
 +LL |     fn f(_: bool, _: bool, _: bool, _: bool);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider refactoring bools into two-variant enums
 +
 +error: more than 3 bools in function parameters
-   --> $DIR/fn_params_excessive_bools.rs:42:5
++  --> $DIR/fn_params_excessive_bools.rs:31:5
++   |
++LL |     fn i(_: bool, _: bool, _: bool, _: bool) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider refactoring bools into two-variant enums
++
++error: more than 3 bools in function parameters
++  --> $DIR/fn_params_excessive_bools.rs:35:5
 +   |
 +LL |     fn f(&self, _: bool, _: bool, _: bool, _: bool) {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider refactoring bools into two-variant enums
 +
 +error: more than 3 bools in function parameters
-   --> $DIR/fn_params_excessive_bools.rs:43:9
++  --> $DIR/fn_params_excessive_bools.rs:50:5
 +   |
 +LL | /     fn n(_: bool, _: u32, _: bool, _: Box<u32>, _: bool, _: bool) {
 +LL | |         fn nn(_: bool, _: bool, _: bool, _: bool) {}
 +LL | |     }
 +   | |_____^
 +   |
 +   = help: consider refactoring bools into two-variant enums
 +
 +error: more than 3 bools in function parameters
- error: aborting due to 6 previous errors
++  --> $DIR/fn_params_excessive_bools.rs:51:9
 +   |
 +LL |         fn nn(_: bool, _: bool, _: bool, _: bool) {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider refactoring bools into two-variant enums
 +
++error: aborting due to 7 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8484da2415ab3af0a1278529ceeff26c58e2f31a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++#![warn(clippy::from_raw_with_void_ptr)]
++
++use std::ffi::c_void;
++use std::rc::Rc;
++use std::sync::Arc;
++
++fn main() {
++    // must lint
++    let ptr = Box::into_raw(Box::new(42usize)) as *mut c_void;
++    let _ = unsafe { Box::from_raw(ptr) };
++
++    // shouldn't be linted
++    let _ = unsafe { Box::from_raw(ptr as *mut usize) };
++
++    // shouldn't be linted
++    let should_not_lint_ptr = Box::into_raw(Box::new(12u8)) as *mut u8;
++    let _ = unsafe { Box::from_raw(should_not_lint_ptr as *mut u8) };
++
++    // must lint
++    let ptr = Rc::into_raw(Rc::new(42usize)) as *mut c_void;
++    let _ = unsafe { Rc::from_raw(ptr) };
++
++    // must lint
++    let ptr = Arc::into_raw(Arc::new(42usize)) as *mut c_void;
++    let _ = unsafe { Arc::from_raw(ptr) };
++
++    // must lint
++    let ptr = std::rc::Weak::into_raw(Rc::downgrade(&Rc::new(42usize))) as *mut c_void;
++    let _ = unsafe { std::rc::Weak::from_raw(ptr) };
++
++    // must lint
++    let ptr = std::sync::Weak::into_raw(Arc::downgrade(&Arc::new(42usize))) as *mut c_void;
++    let _ = unsafe { std::sync::Weak::from_raw(ptr) };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..96e4af12ba386ccc4878fb572bea7be2dffcde5f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: creating a `Box` from a void raw pointer
++  --> $DIR/from_raw_with_void_ptr.rs:10:22
++   |
++LL |     let _ = unsafe { Box::from_raw(ptr) };
++   |                      ^^^^^^^^^^^^^^^^^^
++   |
++help: cast this to a pointer of the appropriate type
++  --> $DIR/from_raw_with_void_ptr.rs:10:36
++   |
++LL |     let _ = unsafe { Box::from_raw(ptr) };
++   |                                    ^^^
++   = note: `-D clippy::from-raw-with-void-ptr` implied by `-D warnings`
++
++error: creating a `Rc` from a void raw pointer
++  --> $DIR/from_raw_with_void_ptr.rs:21:22
++   |
++LL |     let _ = unsafe { Rc::from_raw(ptr) };
++   |                      ^^^^^^^^^^^^^^^^^
++   |
++help: cast this to a pointer of the appropriate type
++  --> $DIR/from_raw_with_void_ptr.rs:21:35
++   |
++LL |     let _ = unsafe { Rc::from_raw(ptr) };
++   |                                   ^^^
++
++error: creating a `Arc` from a void raw pointer
++  --> $DIR/from_raw_with_void_ptr.rs:25:22
++   |
++LL |     let _ = unsafe { Arc::from_raw(ptr) };
++   |                      ^^^^^^^^^^^^^^^^^^
++   |
++help: cast this to a pointer of the appropriate type
++  --> $DIR/from_raw_with_void_ptr.rs:25:36
++   |
++LL |     let _ = unsafe { Arc::from_raw(ptr) };
++   |                                    ^^^
++
++error: creating a `Weak` from a void raw pointer
++  --> $DIR/from_raw_with_void_ptr.rs:29:22
++   |
++LL |     let _ = unsafe { std::rc::Weak::from_raw(ptr) };
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: cast this to a pointer of the appropriate type
++  --> $DIR/from_raw_with_void_ptr.rs:29:46
++   |
++LL |     let _ = unsafe { std::rc::Weak::from_raw(ptr) };
++   |                                              ^^^
++
++error: creating a `Weak` from a void raw pointer
++  --> $DIR/from_raw_with_void_ptr.rs:33:22
++   |
++LL |     let _ = unsafe { std::sync::Weak::from_raw(ptr) };
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: cast this to a pointer of the appropriate type
++  --> $DIR/from_raw_with_void_ptr.rs:33:48
++   |
++LL |     let _ = unsafe { std::sync::Weak::from_raw(ptr) };
++   |                                                ^^^
++
++error: aborting due to 5 previous errors
++
index 937f85904083160c52e7b238e2320c9a2e3d4f6b,0000000000000000000000000000000000000000..6dee4d5b4b6247f60c416e80143ef2e185c39a33
mode 100644,000000..100644
--- /dev/null
@@@ -1,191 -1,0 +1,191 @@@
- error: used `unwrap()` on `an Option` value
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:35:17
 +   |
 +LL |         let _ = boxed_slice.get(1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&boxed_slice[1]`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/get_unwrap.rs:5:9
 +   |
 +LL | #![deny(clippy::get_unwrap)]
 +   |         ^^^^^^^^^^^^^^^^^^
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:35:17
 +   |
 +LL |         let _ = boxed_slice.get(1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +   = note: `-D clippy::unwrap-used` implied by `-D warnings`
 +
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:36:17
 +   |
 +LL |         let _ = some_slice.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_slice[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:36:17
 +   |
 +LL |         let _ = some_slice.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:37:17
 +   |
 +LL |         let _ = some_vec.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vec[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:37:17
 +   |
 +LL |         let _ = some_vec.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:38:17
 +   |
 +LL |         let _ = some_vecdeque.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_vecdeque[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:38:17
 +   |
 +LL |         let _ = some_vecdeque.get(0).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:39:17
 +   |
 +LL |         let _ = some_hashmap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_hashmap[&1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:39:17
 +   |
 +LL |         let _ = some_hashmap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:40:17
 +   |
 +LL |         let _ = some_btreemap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&some_btreemap[&1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:40:17
 +   |
 +LL |         let _ = some_btreemap.get(&1).unwrap();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:44:21
 +   |
 +LL |         let _: u8 = *boxed_slice.get(1).unwrap();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:44:22
 +   |
 +LL |         let _: u8 = *boxed_slice.get(1).unwrap();
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:49:9
 +   |
 +LL |         *boxed_slice.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `boxed_slice[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:49:10
 +   |
 +LL |         *boxed_slice.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:50:9
 +   |
 +LL |         *some_slice.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_slice[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:50:10
 +   |
 +LL |         *some_slice.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:51:9
 +   |
 +LL |         *some_vec.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:51:10
 +   |
 +LL |         *some_vec.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:52:9
 +   |
 +LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vecdeque[0]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:52:10
 +   |
 +LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:64:17
 +   |
 +LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
 +
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:64:17
 +   |
 +LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
 +  --> $DIR/get_unwrap.rs:65:17
 +   |
 +LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `some_vec[0..1]`
 +
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/get_unwrap.rs:65:17
 +   |
 +LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: aborting due to 26 previous errors
 +
index b8e40d995531aeb43c3c021716336450cf3d44d7,0000000000000000000000000000000000000000..61985e56b769e45626fbb083ac7e4b83914b1813
mode 100644,000000..100644
--- /dev/null
@@@ -1,112 -1,0 +1,124 @@@
 +// run-rustfix
 +#![feature(exhaustive_patterns, never_type)]
 +#![allow(dead_code, unreachable_code, unused_variables)]
 +#![allow(clippy::let_and_return)]
 +
 +enum SingleVariantEnum {
 +    Variant(i32),
 +}
 +
 +struct TupleStruct(i32);
 +
++struct NonCopy;
++struct TupleStructWithNonCopy(NonCopy);
++
 +enum EmptyEnum {}
 +
 +macro_rules! match_enum {
 +    ($param:expr) => {
 +        let data = match $param {
 +            SingleVariantEnum::Variant(i) => i,
 +        };
 +    };
 +}
 +
 +fn infallible_destructuring_match_enum() {
 +    let wrapper = SingleVariantEnum::Variant(0);
 +
 +    // This should lint!
 +    let SingleVariantEnum::Variant(data) = wrapper;
 +
 +    // This shouldn't (inside macro)
 +    match_enum!(wrapper);
 +
 +    // This shouldn't!
 +    let data = match wrapper {
 +        SingleVariantEnum::Variant(_) => -1,
 +    };
 +
 +    // Neither should this!
 +    let data = match wrapper {
 +        SingleVariantEnum::Variant(i) => -1,
 +    };
 +
 +    let SingleVariantEnum::Variant(data) = wrapper;
 +}
 +
 +macro_rules! match_struct {
 +    ($param:expr) => {
 +        let data = match $param {
 +            TupleStruct(i) => i,
 +        };
 +    };
 +}
 +
 +fn infallible_destructuring_match_struct() {
 +    let wrapper = TupleStruct(0);
 +
 +    // This should lint!
 +    let TupleStruct(data) = wrapper;
 +
 +    // This shouldn't (inside macro)
 +    match_struct!(wrapper);
 +
 +    // This shouldn't!
 +    let data = match wrapper {
 +        TupleStruct(_) => -1,
 +    };
 +
 +    // Neither should this!
 +    let data = match wrapper {
 +        TupleStruct(i) => -1,
 +    };
 +
 +    let TupleStruct(data) = wrapper;
 +}
 +
++fn infallible_destructuring_match_struct_with_noncopy() {
++    let wrapper = TupleStructWithNonCopy(NonCopy);
++
++    // This should lint! (keeping `ref` in the suggestion)
++    let TupleStructWithNonCopy(ref data) = wrapper;
++
++    let TupleStructWithNonCopy(ref data) = wrapper;
++}
++
 +macro_rules! match_never_enum {
 +    ($param:expr) => {
 +        let data = match $param {
 +            Ok(i) => i,
 +        };
 +    };
 +}
 +
 +fn never_enum() {
 +    let wrapper: Result<i32, !> = Ok(23);
 +
 +    // This should lint!
 +    let Ok(data) = wrapper;
 +
 +    // This shouldn't (inside macro)
 +    match_never_enum!(wrapper);
 +
 +    // This shouldn't!
 +    let data = match wrapper {
 +        Ok(_) => -1,
 +    };
 +
 +    // Neither should this!
 +    let data = match wrapper {
 +        Ok(i) => -1,
 +    };
 +
 +    let Ok(data) = wrapper;
 +}
 +
 +impl EmptyEnum {
 +    fn match_on(&self) -> ! {
 +        // The lint shouldn't pick this up, as `let` won't work here!
 +        let data = match *self {};
 +        data
 +    }
 +}
 +
 +fn main() {}
index 106cd438b90e79e1b0dd28c41c8b0bf3cc1e576d,0000000000000000000000000000000000000000..f2768245bbc417d8fa89944f38f0f3a3b0cd4459
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,132 @@@
 +// run-rustfix
 +#![feature(exhaustive_patterns, never_type)]
 +#![allow(dead_code, unreachable_code, unused_variables)]
 +#![allow(clippy::let_and_return)]
 +
 +enum SingleVariantEnum {
 +    Variant(i32),
 +}
 +
 +struct TupleStruct(i32);
 +
++struct NonCopy;
++struct TupleStructWithNonCopy(NonCopy);
++
 +enum EmptyEnum {}
 +
 +macro_rules! match_enum {
 +    ($param:expr) => {
 +        let data = match $param {
 +            SingleVariantEnum::Variant(i) => i,
 +        };
 +    };
 +}
 +
 +fn infallible_destructuring_match_enum() {
 +    let wrapper = SingleVariantEnum::Variant(0);
 +
 +    // This should lint!
 +    let data = match wrapper {
 +        SingleVariantEnum::Variant(i) => i,
 +    };
 +
 +    // This shouldn't (inside macro)
 +    match_enum!(wrapper);
 +
 +    // This shouldn't!
 +    let data = match wrapper {
 +        SingleVariantEnum::Variant(_) => -1,
 +    };
 +
 +    // Neither should this!
 +    let data = match wrapper {
 +        SingleVariantEnum::Variant(i) => -1,
 +    };
 +
 +    let SingleVariantEnum::Variant(data) = wrapper;
 +}
 +
 +macro_rules! match_struct {
 +    ($param:expr) => {
 +        let data = match $param {
 +            TupleStruct(i) => i,
 +        };
 +    };
 +}
 +
 +fn infallible_destructuring_match_struct() {
 +    let wrapper = TupleStruct(0);
 +
 +    // This should lint!
 +    let data = match wrapper {
 +        TupleStruct(i) => i,
 +    };
 +
 +    // This shouldn't (inside macro)
 +    match_struct!(wrapper);
 +
 +    // This shouldn't!
 +    let data = match wrapper {
 +        TupleStruct(_) => -1,
 +    };
 +
 +    // Neither should this!
 +    let data = match wrapper {
 +        TupleStruct(i) => -1,
 +    };
 +
 +    let TupleStruct(data) = wrapper;
 +}
 +
++fn infallible_destructuring_match_struct_with_noncopy() {
++    let wrapper = TupleStructWithNonCopy(NonCopy);
++
++    // This should lint! (keeping `ref` in the suggestion)
++    let data = match wrapper {
++        TupleStructWithNonCopy(ref n) => n,
++    };
++
++    let TupleStructWithNonCopy(ref data) = wrapper;
++}
++
 +macro_rules! match_never_enum {
 +    ($param:expr) => {
 +        let data = match $param {
 +            Ok(i) => i,
 +        };
 +    };
 +}
 +
 +fn never_enum() {
 +    let wrapper: Result<i32, !> = Ok(23);
 +
 +    // This should lint!
 +    let data = match wrapper {
 +        Ok(i) => i,
 +    };
 +
 +    // This shouldn't (inside macro)
 +    match_never_enum!(wrapper);
 +
 +    // This shouldn't!
 +    let data = match wrapper {
 +        Ok(_) => -1,
 +    };
 +
 +    // Neither should this!
 +    let data = match wrapper {
 +        Ok(i) => -1,
 +    };
 +
 +    let Ok(data) = wrapper;
 +}
 +
 +impl EmptyEnum {
 +    fn match_on(&self) -> ! {
 +        // The lint shouldn't pick this up, as `let` won't work here!
 +        let data = match *self {};
 +        data
 +    }
 +}
 +
 +fn main() {}
index 1b78db42014a2c5a3f493955138680a614780b5c,0000000000000000000000000000000000000000..f8a50f0223d630053c6393708845449ca1f8e970
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,36 @@@
-   --> $DIR/infallible_destructuring_match.rs:26:5
 +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
-   --> $DIR/infallible_destructuring_match.rs:58:5
++  --> $DIR/infallible_destructuring_match.rs:29:5
 +   |
 +LL | /     let data = match wrapper {
 +LL | |         SingleVariantEnum::Variant(i) => i,
 +LL | |     };
 +   | |______^ help: try this: `let SingleVariantEnum::Variant(data) = wrapper;`
 +   |
 +   = note: `-D clippy::infallible-destructuring-match` implied by `-D warnings`
 +
 +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
-   --> $DIR/infallible_destructuring_match.rs:90:5
++  --> $DIR/infallible_destructuring_match.rs:61:5
 +   |
 +LL | /     let data = match wrapper {
 +LL | |         TupleStruct(i) => i,
 +LL | |     };
 +   | |______^ help: try this: `let TupleStruct(data) = wrapper;`
 +
 +error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
- error: aborting due to 3 previous errors
++  --> $DIR/infallible_destructuring_match.rs:85:5
++   |
++LL | /     let data = match wrapper {
++LL | |         TupleStructWithNonCopy(ref n) => n,
++LL | |     };
++   | |______^ help: try this: `let TupleStructWithNonCopy(ref data) = wrapper;`
++
++error: you seem to be trying to use `match` to destructure a single infallible pattern. Consider using `let`
++  --> $DIR/infallible_destructuring_match.rs:104:5
 +   |
 +LL | /     let data = match wrapper {
 +LL | |         Ok(i) => i,
 +LL | |     };
 +   | |______^ help: try this: `let Ok(data) = wrapper;`
 +
++error: aborting due to 4 previous errors
 +
index fb2a93c9580e00085111f8e2f647cc0dc5f9bd74,0000000000000000000000000000000000000000..fd553aa4538ada4d783a00fda912b0f3d46e9e5b
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,25 @@@
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a
 +  --> $DIR/issue_4266.rs:4:1
 +   |
 +LL | async fn sink1<'a>(_: &'a str) {} // lint
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a
 +  --> $DIR/issue_4266.rs:8:1
 +   |
 +LL | async fn one_to_one<'a>(s: &'a str) -> &'a str {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: methods called `new` usually take no `self`
 +  --> $DIR/issue_4266.rs:28:22
 +   |
 +LL |     pub async fn new(&mut self) -> Self {
 +   |                      ^^^^^^^^^
 +   |
 +   = help: consider choosing a less ambiguous name
 +   = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
 +
 +error: aborting due to 3 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8f54cdca91209ba8bb47f24a975ec6ad596e3ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++use std::future::Future;
++
++async fn some_async_fn() {}
++
++fn sync_side_effects() {}
++fn custom() -> impl Future<Output = ()> {
++    sync_side_effects();
++    async {}
++}
++
++fn do_something_to_future(future: &mut impl Future<Output = ()>) {}
++
++fn main() {
++    let _ = some_async_fn();
++    let _ = custom();
++
++    let mut future = some_async_fn();
++    do_something_to_future(&mut future);
++    let _ = future;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33a748736a880ed5997a781ed4fe4a4b0bac13e3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++error: non-binding `let` on a future
++  --> $DIR/let_underscore_future.rs:14:5
++   |
++LL |     let _ = some_async_fn();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider awaiting the future or dropping explicitly with `std::mem::drop`
++   = note: `-D clippy::let-underscore-future` implied by `-D warnings`
++
++error: non-binding `let` on a future
++  --> $DIR/let_underscore_future.rs:15:5
++   |
++LL |     let _ = custom();
++   |     ^^^^^^^^^^^^^^^^^
++   |
++   = help: consider awaiting the future or dropping explicitly with `std::mem::drop`
++
++error: non-binding `let` on a future
++  --> $DIR/let_underscore_future.rs:19:5
++   |
++LL |     let _ = future;
++   |     ^^^^^^^^^^^^^^^
++   |
++   = help: consider awaiting the future or dropping explicitly with `std::mem::drop`
++
++error: aborting due to 3 previous errors
++
index 7a7c4e9249952911d17b70a3935b86bb166effc5,0000000000000000000000000000000000000000..4dff4d766bcc382ada595d3b5ade11afc3d420dc
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,39 @@@
-     let m = std::sync::Mutex::new(());
-     let rw = std::sync::RwLock::new(());
-     let _ = m.lock();
-     let _ = rw.read();
-     let _ = rw.write();
-     let _ = m.try_lock();
-     let _ = rw.try_read();
-     let _ = rw.try_write();
-     // These shouldn't throw an error.
-     let _ = m;
-     let _ = rw;
 +#![warn(clippy::let_underscore_lock)]
 +
 +extern crate parking_lot;
 +
 +fn main() {
 +    use parking_lot::{lock_api::RawMutex, Mutex, RwLock};
 +
 +    let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ());
 +    let _ = p_m.lock();
 +
 +    let p_m1 = Mutex::new(0);
 +    let _ = p_m1.lock();
 +
 +    let p_rw = RwLock::new(0);
 +    let _ = p_rw.read();
 +    let _ = p_rw.write();
 +
 +    // These shouldn't throw an error.
 +    let _ = p_m;
 +    let _ = p_m1;
 +    let _ = p_rw;
 +}
++
++fn uplifted() {
++    // shouldn't lint std locks as they were uplifted as rustc's `let_underscore_lock`
++
++    let m = std::sync::Mutex::new(());
++    let rw = std::sync::RwLock::new(());
++
++    let _ = m.lock();
++    let _ = rw.read();
++    let _ = rw.write();
++    let _ = m.try_lock();
++    let _ = rw.try_read();
++    let _ = rw.try_write();
++
++    let _ = m;
++    let _ = rw;
++}
index d7779e7b6c48af2c7ee4a9b21704c3e415c8b2da,0000000000000000000000000000000000000000..f137d4112092d9fce7591817f0356958ba85f179
mode 100644,000000..100644
--- /dev/null
@@@ -1,83 -1,0 +1,35 @@@
- error: non-binding let on a synchronization lock
++error: non-binding `let` on a synchronization lock
 +  --> $DIR/let_underscore_lock.rs:9:5
 +   |
- LL |     let _ = m.lock();
-    |     ^^^^^^^^^^^^^^^^^
-    |
-    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
-    = note: `-D clippy::let-underscore-lock` implied by `-D warnings`
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:10:5
-    |
- LL |     let _ = rw.read();
-    |     ^^^^^^^^^^^^^^^^^^
-    |
-    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:11:5
-    |
- LL |     let _ = rw.write();
-    |     ^^^^^^^^^^^^^^^^^^^
-    |
-    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:12:5
-    |
- LL |     let _ = m.try_lock();
-    |     ^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:13:5
-    |
- LL |     let _ = rw.try_read();
-    |     ^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:14:5
-    |
- LL |     let _ = rw.try_write();
-    |     ^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:23:5
-    |
 +LL |     let _ = p_m.lock();
 +   |     ^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
++   = note: `-D clippy::let-underscore-lock` implied by `-D warnings`
 +
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:26:5
++error: non-binding `let` on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:12:5
 +   |
 +LL |     let _ = p_m1.lock();
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 +
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:29:5
++error: non-binding `let` on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:15:5
 +   |
 +LL |     let _ = p_rw.read();
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 +
- error: non-binding let on a synchronization lock
-   --> $DIR/let_underscore_lock.rs:30:5
++error: non-binding `let` on a synchronization lock
++  --> $DIR/let_underscore_lock.rs:16:5
 +   |
 +LL |     let _ = p_rw.write();
 +   |     ^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 +
- error: aborting due to 10 previous errors
++error: aborting due to 4 previous errors
 +
index bae60f2ff9b79f7af718679cab33f8417c718ca2,0000000000000000000000000000000000000000..28d760eb46ecccd27d0d1ec2658d4606b566e084
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
- error: non-binding let on a result of a `#[must_use]` function
++error: non-binding `let` on a result of a `#[must_use]` function
 +  --> $DIR/let_underscore_must_use.rs:67:5
 +   |
 +LL |     let _ = f();
 +   |     ^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using function result
 +   = note: `-D clippy::let-underscore-must-use` implied by `-D warnings`
 +
- error: non-binding let on an expression with `#[must_use]` type
++error: non-binding `let` on an expression with `#[must_use]` type
 +  --> $DIR/let_underscore_must_use.rs:68:5
 +   |
 +LL |     let _ = g();
 +   |     ^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using expression value
 +
- error: non-binding let on a result of a `#[must_use]` function
++error: non-binding `let` on a result of a `#[must_use]` function
 +  --> $DIR/let_underscore_must_use.rs:70:5
 +   |
 +LL |     let _ = l(0_u32);
 +   |     ^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using function result
 +
- error: non-binding let on a result of a `#[must_use]` function
++error: non-binding `let` on a result of a `#[must_use]` function
 +  --> $DIR/let_underscore_must_use.rs:74:5
 +   |
 +LL |     let _ = s.f();
 +   |     ^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using function result
 +
- error: non-binding let on an expression with `#[must_use]` type
++error: non-binding `let` on an expression with `#[must_use]` type
 +  --> $DIR/let_underscore_must_use.rs:75:5
 +   |
 +LL |     let _ = s.g();
 +   |     ^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using expression value
 +
- error: non-binding let on a result of a `#[must_use]` function
++error: non-binding `let` on a result of a `#[must_use]` function
 +  --> $DIR/let_underscore_must_use.rs:78:5
 +   |
 +LL |     let _ = S::h();
 +   |     ^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using function result
 +
- error: non-binding let on an expression with `#[must_use]` type
++error: non-binding `let` on an expression with `#[must_use]` type
 +  --> $DIR/let_underscore_must_use.rs:79:5
 +   |
 +LL |     let _ = S::p();
 +   |     ^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using expression value
 +
- error: non-binding let on a result of a `#[must_use]` function
++error: non-binding `let` on a result of a `#[must_use]` function
 +  --> $DIR/let_underscore_must_use.rs:81:5
 +   |
 +LL |     let _ = S::a();
 +   |     ^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using function result
 +
- error: non-binding let on an expression with `#[must_use]` type
++error: non-binding `let` on an expression with `#[must_use]` type
 +  --> $DIR/let_underscore_must_use.rs:83:5
 +   |
 +LL |     let _ = if true { Ok(()) } else { Err(()) };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using expression value
 +
- error: non-binding let on a result of a `#[must_use]` function
++error: non-binding `let` on a result of a `#[must_use]` function
 +  --> $DIR/let_underscore_must_use.rs:87:5
 +   |
 +LL |     let _ = a.is_ok();
 +   |     ^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using function result
 +
- error: non-binding let on an expression with `#[must_use]` type
++error: non-binding `let` on an expression with `#[must_use]` type
 +  --> $DIR/let_underscore_must_use.rs:89:5
 +   |
 +LL |     let _ = a.map(|_| ());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider explicitly using expression value
 +
- error: non-binding let on an expression with `#[must_use]` type
++error: non-binding `let` on an expression with `#[must_use]` type
 +  --> $DIR/let_underscore_must_use.rs:91:5
 +   |
 +LL |     let _ = a;
 +   |     ^^^^^^^^^^
 +   |
 +   = help: consider explicitly using expression value
 +
 +error: aborting due to 12 previous errors
 +
index 96cd87c0e19a7079f0940975ebd69241f19dd5b8,0000000000000000000000000000000000000000..552213a7ff229e7fb97bc08011dbcc1a459680ae
mode 100644,000000..100644
--- /dev/null
@@@ -1,125 -1,0 +1,125 @@@
-     // Test for loop over implicitly implicitly adjusted `Iterator` with `if let` statement
 +#![warn(clippy::manual_flatten)]
 +#![allow(clippy::useless_vec, clippy::uninlined_format_args)]
 +
 +fn main() {
 +    // Test for loop over implicitly adjusted `Iterator` with `if let` expression
 +    let x = vec![Some(1), Some(2), Some(3)];
 +    for n in x {
 +        if let Some(y) = n {
 +            println!("{}", y);
 +        }
 +    }
 +
++    // Test for loop over implicitly adjusted `Iterator` with `if let` statement
 +    let y: Vec<Result<i32, i32>> = vec![];
 +    for n in y.clone() {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        };
 +    }
 +
 +    // Test for loop over by reference
 +    for n in &y {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        }
 +    }
 +
 +    // Test for loop over an implicit reference
 +    let z = &y;
 +    for n in z {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        }
 +    }
 +
 +    // Test for loop over `Iterator` with `if let` expression
 +    let z = vec![Some(1), Some(2), Some(3)];
 +    let z = z.iter();
 +    for n in z {
 +        if let Some(m) = n {
 +            println!("{}", m);
 +        }
 +    }
 +
 +    // Using the `None` variant should not trigger the lint
 +    // Note: for an autofixable suggestion, the binding in the for loop has to take the
 +    // name of the binding in the `if let`
 +    let z = vec![Some(1), Some(2), Some(3)];
 +    for n in z {
 +        if n.is_none() {
 +            println!("Nada.");
 +        }
 +    }
 +
 +    // Using the `Err` variant should not trigger the lint
 +    for n in y.clone() {
 +        if let Err(e) = n {
 +            println!("Oops: {}!", e);
 +        }
 +    }
 +
 +    // Having an else clause should not trigger the lint
 +    for n in y.clone() {
 +        if let Ok(n) = n {
 +            println!("{}", n);
 +        } else {
 +            println!("Oops!");
 +        }
 +    }
 +
 +    let vec_of_ref = vec![&Some(1)];
 +    for n in &vec_of_ref {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +
 +    let vec_of_ref = &vec_of_ref;
 +    for n in vec_of_ref {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +
 +    let slice_of_ref = &[&Some(1)];
 +    for n in slice_of_ref {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +
 +    struct Test {
 +        a: usize,
 +    }
 +
 +    let mut vec_of_struct = [Some(Test { a: 1 }), None];
 +
 +    // Usage of `if let` expression should not trigger lint
 +    for n in vec_of_struct.iter_mut() {
 +        if let Some(z) = n {
 +            *n = None;
 +        }
 +    }
 +
 +    // Using manual flatten should not trigger the lint
 +    for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
 +        println!("{}", n);
 +    }
 +
 +    run_unformatted_tests();
 +}
 +
 +#[rustfmt::skip]
 +fn run_unformatted_tests() {
 +    // Skip rustfmt here on purpose so the suggestion does not fit in one line
 +    for n in vec![
 +        Some(1),
 +        Some(2),
 +        Some(3)
 +    ].iter() {
 +        if let Some(n) = n {
 +            println!("{:?}", n);
 +        }
 +    }
 +}
index 0fa776b7b2e4ed4b504691488e28d6ba9451b1f2,0000000000000000000000000000000000000000..85a91543c893ac1c91c4db6f07cd6b1533ade8d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,28 @@@
 +// run-rustfix
 +#![warn(clippy::manual_instant_elapsed)]
 +#![allow(clippy::unnecessary_operation)]
++#![allow(clippy::unchecked_duration_subtraction)]
 +#![allow(unused_variables)]
 +#![allow(unused_must_use)]
 +
 +use std::time::Instant;
 +
 +fn main() {
 +    let prev_instant = Instant::now();
 +
 +    {
 +        // don't influence
 +        let another_instant = Instant::now();
 +    }
 +
 +    let duration = prev_instant.elapsed();
 +
 +    // don't catch
 +    let duration = prev_instant.elapsed();
 +
 +    Instant::now() - duration;
 +
 +    let ref_to_instant = &Instant::now();
 +
 +    (*ref_to_instant).elapsed(); // to ensure parens are added correctly
 +}
index 5b11b84535ddc46e766f706f0b0a295aa854f978,0000000000000000000000000000000000000000..c98cb15b91649dc9d0f4b3e00485fb7c3e63b79b
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,28 @@@
 +// run-rustfix
 +#![warn(clippy::manual_instant_elapsed)]
 +#![allow(clippy::unnecessary_operation)]
++#![allow(clippy::unchecked_duration_subtraction)]
 +#![allow(unused_variables)]
 +#![allow(unused_must_use)]
 +
 +use std::time::Instant;
 +
 +fn main() {
 +    let prev_instant = Instant::now();
 +
 +    {
 +        // don't influence
 +        let another_instant = Instant::now();
 +    }
 +
 +    let duration = Instant::now() - prev_instant;
 +
 +    // don't catch
 +    let duration = prev_instant.elapsed();
 +
 +    Instant::now() - duration;
 +
 +    let ref_to_instant = &Instant::now();
 +
 +    Instant::now() - *ref_to_instant; // to ensure parens are added correctly
 +}
index 5537f5642a23cd01488caa8cc7f131cd82f4a19b,0000000000000000000000000000000000000000..4ce1f689107e961972ac9c749f04118d50d47bb3
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
-   --> $DIR/manual_instant_elapsed.rs:17:20
 +error: manual implementation of `Instant::elapsed`
-   --> $DIR/manual_instant_elapsed.rs:26:5
++  --> $DIR/manual_instant_elapsed.rs:18:20
 +   |
 +LL |     let duration = Instant::now() - prev_instant;
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `prev_instant.elapsed()`
 +   |
 +   = note: `-D clippy::manual-instant-elapsed` implied by `-D warnings`
 +
 +error: manual implementation of `Instant::elapsed`
++  --> $DIR/manual_instant_elapsed.rs:27:5
 +   |
 +LL |     Instant::now() - *ref_to_instant; // to ensure parens are added correctly
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*ref_to_instant).elapsed()`
 +
 +error: aborting due to 2 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..765bb785994e21275752475450dc439b3e4e2300
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![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' | '_'));
++}
++
++fn msrv_1_23() {
++    #![clippy::msrv = "1.23"]
++
++    assert!(matches!(b'1', b'0'..=b'9'));
++    assert!(matches!('X', 'A'..='Z'));
++    assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
++}
++
++fn msrv_1_24() {
++    #![clippy::msrv = "1.24"]
++
++    assert!(b'1'.is_ascii_digit());
++    assert!('X'.is_ascii_uppercase());
++    assert!('x'.is_ascii_alphabetic());
++}
++
++fn msrv_1_46() {
++    #![clippy::msrv = "1.46"]
++    const FOO: bool = matches!('x', '0'..='9');
++}
++
++fn msrv_1_47() {
++    #![clippy::msrv = "1.47"]
++    const FOO: bool = 'x'.is_ascii_digit();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..be133161041234b251968cafde2e28208c80027b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++// run-rustfix
++
++#![feature(custom_inner_attributes)]
++#![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' | '_'));
++}
++
++fn msrv_1_23() {
++    #![clippy::msrv = "1.23"]
++
++    assert!(matches!(b'1', b'0'..=b'9'));
++    assert!(matches!('X', 'A'..='Z'));
++    assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
++}
++
++fn msrv_1_24() {
++    #![clippy::msrv = "1.24"]
++
++    assert!(matches!(b'1', b'0'..=b'9'));
++    assert!(matches!('X', 'A'..='Z'));
++    assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
++}
++
++fn msrv_1_46() {
++    #![clippy::msrv = "1.46"]
++    const FOO: bool = matches!('x', '0'..='9');
++}
++
++fn msrv_1_47() {
++    #![clippy::msrv = "1.47"]
++    const FOO: bool = matches!('x', '0'..='9');
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c0a9d4db1a15960f9d47c605c6321b087c270e17
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:8: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:9:13
++   |
++LL |     assert!(matches!('X', 'A'..='Z'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:10:13
++   |
++LL |     assert!(matches!(b'x', b'a'..=b'z'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'x'.is_ascii_lowercase()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:11:13
++   |
++LL |     assert!(matches!(b'X', b'A'..=b'Z'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'X'.is_ascii_uppercase()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:14:13
++   |
++LL |     assert!(matches!(num, '0'..='9'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.is_ascii_digit()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:15:13
++   |
++LL |     assert!(matches!(b'1', b'0'..=b'9'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:16:13
++   |
++LL |     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:32:13
++   |
++LL |     assert!(matches!(b'1', b'0'..=b'9'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b'1'.is_ascii_digit()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:33:13
++   |
++LL |     assert!(matches!('X', 'A'..='Z'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'X'.is_ascii_uppercase()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:34:13
++   |
++LL |     assert!(matches!('x', 'A'..='Z' | 'a'..='z'));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_alphabetic()`
++
++error: manual check for common ascii range
++  --> $DIR/manual_is_ascii_check.rs:44:23
++   |
++LL |     const FOO: bool = matches!('x', '0'..='9');
++   |                       ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_digit()`
++
++error: aborting due to 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2ef40e5911af46d077b302bcb78e787bce50b5d4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,237 @@@
++#![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!() };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..453b68b8bd003c8f43cadd240412ff0a146ad07b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,263 @@@
++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: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..93c86ca24fea3a079c80c8ae7bb32204b29f6be3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,121 @@@
++#![allow(unused_braces, unused_variables, dead_code)]
++#![allow(clippy::collapsible_else_if, clippy::let_unit_value)]
++#![warn(clippy::manual_let_else)]
++// Ensure that we don't conflict with match -> if let lints
++#![warn(clippy::single_match_else, clippy::single_match)]
++
++fn f() -> Result<u32, u32> {
++    Ok(0)
++}
++
++fn g() -> Option<()> {
++    None
++}
++
++fn h() -> (Option<()>, Option<()>) {
++    (None, None)
++}
++
++enum Variant {
++    Foo,
++    Bar(u32),
++    Baz(u32),
++}
++
++fn build_enum() -> Variant {
++    Variant::Foo
++}
++
++fn main() {}
++
++fn fire() {
++    let v = match g() {
++        Some(v_some) => v_some,
++        None => return,
++    };
++
++    let v = match g() {
++        Some(v_some) => v_some,
++        _ => return,
++    };
++
++    loop {
++        // More complex pattern for the identity arm and diverging arm
++        let v = match h() {
++            (Some(_), Some(_)) | (None, None) => continue,
++            (Some(v), None) | (None, Some(v)) => v,
++        };
++        // Custom enums are supported as long as the "else" arm is a simple _
++        let v = match build_enum() {
++            _ => continue,
++            Variant::Bar(v) | Variant::Baz(v) => v,
++        };
++    }
++
++    // There is a _ in the diverging arm
++    // TODO also support unused bindings aka _v
++    let v = match f() {
++        Ok(v) => v,
++        Err(_) => return,
++    };
++
++    // Err(()) is an allowed pattern
++    let v = match f().map_err(|_| ()) {
++        Ok(v) => v,
++        Err(()) => return,
++    };
++}
++
++fn not_fire() {
++    // Multiple diverging arms
++    let v = match h() {
++        _ => panic!(),
++        (None, Some(_v)) => return,
++        (Some(v), None) => v,
++    };
++
++    // Multiple identity arms
++    let v = match h() {
++        _ => panic!(),
++        (None, Some(v)) => v,
++        (Some(v), None) => v,
++    };
++
++    // No diverging arm at all, only identity arms.
++    // This is no case for let else, but destructuring assignment.
++    let v = match f() {
++        Ok(v) => v,
++        Err(e) => e,
++    };
++
++    // The identity arm has a guard
++    let v = match g() {
++        Some(v) if g().is_none() => v,
++        _ => return,
++    };
++
++    // The diverging arm has a guard
++    let v = match f() {
++        Err(v) if v > 0 => panic!(),
++        Ok(v) | Err(v) => v,
++    };
++
++    // The diverging arm creates a binding
++    let v = match f() {
++        Ok(v) => v,
++        Err(e) => panic!("error: {e}"),
++    };
++
++    // Custom enum where the diverging arm
++    // explicitly mentions the variant
++    let v = match build_enum() {
++        Variant::Foo => return,
++        Variant::Bar(v) | Variant::Baz(v) => v,
++    };
++
++    // The custom enum is surrounded by an Err()
++    let v = match Err(build_enum()) {
++        Ok(v) | Err(Variant::Bar(v) | Variant::Baz(v)) => v,
++        Err(Variant::Foo) => return,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..38be5ac54547368b7fe98d336a1da5acc2e85db7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: this could be rewritten as `let...else`
++  --> $DIR/manual_let_else_match.rs:32:5
++   |
++LL | /     let v = match g() {
++LL | |         Some(v_some) => v_some,
++LL | |         None => return,
++LL | |     };
++   | |______^ 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_match.rs:37:5
++   |
++LL | /     let v = match g() {
++LL | |         Some(v_some) => v_some,
++LL | |         _ => return,
++LL | |     };
++   | |______^ help: consider writing: `let Some(v_some) = g() else { return };`
++
++error: this could be rewritten as `let...else`
++  --> $DIR/manual_let_else_match.rs:44:9
++   |
++LL | /         let v = match h() {
++LL | |             (Some(_), Some(_)) | (None, None) => continue,
++LL | |             (Some(v), None) | (None, Some(v)) => v,
++LL | |         };
++   | |__________^ help: consider writing: `let (Some(v), None) | (None, Some(v)) = h() else { continue };`
++
++error: this could be rewritten as `let...else`
++  --> $DIR/manual_let_else_match.rs:49:9
++   |
++LL | /         let v = match build_enum() {
++LL | |             _ => continue,
++LL | |             Variant::Bar(v) | Variant::Baz(v) => v,
++LL | |         };
++   | |__________^ help: consider writing: `let Variant::Bar(v) | Variant::Baz(v) = build_enum() else { continue };`
++
++error: this could be rewritten as `let...else`
++  --> $DIR/manual_let_else_match.rs:57:5
++   |
++LL | /     let v = match f() {
++LL | |         Ok(v) => v,
++LL | |         Err(_) => return,
++LL | |     };
++   | |______^ help: consider writing: `let Ok(v) = f() else { return };`
++
++error: this could be rewritten as `let...else`
++  --> $DIR/manual_let_else_match.rs:63:5
++   |
++LL | /     let v = match f().map_err(|_| ()) {
++LL | |         Ok(v) => v,
++LL | |         Err(()) => return,
++LL | |     };
++   | |______^ help: consider writing: `let Ok(v) = f().map_err(|_| ()) else { return };`
++
++error: aborting due to 6 previous errors
++
index d864f85545349669a72ac1fd8ed3fcaac6bdb3f3,0000000000000000000000000000000000000000..fc8511626b3d122264723e47ec3291caf74f6bb7
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,41 @@@
 +// run-rustfix
 +#![warn(clippy::manual_ok_or)]
++#![allow(clippy::or_fun_call)]
 +#![allow(clippy::disallowed_names)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(dead_code)]
 +#![allow(unused_must_use)]
 +
 +fn main() {
 +    // basic case
 +    let foo: Option<i32> = None;
 +    foo.ok_or("error");
 +
 +    // eta expansion case
 +    foo.ok_or("error");
 +
 +    // turbo fish syntax
 +    None::<i32>.ok_or("error");
 +
 +    // multiline case
 +    #[rustfmt::skip]
 +    foo.ok_or(&format!(
 +        "{}{}{}{}{}{}{}",
 +        "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
 +
 +    // not applicable, closure isn't direct `Ok` wrapping
 +    foo.map_or(Err("error"), |v| Ok(v + 1));
 +
 +    // not applicable, or side isn't `Result::Err`
 +    foo.map_or(Ok::<i32, &str>(1), |v| Ok(v));
 +
 +    // not applicable, expr is not a `Result` value
 +    foo.map_or(42, |v| v);
 +
 +    // TODO patterns not covered yet
 +    match foo {
 +        Some(v) => Ok(v),
 +        None => Err("error"),
 +    };
 +    foo.map_or_else(|| Err("error"), |v| Ok(v));
 +}
index 6264768460ef60539e1379f1dbc28c16af415b7d,0000000000000000000000000000000000000000..b5303d33f5fd1e4b683bc4e20ea36a15ebc9ebaa
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,45 @@@
 +// run-rustfix
 +#![warn(clippy::manual_ok_or)]
++#![allow(clippy::or_fun_call)]
 +#![allow(clippy::disallowed_names)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(dead_code)]
 +#![allow(unused_must_use)]
 +
 +fn main() {
 +    // basic case
 +    let foo: Option<i32> = None;
 +    foo.map_or(Err("error"), |v| Ok(v));
 +
 +    // eta expansion case
 +    foo.map_or(Err("error"), Ok);
 +
 +    // turbo fish syntax
 +    None::<i32>.map_or(Err("error"), |v| Ok(v));
 +
 +    // multiline case
 +    #[rustfmt::skip]
 +    foo.map_or(Err::<i32, &str>(
 +        &format!(
 +            "{}{}{}{}{}{}{}",
 +            "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")
 +        ),
 +        |v| Ok(v),
 +    );
 +
 +    // not applicable, closure isn't direct `Ok` wrapping
 +    foo.map_or(Err("error"), |v| Ok(v + 1));
 +
 +    // not applicable, or side isn't `Result::Err`
 +    foo.map_or(Ok::<i32, &str>(1), |v| Ok(v));
 +
 +    // not applicable, expr is not a `Result` value
 +    foo.map_or(42, |v| v);
 +
 +    // TODO patterns not covered yet
 +    match foo {
 +        Some(v) => Ok(v),
 +        None => Err("error"),
 +    };
 +    foo.map_or_else(|| Err("error"), |v| Ok(v));
 +}
index 65459a097384b08dedd024e2a3d2fc172b4f7524,0000000000000000000000000000000000000000..b4a17f143e3fce05ed999748c4f1bdc19983ec81
mode 100644,000000..100644
--- /dev/null
@@@ -1,41 -1,0 +1,41 @@@
-   --> $DIR/manual_ok_or.rs:11:5
 +error: this pattern reimplements `Option::ok_or`
-   --> $DIR/manual_ok_or.rs:14:5
++  --> $DIR/manual_ok_or.rs:12:5
 +   |
 +LL |     foo.map_or(Err("error"), |v| Ok(v));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
 +   |
 +   = note: `-D clippy::manual-ok-or` implied by `-D warnings`
 +
 +error: this pattern reimplements `Option::ok_or`
-   --> $DIR/manual_ok_or.rs:17:5
++  --> $DIR/manual_ok_or.rs:15:5
 +   |
 +LL |     foo.map_or(Err("error"), Ok);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `foo.ok_or("error")`
 +
 +error: this pattern reimplements `Option::ok_or`
-   --> $DIR/manual_ok_or.rs:21:5
++  --> $DIR/manual_ok_or.rs:18:5
 +   |
 +LL |     None::<i32>.map_or(Err("error"), |v| Ok(v));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `None::<i32>.ok_or("error")`
 +
 +error: this pattern reimplements `Option::ok_or`
++  --> $DIR/manual_ok_or.rs:22:5
 +   |
 +LL | /     foo.map_or(Err::<i32, &str>(
 +LL | |         &format!(
 +LL | |             "{}{}{}{}{}{}{}",
 +LL | |             "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer")
 +LL | |         ),
 +LL | |         |v| Ok(v),
 +LL | |     );
 +   | |_____^
 +   |
 +help: replace with
 +   |
 +LL ~     foo.ok_or(&format!(
 +LL +         "{}{}{}{}{}{}{}",
 +LL ~         "Alice", "Bob", "Sarah", "Marc", "Sandra", "Eric", "Jenifer"));
 +   |
 +
 +error: aborting due to 4 previous errors
 +
index 312819a0a2cf83d567307d788cb5e8c72394561b,0000000000000000000000000000000000000000..53628ef6531aaad6129b8d482ea48c96d7ecc884
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,64 @@@
- #![allow(clippy::let_underscore_drop)]
 +// run-rustfix
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(clippy::missing_docs_in_private_items)]
 +#![allow(clippy::map_identity)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::unnecessary_wraps)]
 +#![feature(result_flattening)]
 +
 +fn main() {
 +    // mapping to Option on Iterator
 +    fn option_id(x: i8) -> Option<i8> {
 +        Some(x)
 +    }
 +    let option_id_ref: fn(i8) -> Option<i8> = option_id;
 +    let option_id_closure = |x| Some(x);
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
 +
 +    // mapping to Iterator on Iterator
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
 +
 +    // mapping to Option on Option
 +    let _: Option<_> = (Some(Some(1))).and_then(|x| x);
 +
 +    // mapping to Result on Result
 +    let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
 +
 +    issue8734();
 +    issue8878();
 +}
 +
 +fn issue8734() {
 +    let _ = [0u8, 1, 2, 3]
 +        .into_iter()
 +        .flat_map(|n| match n {
 +            1 => [n
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)],
 +            n => [n],
 +        });
 +}
 +
 +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
 +#[rustfmt::skip] // whitespace is important for this one
 +fn issue8878() {
 +    std::collections::HashMap::<u32, u32>::new()
 +        .get(&0)
 +        .and_then(|_| {
 +// we need some newlines
 +// so that the span is big enough
 +// for a split output of the diagnostic
 +            Some("")
 + // whitespace beforehand is important as well
 +        });
 +}
index 3fbf4f9a1b044b8c5621a2f73c8ffd7bd39deb84,0000000000000000000000000000000000000000..76016c8ed3cd1826cc3c8eb5ae00dca1a597aa5b
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,66 @@@
- #![allow(clippy::let_underscore_drop)]
 +// run-rustfix
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(clippy::missing_docs_in_private_items)]
 +#![allow(clippy::map_identity)]
 +#![allow(clippy::redundant_closure)]
 +#![allow(clippy::unnecessary_wraps)]
 +#![feature(result_flattening)]
 +
 +fn main() {
 +    // mapping to Option on Iterator
 +    fn option_id(x: i8) -> Option<i8> {
 +        Some(x)
 +    }
 +    let option_id_ref: fn(i8) -> Option<i8> = option_id;
 +    let option_id_closure = |x| Some(x);
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
 +
 +    // mapping to Iterator on Iterator
 +    let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
 +
 +    // mapping to Option on Option
 +    let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
 +
 +    // mapping to Result on Result
 +    let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
 +
 +    issue8734();
 +    issue8878();
 +}
 +
 +fn issue8734() {
 +    let _ = [0u8, 1, 2, 3]
 +        .into_iter()
 +        .map(|n| match n {
 +            1 => [n
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)
 +                .saturating_add(1)],
 +            n => [n],
 +        })
 +        .flatten();
 +}
 +
 +#[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
 +#[rustfmt::skip] // whitespace is important for this one
 +fn issue8878() {
 +    std::collections::HashMap::<u32, u32>::new()
 +        .get(&0)
 +        .map(|_| {
 +// we need some newlines
 +// so that the span is big enough
 +// for a split output of the diagnostic
 +            Some("")
 + // whitespace beforehand is important as well
 +        })
 +        .flatten();
 +}
index c91f0b9ae94fec2626997de308d60c006d6ffaa4,0000000000000000000000000000000000000000..b6b0c4d09c37b5eb65bcfeac5cb428b113e165c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
-   --> $DIR/map_flatten_fixable.rs:18:47
 +error: called `map(..).flatten()` on `Iterator`
-   --> $DIR/map_flatten_fixable.rs:19:47
++  --> $DIR/map_flatten_fixable.rs:17:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id)`
 +   |
 +   = note: `-D clippy::map-flatten` implied by `-D warnings`
 +
 +error: called `map(..).flatten()` on `Iterator`
-   --> $DIR/map_flatten_fixable.rs:20:47
++  --> $DIR/map_flatten_fixable.rs:18:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_ref).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_ref)`
 +
 +error: called `map(..).flatten()` on `Iterator`
-   --> $DIR/map_flatten_fixable.rs:21:47
++  --> $DIR/map_flatten_fixable.rs:19:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id_closure).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(option_id_closure)`
 +
 +error: called `map(..).flatten()` on `Iterator`
-   --> $DIR/map_flatten_fixable.rs:24:47
++  --> $DIR/map_flatten_fixable.rs:20:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| x.checked_add(1)).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `filter_map` and remove the `.flatten()`: `filter_map(|x| x.checked_add(1))`
 +
 +error: called `map(..).flatten()` on `Iterator`
-   --> $DIR/map_flatten_fixable.rs:27:40
++  --> $DIR/map_flatten_fixable.rs:23:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(|x| 0..x).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `flat_map` and remove the `.flatten()`: `flat_map(|x| 0..x)`
 +
 +error: called `map(..).flatten()` on `Option`
-   --> $DIR/map_flatten_fixable.rs:30:42
++  --> $DIR/map_flatten_fixable.rs:26:40
 +   |
 +LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
 +   |                                        ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
 +
 +error: called `map(..).flatten()` on `Result`
-   --> $DIR/map_flatten_fixable.rs:39:10
++  --> $DIR/map_flatten_fixable.rs:29:42
 +   |
 +LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
 +   |                                          ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
 +
 +error: called `map(..).flatten()` on `Iterator`
-   --> $DIR/map_flatten_fixable.rs:59:10
++  --> $DIR/map_flatten_fixable.rs:38:10
 +   |
 +LL |           .map(|n| match n {
 +   |  __________^
 +LL | |             1 => [n
 +LL | |                 .saturating_add(1)
 +LL | |                 .saturating_add(1)
 +...  |
 +LL | |         })
 +LL | |         .flatten();
 +   | |__________________^
 +   |
 +help: try replacing `map` with `flat_map` and remove the `.flatten()`
 +   |
 +LL ~         .flat_map(|n| match n {
 +LL +             1 => [n
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)
 +LL +                 .saturating_add(1)],
 +LL +             n => [n],
 +LL ~         });
 +   |
 +
 +error: called `map(..).flatten()` on `Option`
++  --> $DIR/map_flatten_fixable.rs:58:10
 +   |
 +LL |           .map(|_| {
 +   |  __________^
 +LL | | // we need some newlines
 +LL | | // so that the span is big enough
 +LL | | // for a split output of the diagnostic
 +...  |
 +LL | |         })
 +LL | |         .flatten();
 +   | |__________________^
 +   |
 +help: try replacing `map` with `and_then` and remove the `.flatten()`
 +   |
 +LL ~         .and_then(|_| {
 +LL + // we need some newlines
 +LL + // so that the span is big enough
 +LL + // for a split output of the diagnostic
 +LL +             Some("")
 +LL +  // whitespace beforehand is important as well
 +LL ~         });
 +   |
 +
 +error: aborting due to 9 previous errors
 +
index 2498007694c560ec3c32679db92621ef1fdefbb4,0000000000000000000000000000000000000000..968f462f8a029d19f2d5dfae483dcc976d403496
mode 100644,000000..100644
--- /dev/null
@@@ -1,211 -1,0 +1,216 @@@
- #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +// run-rustfix
 +
 +#![feature(custom_inner_attributes)]
 +#![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,
 +    };
 +}
 +
 +fn msrv_1_41() {
 +    #![clippy::msrv = "1.41"]
 +
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
 +
 +fn msrv_1_42() {
 +    #![clippy::msrv = "1.42"]
 +
 +    let _y = matches!(Some(5), Some(0));
 +}
index b4e48499bd0fb193fca123c306f129d24543e904,0000000000000000000000000000000000000000..c6b479e27c5aca477a73e782291f7663412669bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,255 -1,0 +1,260 @@@
- #![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +// run-rustfix
 +
 +#![feature(custom_inner_attributes)]
 +#![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,
 +    };
 +}
 +
 +fn msrv_1_41() {
 +    #![clippy::msrv = "1.41"]
 +
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
 +
 +fn msrv_1_42() {
 +    #![clippy::msrv = "1.42"]
 +
 +    let _y = match Some(5) {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +}
index f1d1c23aeb0de7b477a497e536a66e8bd6dcb2b4,0000000000000000000000000000000000000000..a4df8008ac239288436dc9919382b7f2304e58d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,147 @@@
-   --> $DIR/match_expr_like_matches_macro.rs:11:14
 +error: match expression looks like `matches!` macro
-   --> $DIR/match_expr_like_matches_macro.rs:17:14
++  --> $DIR/match_expr_like_matches_macro.rs:16: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:23:14
++  --> $DIR/match_expr_like_matches_macro.rs:22: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:29:15
++  --> $DIR/match_expr_like_matches_macro.rs:28: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:35:16
++  --> $DIR/match_expr_like_matches_macro.rs:34: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:59:20
++  --> $DIR/match_expr_like_matches_macro.rs:40: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:69:20
++  --> $DIR/match_expr_like_matches_macro.rs:64: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:79:20
++  --> $DIR/match_expr_like_matches_macro.rs:74: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:139:18
++  --> $DIR/match_expr_like_matches_macro.rs:84: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:148:18
++  --> $DIR/match_expr_like_matches_macro.rs:144: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:165:21
++  --> $DIR/match_expr_like_matches_macro.rs:153: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:179:20
++  --> $DIR/match_expr_like_matches_macro.rs:170: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:191:20
++  --> $DIR/match_expr_like_matches_macro.rs:184: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:251:14
++  --> $DIR/match_expr_like_matches_macro.rs:196: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
 +   |
 +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 c9ded7f1ad03e2296771cf9e16f9642ed228ef20,0000000000000000000000000000000000000000..183c262ce0b519072d38d33506a729e5bb927387
mode 100644,000000..100644
--- /dev/null
@@@ -1,108 -1,0 +1,87 @@@
- LL | / pub fn unwrap() {
- LL | |     let result = Err("Hi");
- LL | |     result.unwrap()
- LL | | }
-    | |_^
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:6:1
 +   |
- LL | / pub fn panic() {
- LL | |     panic!("This function panics")
- LL | | }
-    | |_^
++LL | pub fn unwrap() {
++   | ^^^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:8:5
 +   |
 +LL |     result.unwrap()
 +   |     ^^^^^^^^^^^^^^^
 +   = note: `-D clippy::missing-panics-doc` implied by `-D warnings`
 +
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:12:1
 +   |
- LL | / pub fn todo() {
- LL | |     todo!()
- LL | | }
-    | |_^
++LL | pub fn panic() {
++   | ^^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:13:5
 +   |
 +LL |     panic!("This function panics")
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:17:1
 +   |
- LL | / pub fn inner_body(opt: Option<u32>) {
- LL | |     opt.map(|x| {
- LL | |         if x == 10 {
- LL | |             panic!()
- LL | |         }
- LL | |     });
- LL | | }
-    | |_^
++LL | pub fn todo() {
++   | ^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:18:5
 +   |
 +LL |     todo!()
 +   |     ^^^^^^^
 +
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:22:1
 +   |
- LL | / pub fn unreachable_and_panic() {
- LL | |     if true { unreachable!() } else { panic!() }
- LL | | }
-    | |_^
++LL | pub fn inner_body(opt: Option<u32>) {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:25:13
 +   |
 +LL |             panic!()
 +   |             ^^^^^^^^
 +
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:31:1
 +   |
- LL | / pub fn assert_eq() {
- LL | |     let x = 0;
- LL | |     assert_eq!(x, 0);
- LL | | }
-    | |_^
++LL | pub fn unreachable_and_panic() {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:32:39
 +   |
 +LL |     if true { unreachable!() } else { panic!() }
 +   |                                       ^^^^^^^^
 +
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:36:1
 +   |
- LL | / pub fn assert_ne() {
- LL | |     let x = 0;
- LL | |     assert_ne!(x, 0);
- LL | | }
-    | |_^
++LL | pub fn assert_eq() {
++   | ^^^^^^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:38:5
 +   |
 +LL |     assert_eq!(x, 0);
 +   |     ^^^^^^^^^^^^^^^^
 +
 +error: docs for function which may panic missing `# Panics` section
 +  --> $DIR/missing_panics_doc.rs:42:1
 +   |
++LL | pub fn assert_ne() {
++   | ^^^^^^^^^^^^^^^^^^
 +   |
 +note: first possible panic found here
 +  --> $DIR/missing_panics_doc.rs:44:5
 +   |
 +LL |     assert_ne!(x, 0);
 +   |     ^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 7 previous errors
 +
index 370dbd5882161c837c4ffd14a531c99aa7145f0d,0000000000000000000000000000000000000000..7de15330594702cafa4baef7a41f4e058095caa1
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,54 @@@
- #![allow(unused)]
++#![allow(unused, clippy::needless_lifetimes)]
 +#![warn(clippy::mut_from_ref)]
 +
 +struct Foo;
 +
 +impl Foo {
 +    fn this_wont_hurt_a_bit(&self) -> &mut Foo {
 +        unsafe { unimplemented!() }
 +    }
 +}
 +
 +trait Ouch {
 +    fn ouch(x: &Foo) -> &mut Foo;
 +}
 +
 +impl Ouch for Foo {
 +    fn ouch(x: &Foo) -> &mut Foo {
 +        unsafe { unimplemented!() }
 +    }
 +}
 +
 +fn fail(x: &u32) -> &mut u16 {
 +    unsafe { unimplemented!() }
 +}
 +
 +fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 {
 +    unsafe { unimplemented!() }
 +}
 +
 +fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 {
 +    unsafe { unimplemented!() }
 +}
 +
 +// this is OK, because the result borrows y
 +fn works<'a>(x: &u32, y: &'a mut u32) -> &'a mut u32 {
 +    unsafe { unimplemented!() }
 +}
 +
 +// this is also OK, because the result could borrow y
 +fn also_works<'a>(x: &'a u32, y: &'a mut u32) -> &'a mut u32 {
 +    unsafe { unimplemented!() }
 +}
 +
 +unsafe fn also_broken(x: &u32) -> &mut u32 {
 +    unimplemented!()
 +}
 +
 +fn without_unsafe(x: &u32) -> &mut u32 {
 +    unimplemented!()
 +}
 +
 +fn main() {
 +    //TODO
 +}
index ac8fd9d8fb0937c841491f15e995af378c3b25e3,0000000000000000000000000000000000000000..ee3a856566cc1d40cb000c91e3da40819978d918
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,76 @@@
 +// aux-build:macro_rules.rs
 +#![warn(clippy::mut_mut)]
 +#![allow(unused)]
 +#![allow(clippy::no_effect, clippy::uninlined_format_args, clippy::unnecessary_operation)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +fn fun(x: &mut &mut u32) -> bool {
 +    **x > 0
 +}
 +
 +fn less_fun(x: *mut *mut u32) {
 +    let y = x;
 +}
 +
 +macro_rules! mut_ptr {
 +    ($p:expr) => {
 +        &mut $p
 +    };
 +}
 +
 +#[allow(unused_mut, unused_variables)]
 +fn main() {
 +    let mut x = &mut &mut 1u32;
 +    {
 +        let mut y = &mut x;
 +    }
 +
 +    if fun(x) {
 +        let y: &mut &mut u32 = &mut &mut 2;
 +        **y + **x;
 +    }
 +
 +    if fun(x) {
 +        let y: &mut &mut &mut u32 = &mut &mut &mut 2;
 +        ***y + **x;
 +    }
 +
 +    let mut z = mut_ptr!(&mut 3u32);
 +}
 +
 +fn issue939() {
 +    let array = [5, 6, 7, 8, 9];
 +    let mut args = array.iter().skip(2);
 +    for &arg in &mut args {
 +        println!("{}", arg);
 +    }
 +
 +    let args = &mut args;
 +    for arg in args {
 +        println!(":{}", arg);
 +    }
 +}
 +
 +fn issue6922() {
 +    // do not lint from an external macro
 +    mut_mut!();
 +}
++
++mod issue9035 {
++    use std::fmt::Display;
++
++    struct Foo<'a> {
++        inner: &'a mut dyn Display,
++    }
++
++    impl Foo<'_> {
++        fn foo(&mut self) {
++            let hlp = &mut self.inner;
++            bar(hlp);
++        }
++    }
++
++    fn bar(_: &mut impl Display) {}
++}
index e1ae1ef928223bf748cd6c21c5bf2af27d962a43,0000000000000000000000000000000000000000..7fdeb27ed988fcdc39c83daef4d623fe930405ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,84 -1,0 +1,84 @@@
-             n = 1; // FIXME: warning because is is not immediately followed by break
 +#![allow(unused)]
 +
 +fn main() {}
 +
 +fn mut_range_bound_upper() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        m = 5;
 +    } // warning
 +}
 +
 +fn mut_range_bound_lower() {
 +    let mut m = 4;
 +    for i in m..10 {
 +        m *= 2;
 +    } // warning
 +}
 +
 +fn mut_range_bound_both() {
 +    let mut m = 4;
 +    let mut n = 6;
 +    for i in m..n {
 +        m = 5;
 +        n = 7;
 +    } // warning (1 for each mutated bound)
 +}
 +
 +fn mut_range_bound_no_mutation() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        continue;
 +    } // no warning
 +}
 +
 +fn mut_borrow_range_bound() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        let n = &mut m; // warning
 +        *n += 1;
 +    }
 +}
 +
 +fn immut_borrow_range_bound() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        let n = &m; // should be no warning?
 +    }
 +}
 +
 +fn immut_range_bound() {
 +    let m = 4;
 +    for i in 0..m {
 +        continue;
 +    } // no warning
 +}
 +
 +fn mut_range_bound_break() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        if m == 4 {
 +            m = 5; // no warning because of immediate break
 +            break;
 +        }
 +    }
 +}
 +
 +fn mut_range_bound_no_immediate_break() {
 +    let mut m = 4;
 +    for i in 0..m {
 +        m = 2; // warning because it is not immediately followed by break
 +        if m == 4 {
 +            break;
 +        }
 +    }
 +
 +    let mut n = 3;
 +    for i in n..10 {
 +        if n == 4 {
++            n = 1; // FIXME: warning because it is not immediately followed by break
 +            let _ = 2;
 +            break;
 +        }
 +    }
 +}
index e0c8dced382efc0b390f0d07f601d817c0141b73,0000000000000000000000000000000000000000..b679b7a0aaf82ff7e351c2497fb216df920c2622
mode 100644,000000..100644
--- /dev/null
@@@ -1,59 -1,0 +1,59 @@@
- LL |             n = 1; // FIXME: warning because is is not immediately followed by break
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:8:9
 +   |
 +LL |         m = 5;
 +   |         ^
 +   |
 +   = note: the range of the loop is unchanged
 +   = note: `-D clippy::mut-range-bound` implied by `-D warnings`
 +
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:15:9
 +   |
 +LL |         m *= 2;
 +   |         ^
 +   |
 +   = note: the range of the loop is unchanged
 +
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:23:9
 +   |
 +LL |         m = 5;
 +   |         ^
 +   |
 +   = note: the range of the loop is unchanged
 +
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:24:9
 +   |
 +LL |         n = 7;
 +   |         ^
 +   |
 +   = note: the range of the loop is unchanged
 +
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:38:22
 +   |
 +LL |         let n = &mut m; // warning
 +   |                      ^
 +   |
 +   = note: the range of the loop is unchanged
 +
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:70:9
 +   |
 +LL |         m = 2; // warning because it is not immediately followed by break
 +   |         ^
 +   |
 +   = note: the range of the loop is unchanged
 +
 +error: attempt to mutate range bound within loop
 +  --> $DIR/mut_range_bound.rs:79:13
 +   |
++LL |             n = 1; // FIXME: warning because it is not immediately followed by break
 +   |             ^
 +   |
 +   = note: the range of the loop is unchanged
 +
 +error: aborting due to 7 previous errors
 +
index 340e89d2db1d2c25d97f14fd242ede26de1bb2eb,0000000000000000000000000000000000000000..85b6b639d5549c9b6636597617a8c486370a7863
mode 100644,000000..100644
--- /dev/null
@@@ -1,387 -1,0 +1,512 @@@
 +// run-rustfix
 +#![feature(custom_inner_attributes, lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables)]
 +#[allow(
 +    clippy::uninlined_format_args,
 +    clippy::unnecessary_mut_passed,
 +    clippy::unnecessary_to_owned
 +)]
 +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)]
 +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)
 +    }
 +}
 +
 +mod under_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.52.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +mod meets_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.53.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +#[allow(unused)]
 +fn issue9383() {
 +    // Should not lint because unions need explicit deref when accessing field
 +    use std::mem::ManuallyDrop;
 +
 +    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);
++    }
++}
++
++#[allow(dead_code)]
++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 c93711ac8e28490feb37a078d14f66da07e904a7,0000000000000000000000000000000000000000..7b97bcf3817ec0bc60c4b93acdfdabf1de063a70
mode 100644,000000..100644
--- /dev/null
@@@ -1,387 -1,0 +1,512 @@@
 +// run-rustfix
 +#![feature(custom_inner_attributes, lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables)]
 +#[allow(
 +    clippy::uninlined_format_args,
 +    clippy::unnecessary_mut_passed,
 +    clippy::unnecessary_to_owned
 +)]
 +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)]
 +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)
 +    }
 +}
 +
 +mod under_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.52.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +mod meets_msrv {
 +    #![allow(dead_code)]
 +    #![clippy::msrv = "1.53.0"]
 +
 +    fn foo() {
 +        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +    }
 +}
 +
 +#[allow(unused)]
 +fn issue9383() {
 +    // Should not lint because unions need explicit deref when accessing field
 +    use std::mem::ManuallyDrop;
 +
 +    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);
++    }
++}
++
++#[allow(dead_code)]
++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 8b593268bec2112b82b70c268d4c5d1ffafa9127,0000000000000000000000000000000000000000..485e6b84c868b11972932b16a35f6e664e7ae6f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,214 -1,0 +1,220 @@@
- error: aborting due to 35 previous errors
 +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:192:13
 +   |
 +LL |             (&self.f)()
 +   |             ^^^^^^^^^ help: change this to: `(self.f)`
 +
 +error: this expression borrows a value the compiler would automatically borrow
 +  --> $DIR/needless_borrow.rs:201:13
 +   |
 +LL |             (&mut self.f)()
 +   |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:286:20
 +   |
 +LL |         takes_iter(&mut x)
 +   |                    ^^^^^^ help: change this to: `x`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:304:55
 +   |
 +LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
 +   |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:344:37
 +   |
 +LL |         let _ = std::fs::write("x", &arg);
 +   |                                     ^^^^ help: change this to: `arg`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:345:37
 +   |
 +LL |         let _ = std::fs::write("x", &loc);
 +   |                                     ^^^^ help: change this to: `loc`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:364:15
 +   |
 +LL |         debug(&x);
 +   |               ^^ help: change this to: `x`
 +
 +error: the borrowed expression implements the required traits
 +  --> $DIR/needless_borrow.rs:374:15
 +   |
 +LL |         use_x(&x);
 +   |               ^^ help: change this to: `x`
 +
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:474:13
++   |
++LL |         foo(&a);
++   |             ^^ help: change this to: `a`
++
++error: aborting due to 36 previous errors
 +
index bcb4eb2dd48a65615d4241b5f8f48f1370abd12c,0000000000000000000000000000000000000000..0c47ceb7b6791594e877bbb0e9388bfec9815bee
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,123 @@@
- #![allow(unused, clippy::needless_borrow)]
 +// run-rustfix
 +
 +#![warn(clippy::needless_borrowed_reference)]
- fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec<u8>) {
++#![allow(
++    unused,
++    irrefutable_let_patterns,
++    non_shorthand_field_patterns,
++    clippy::needless_borrow
++)]
 +
 +fn main() {}
 +
- fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec<u8>) {
++struct Struct {
++    a: usize,
++    b: usize,
++    c: usize,
++}
++
++struct TupleStruct(u8, u8, u8);
++
++fn should_lint(
++    array: [u8; 4],
++    slice: &[u8],
++    slice_of_refs: &[&u8],
++    vec: Vec<u8>,
++    tuple: (u8, u8, u8),
++    tuple_struct: TupleStruct,
++    s: Struct,
++) {
 +    let mut v = Vec::<String>::new();
 +    let _ = v.iter_mut().filter(|a| a.is_empty());
 +
 +    let var = 3;
 +    let thingy = Some(&var);
 +    if let Some(v) = thingy {}
 +
 +    if let &[a, ref b] = slice_of_refs {}
 +
 +    let [a, ..] = &array;
 +    let [a, b, ..] = &array;
 +
 +    if let [a, b] = slice {}
 +    if let [a, b] = &vec[..] {}
 +
 +    if let [a, b, ..] = slice {}
 +    if let [a, .., b] = slice {}
 +    if let [.., a, b] = slice {}
++
++    if let [a, _] = slice {}
++
++    if let (a, b, c) = &tuple {}
++    if let (a, _, c) = &tuple {}
++    if let (a, ..) = &tuple {}
++
++    if let TupleStruct(a, ..) = &tuple_struct {}
++
++    if let Struct {
++        a,
++        b: b,
++        c: renamed,
++    } = &s
++    {}
++
++    if let Struct { a, b: _, .. } = &s {}
 +}
 +
-         (&Animal::Dog(ref a), &Animal::Dog(_)) => (), //              ^ should **not** be linted
++fn should_not_lint(
++    array: [u8; 4],
++    slice: &[u8],
++    slice_of_refs: &[&u8],
++    vec: Vec<u8>,
++    tuple: (u8, u8, u8),
++    tuple_struct: TupleStruct,
++    s: Struct,
++) {
 +    if let [ref a] = slice {}
 +    if let &[ref a, b] = slice {}
 +    if let &[ref a, .., b] = slice {}
 +
++    if let &(ref a, b, ..) = &tuple {}
++    if let &TupleStruct(ref a, b, ..) = &tuple_struct {}
++    if let &Struct { ref a, b, .. } = &s {}
++
 +    // must not be removed as variables must be bound consistently across | patterns
 +    if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {}
 +
++    // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match
++    // the lint name
++    if let &[] = slice {}
++    if let &[_] = slice {}
++    if let &[..] = slice {}
++    if let &(..) = &tuple {}
++    if let &TupleStruct(..) = &tuple_struct {}
++    if let &Struct { .. } = &s {}
++
 +    let mut var2 = 5;
 +    let thingy2 = Some(&mut var2);
 +    if let Some(&mut ref mut v) = thingy2 {
 +        //          ^ should **not** be linted
 +        // v is borrowed as mutable.
 +        *v = 10;
 +    }
 +    if let Some(&mut ref v) = thingy2 {
 +        //          ^ should **not** be linted
 +        // here, v is borrowed as immutable.
 +        // can't do that:
 +        //*v = 15;
 +    }
 +}
 +
 +enum Animal {
 +    Cat(u64),
 +    Dog(u64),
 +}
 +
 +fn foo(a: &Animal, b: &Animal) {
 +    match (a, b) {
 +        // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63
 +        (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (),
 +        //                  ^    and   ^ should **not** be linted
++        (Animal::Dog(a), &Animal::Dog(_)) => (),
 +    }
 +}
index f6de1a6d83d1bbdf965c19a28823899956608e44,0000000000000000000000000000000000000000..f883bb0c889172c64a4155647e34055bd430fadb
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,123 @@@
- #![allow(unused, clippy::needless_borrow)]
 +// run-rustfix
 +
 +#![warn(clippy::needless_borrowed_reference)]
- fn should_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec<u8>) {
++#![allow(
++    unused,
++    irrefutable_let_patterns,
++    non_shorthand_field_patterns,
++    clippy::needless_borrow
++)]
 +
 +fn main() {}
 +
- fn should_not_lint(array: [u8; 4], slice: &[u8], slice_of_refs: &[&u8], vec: Vec<u8>) {
++struct Struct {
++    a: usize,
++    b: usize,
++    c: usize,
++}
++
++struct TupleStruct(u8, u8, u8);
++
++fn should_lint(
++    array: [u8; 4],
++    slice: &[u8],
++    slice_of_refs: &[&u8],
++    vec: Vec<u8>,
++    tuple: (u8, u8, u8),
++    tuple_struct: TupleStruct,
++    s: Struct,
++) {
 +    let mut v = Vec::<String>::new();
 +    let _ = v.iter_mut().filter(|&ref a| a.is_empty());
 +
 +    let var = 3;
 +    let thingy = Some(&var);
 +    if let Some(&ref v) = thingy {}
 +
 +    if let &[&ref a, ref b] = slice_of_refs {}
 +
 +    let &[ref a, ..] = &array;
 +    let &[ref a, ref b, ..] = &array;
 +
 +    if let &[ref a, ref b] = slice {}
 +    if let &[ref a, ref b] = &vec[..] {}
 +
 +    if let &[ref a, ref b, ..] = slice {}
 +    if let &[ref a, .., ref b] = slice {}
 +    if let &[.., ref a, ref b] = slice {}
++
++    if let &[ref a, _] = slice {}
++
++    if let &(ref a, ref b, ref c) = &tuple {}
++    if let &(ref a, _, ref c) = &tuple {}
++    if let &(ref a, ..) = &tuple {}
++
++    if let &TupleStruct(ref a, ..) = &tuple_struct {}
++
++    if let &Struct {
++        ref a,
++        b: ref b,
++        c: ref renamed,
++    } = &s
++    {}
++
++    if let &Struct { ref a, b: _, .. } = &s {}
 +}
 +
-         (&Animal::Dog(ref a), &Animal::Dog(_)) => (), //              ^ should **not** be linted
++fn should_not_lint(
++    array: [u8; 4],
++    slice: &[u8],
++    slice_of_refs: &[&u8],
++    vec: Vec<u8>,
++    tuple: (u8, u8, u8),
++    tuple_struct: TupleStruct,
++    s: Struct,
++) {
 +    if let [ref a] = slice {}
 +    if let &[ref a, b] = slice {}
 +    if let &[ref a, .., b] = slice {}
 +
++    if let &(ref a, b, ..) = &tuple {}
++    if let &TupleStruct(ref a, b, ..) = &tuple_struct {}
++    if let &Struct { ref a, b, .. } = &s {}
++
 +    // must not be removed as variables must be bound consistently across | patterns
 +    if let (&[ref a], _) | ([], ref a) = (slice_of_refs, &1u8) {}
 +
++    // the `&`s here technically could be removed, but it'd be noisy and without a `ref` doesn't match
++    // the lint name
++    if let &[] = slice {}
++    if let &[_] = slice {}
++    if let &[..] = slice {}
++    if let &(..) = &tuple {}
++    if let &TupleStruct(..) = &tuple_struct {}
++    if let &Struct { .. } = &s {}
++
 +    let mut var2 = 5;
 +    let thingy2 = Some(&mut var2);
 +    if let Some(&mut ref mut v) = thingy2 {
 +        //          ^ should **not** be linted
 +        // v is borrowed as mutable.
 +        *v = 10;
 +    }
 +    if let Some(&mut ref v) = thingy2 {
 +        //          ^ should **not** be linted
 +        // here, v is borrowed as immutable.
 +        // can't do that:
 +        //*v = 15;
 +    }
 +}
 +
 +enum Animal {
 +    Cat(u64),
 +    Dog(u64),
 +}
 +
 +fn foo(a: &Animal, b: &Animal) {
 +    match (a, b) {
 +        // lifetime mismatch error if there is no '&ref' before `feature(nll)` stabilization in 1.63
 +        (&Animal::Cat(v), &ref k) | (&ref k, &Animal::Cat(v)) => (),
 +        //                  ^    and   ^ should **not** be linted
++        (Animal::Dog(a), &Animal::Dog(_)) => (),
 +    }
 +}
index 7453542e673f3bbf5f07f0003b408d4c9c77310f,0000000000000000000000000000000000000000..8d0f0c258dd26cc292d6e3eff5ab58140de082e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,214 @@@
-   --> $DIR/needless_borrowed_ref.rs:10:34
 +error: this pattern takes a reference on something that is being dereferenced
-   --> $DIR/needless_borrowed_ref.rs:14:17
++  --> $DIR/needless_borrowed_ref.rs:31:34
 +   |
 +LL |     let _ = v.iter_mut().filter(|&ref a| a.is_empty());
 +   |                                  ^^^^^^
 +   |
 +   = note: `-D clippy::needless-borrowed-reference` implied by `-D warnings`
 +help: try removing the `&ref` part
 +   |
 +LL -     let _ = v.iter_mut().filter(|&ref a| a.is_empty());
 +LL +     let _ = v.iter_mut().filter(|a| a.is_empty());
 +   |
 +
 +error: this pattern takes a reference on something that is being dereferenced
-   --> $DIR/needless_borrowed_ref.rs:16:14
++  --> $DIR/needless_borrowed_ref.rs:35:17
 +   |
 +LL |     if let Some(&ref v) = thingy {}
 +   |                 ^^^^^^
 +   |
 +help: try removing the `&ref` part
 +   |
 +LL -     if let Some(&ref v) = thingy {}
 +LL +     if let Some(v) = thingy {}
 +   |
 +
 +error: this pattern takes a reference on something that is being dereferenced
-   --> $DIR/needless_borrowed_ref.rs:18:9
++  --> $DIR/needless_borrowed_ref.rs:37:14
 +   |
 +LL |     if let &[&ref a, ref b] = slice_of_refs {}
 +   |              ^^^^^^
 +   |
 +help: try removing the `&ref` part
 +   |
 +LL -     if let &[&ref a, ref b] = slice_of_refs {}
 +LL +     if let &[a, ref b] = slice_of_refs {}
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
-   --> $DIR/needless_borrowed_ref.rs:19:9
++  --> $DIR/needless_borrowed_ref.rs:39:9
 +   |
 +LL |     let &[ref a, ..] = &array;
 +   |         ^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     let &[ref a, ..] = &array;
 +LL +     let [a, ..] = &array;
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
-   --> $DIR/needless_borrowed_ref.rs:21:12
++  --> $DIR/needless_borrowed_ref.rs:40:9
 +   |
 +LL |     let &[ref a, ref b, ..] = &array;
 +   |         ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     let &[ref a, ref b, ..] = &array;
 +LL +     let [a, b, ..] = &array;
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
-   --> $DIR/needless_borrowed_ref.rs:22:12
++  --> $DIR/needless_borrowed_ref.rs:42:12
 +   |
 +LL |     if let &[ref a, ref b] = slice {}
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     if let &[ref a, ref b] = slice {}
 +LL +     if let [a, b] = slice {}
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
-   --> $DIR/needless_borrowed_ref.rs:24:12
++  --> $DIR/needless_borrowed_ref.rs:43:12
 +   |
 +LL |     if let &[ref a, ref b] = &vec[..] {}
 +   |            ^^^^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     if let &[ref a, ref b] = &vec[..] {}
 +LL +     if let [a, b] = &vec[..] {}
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
-   --> $DIR/needless_borrowed_ref.rs:25:12
++  --> $DIR/needless_borrowed_ref.rs:45:12
 +   |
 +LL |     if let &[ref a, ref b, ..] = slice {}
 +   |            ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     if let &[ref a, ref b, ..] = slice {}
 +LL +     if let [a, b, ..] = slice {}
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
-   --> $DIR/needless_borrowed_ref.rs:26:12
++  --> $DIR/needless_borrowed_ref.rs:46:12
 +   |
 +LL |     if let &[ref a, .., ref b] = slice {}
 +   |            ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     if let &[ref a, .., ref b] = slice {}
 +LL +     if let [a, .., b] = slice {}
 +   |
 +
 +error: dereferencing a slice pattern where every element takes a reference
- error: aborting due to 10 previous errors
++  --> $DIR/needless_borrowed_ref.rs:47:12
 +   |
 +LL |     if let &[.., ref a, ref b] = slice {}
 +   |            ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try removing the `&` and `ref` parts
 +   |
 +LL -     if let &[.., ref a, ref b] = slice {}
 +LL +     if let [.., a, b] = slice {}
 +   |
 +
++error: dereferencing a slice pattern where every element takes a reference
++  --> $DIR/needless_borrowed_ref.rs:49:12
++   |
++LL |     if let &[ref a, _] = slice {}
++   |            ^^^^^^^^^^^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL -     if let &[ref a, _] = slice {}
++LL +     if let [a, _] = slice {}
++   |
++
++error: dereferencing a tuple pattern where every element takes a reference
++  --> $DIR/needless_borrowed_ref.rs:51:12
++   |
++LL |     if let &(ref a, ref b, ref c) = &tuple {}
++   |            ^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL -     if let &(ref a, ref b, ref c) = &tuple {}
++LL +     if let (a, b, c) = &tuple {}
++   |
++
++error: dereferencing a tuple pattern where every element takes a reference
++  --> $DIR/needless_borrowed_ref.rs:52:12
++   |
++LL |     if let &(ref a, _, ref c) = &tuple {}
++   |            ^^^^^^^^^^^^^^^^^^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL -     if let &(ref a, _, ref c) = &tuple {}
++LL +     if let (a, _, c) = &tuple {}
++   |
++
++error: dereferencing a tuple pattern where every element takes a reference
++  --> $DIR/needless_borrowed_ref.rs:53:12
++   |
++LL |     if let &(ref a, ..) = &tuple {}
++   |            ^^^^^^^^^^^^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL -     if let &(ref a, ..) = &tuple {}
++LL +     if let (a, ..) = &tuple {}
++   |
++
++error: dereferencing a tuple pattern where every element takes a reference
++  --> $DIR/needless_borrowed_ref.rs:55:12
++   |
++LL |     if let &TupleStruct(ref a, ..) = &tuple_struct {}
++   |            ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL -     if let &TupleStruct(ref a, ..) = &tuple_struct {}
++LL +     if let TupleStruct(a, ..) = &tuple_struct {}
++   |
++
++error: dereferencing a struct pattern where every field's pattern takes a reference
++  --> $DIR/needless_borrowed_ref.rs:57:12
++   |
++LL |       if let &Struct {
++   |  ____________^
++LL | |         ref a,
++LL | |         b: ref b,
++LL | |         c: ref renamed,
++LL | |     } = &s
++   | |_____^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL ~     if let Struct {
++LL ~         a,
++LL ~         b: b,
++LL ~         c: renamed,
++   |
++
++error: dereferencing a struct pattern where every field's pattern takes a reference
++  --> $DIR/needless_borrowed_ref.rs:64:12
++   |
++LL |     if let &Struct { ref a, b: _, .. } = &s {}
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: try removing the `&` and `ref` parts
++   |
++LL -     if let &Struct { ref a, b: _, .. } = &s {}
++LL +     if let Struct { a, b: _, .. } = &s {}
++   |
++
++error: aborting due to 17 previous errors
 +
index 6ecbbcb62495553c3630cff05f0dc4dc871b6639,0000000000000000000000000000000000000000..2659ad384885e6a3e8efaea935716376a05e8667
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,65 @@@
 +// run-rustfix
 +
 +#![allow(unused, clippy::suspicious_map, clippy::iter_count)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
 +
 +#[warn(clippy::needless_collect)]
 +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
 +fn main() {
 +    let sample = [1; 5];
 +    let len = sample.iter().count();
 +    if sample.iter().next().is_none() {
 +        // Empty
 +    }
 +    sample.iter().cloned().any(|x| x == 1);
 +    // #7164 HashMap's and BTreeMap's `len` usage should not be linted
 +    sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
 +    sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().len();
 +
 +    sample.iter().map(|x| (x, x)).next().is_none();
 +    sample.iter().map(|x| (x, x)).next().is_none();
 +
 +    // Notice the `HashSet`--this should not be linted
 +    sample.iter().collect::<HashSet<_>>().len();
 +    // Neither should this
 +    sample.iter().collect::<BTreeSet<_>>().len();
 +
 +    sample.iter().count();
 +    sample.iter().next().is_none();
 +    sample.iter().cloned().any(|x| x == 1);
 +    sample.iter().any(|x| x == &1);
 +
 +    // `BinaryHeap` doesn't have `contains` method
 +    sample.iter().count();
 +    sample.iter().next().is_none();
++
++    // Don't lint string from str
++    let _ = ["", ""].into_iter().collect::<String>().is_empty();
++
++    let _ = sample.iter().next().is_none();
++    let _ = sample.iter().any(|x| x == &0);
++
++    struct VecWrapper<T>(Vec<T>);
++    impl<T> core::ops::Deref for VecWrapper<T> {
++        type Target = Vec<T>;
++        fn deref(&self) -> &Self::Target {
++            &self.0
++        }
++    }
++    impl<T> IntoIterator for VecWrapper<T> {
++        type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
++        type Item = <Vec<T> as IntoIterator>::Item;
++        fn into_iter(self) -> Self::IntoIter {
++            self.0.into_iter()
++        }
++    }
++    impl<T> FromIterator<T> for VecWrapper<T> {
++        fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
++            Self(Vec::from_iter(iter))
++        }
++    }
++
++    let _ = sample.iter().next().is_none();
++    let _ = sample.iter().any(|x| x == &0);
 +}
index 8dc69bcf5b38df30295e0539b1a038916613861e,0000000000000000000000000000000000000000..535ec82982b13bcf45493412a527dfc38a886343
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,65 @@@
 +// run-rustfix
 +
 +#![allow(unused, clippy::suspicious_map, clippy::iter_count)]
 +
 +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList};
 +
 +#[warn(clippy::needless_collect)]
 +#[allow(unused_variables, clippy::iter_cloned_collect, clippy::iter_next_slice)]
 +fn main() {
 +    let sample = [1; 5];
 +    let len = sample.iter().collect::<Vec<_>>().len();
 +    if sample.iter().collect::<Vec<_>>().is_empty() {
 +        // Empty
 +    }
 +    sample.iter().cloned().collect::<Vec<_>>().contains(&1);
 +    // #7164 HashMap's and BTreeMap's `len` usage should not be linted
 +    sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().len();
 +    sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().len();
 +
 +    sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty();
 +    sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty();
 +
 +    // Notice the `HashSet`--this should not be linted
 +    sample.iter().collect::<HashSet<_>>().len();
 +    // Neither should this
 +    sample.iter().collect::<BTreeSet<_>>().len();
 +
 +    sample.iter().collect::<LinkedList<_>>().len();
 +    sample.iter().collect::<LinkedList<_>>().is_empty();
 +    sample.iter().cloned().collect::<LinkedList<_>>().contains(&1);
 +    sample.iter().collect::<LinkedList<_>>().contains(&&1);
 +
 +    // `BinaryHeap` doesn't have `contains` method
 +    sample.iter().collect::<BinaryHeap<_>>().len();
 +    sample.iter().collect::<BinaryHeap<_>>().is_empty();
++
++    // Don't lint string from str
++    let _ = ["", ""].into_iter().collect::<String>().is_empty();
++
++    let _ = sample.iter().collect::<HashSet<_>>().is_empty();
++    let _ = sample.iter().collect::<HashSet<_>>().contains(&&0);
++
++    struct VecWrapper<T>(Vec<T>);
++    impl<T> core::ops::Deref for VecWrapper<T> {
++        type Target = Vec<T>;
++        fn deref(&self) -> &Self::Target {
++            &self.0
++        }
++    }
++    impl<T> IntoIterator for VecWrapper<T> {
++        type IntoIter = <Vec<T> as IntoIterator>::IntoIter;
++        type Item = <Vec<T> as IntoIterator>::Item;
++        fn into_iter(self) -> Self::IntoIter {
++            self.0.into_iter()
++        }
++    }
++    impl<T> FromIterator<T> for VecWrapper<T> {
++        fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
++            Self(Vec::from_iter(iter))
++        }
++    }
++
++    let _ = sample.iter().collect::<VecWrapper<_>>().is_empty();
++    let _ = sample.iter().collect::<VecWrapper<_>>().contains(&&0);
 +}
index 039091627a8d632c3fed7cabff373e95e4315bfc,0000000000000000000000000000000000000000..584d2a1d8356f945292672f16fdb32f758b4da79
mode 100644,000000..100644
--- /dev/null
@@@ -1,70 -1,0 +1,94 @@@
- error: aborting due to 11 previous errors
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:11:29
 +   |
 +LL |     let len = sample.iter().collect::<Vec<_>>().len();
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
 +   |
 +   = note: `-D clippy::needless-collect` implied by `-D warnings`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:12:22
 +   |
 +LL |     if sample.iter().collect::<Vec<_>>().is_empty() {
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:15:28
 +   |
 +LL |     sample.iter().cloned().collect::<Vec<_>>().contains(&1);
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:20:35
 +   |
 +LL |     sample.iter().map(|x| (x, x)).collect::<HashMap<_, _>>().is_empty();
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:21:35
 +   |
 +LL |     sample.iter().map(|x| (x, x)).collect::<BTreeMap<_, _>>().is_empty();
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:28:19
 +   |
 +LL |     sample.iter().collect::<LinkedList<_>>().len();
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:29:19
 +   |
 +LL |     sample.iter().collect::<LinkedList<_>>().is_empty();
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:30:28
 +   |
 +LL |     sample.iter().cloned().collect::<LinkedList<_>>().contains(&1);
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == 1)`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:31:19
 +   |
 +LL |     sample.iter().collect::<LinkedList<_>>().contains(&&1);
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &1)`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:34:19
 +   |
 +LL |     sample.iter().collect::<BinaryHeap<_>>().len();
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `count()`
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect.rs:35:19
 +   |
 +LL |     sample.iter().collect::<BinaryHeap<_>>().is_empty();
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
 +
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:40:27
++   |
++LL |     let _ = sample.iter().collect::<HashSet<_>>().is_empty();
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:41:27
++   |
++LL |     let _ = sample.iter().collect::<HashSet<_>>().contains(&&0);
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)`
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:63:27
++   |
++LL |     let _ = sample.iter().collect::<VecWrapper<_>>().is_empty();
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()`
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect.rs:64:27
++   |
++LL |     let _ = sample.iter().collect::<VecWrapper<_>>().contains(&&0);
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `any(|x| x == &0)`
++
++error: aborting due to 15 previous errors
 +
index 6d213b46c20cb2f424e93f70f38e64e721ff49fe,0000000000000000000000000000000000000000..fe4209e99b2f56ec85ea4a266bb4c72432ff85d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,305 -1,0 +1,306 @@@
 +#![allow(clippy::uninlined_format_args)]
++#![warn(clippy::needless_collect)]
 +
 +use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +
 +fn main() {
 +    let sample = [1; 5];
 +    let indirect_iter = sample.iter().collect::<Vec<_>>();
 +    indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +    let indirect_len = sample.iter().collect::<VecDeque<_>>();
 +    indirect_len.len();
 +    let indirect_empty = sample.iter().collect::<VecDeque<_>>();
 +    indirect_empty.is_empty();
 +    let indirect_contains = sample.iter().collect::<VecDeque<_>>();
 +    indirect_contains.contains(&&5);
 +    let indirect_negative = sample.iter().collect::<Vec<_>>();
 +    indirect_negative.len();
 +    indirect_negative
 +        .into_iter()
 +        .map(|x| (*x, *x + 1))
 +        .collect::<HashMap<_, _>>();
 +
 +    // #6202
 +    let a = "a".to_string();
 +    let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
 +    let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
 +    non_copy_contains.contains(&a);
 +
 +    // Fix #5991
 +    let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 +    let vec_b = vec_a.iter().collect::<Vec<_>>();
 +    if vec_b.len() > 3 {}
 +    let other_vec = vec![1, 3, 12, 4, 16, 2];
 +    let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::<Vec<_>>();
 +
 +    // Fix #6297
 +    let sample = [1; 5];
 +    let multiple_indirect = sample.iter().collect::<Vec<_>>();
 +    let sample2 = vec![2, 3];
 +    if multiple_indirect.is_empty() {
 +        // do something
 +    } else {
 +        let found = sample2
 +            .iter()
 +            .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0))
 +            .collect::<Vec<_>>();
 +    }
 +}
 +
 +mod issue7110 {
 +    // #7110 - lint for type annotation cases
 +    use super::*;
 +
 +    fn lint_vec(string: &str) -> usize {
 +        let buffer: Vec<&str> = string.split('/').collect();
 +        buffer.len()
 +    }
 +    fn lint_vec_deque() -> usize {
 +        let sample = [1; 5];
 +        let indirect_len: VecDeque<_> = sample.iter().collect();
 +        indirect_len.len()
 +    }
 +    fn lint_linked_list() -> usize {
 +        let sample = [1; 5];
 +        let indirect_len: LinkedList<_> = sample.iter().collect();
 +        indirect_len.len()
 +    }
 +    fn lint_binary_heap() -> usize {
 +        let sample = [1; 5];
 +        let indirect_len: BinaryHeap<_> = sample.iter().collect();
 +        indirect_len.len()
 +    }
 +    fn dont_lint(string: &str) -> usize {
 +        let buffer: Vec<&str> = string.split('/').collect();
 +        for buff in &buffer {
 +            println!("{}", buff);
 +        }
 +        buffer.len()
 +    }
 +}
 +
 +mod issue7975 {
 +    use super::*;
 +
 +    fn direct_mapping_with_used_mutable_reference() -> Vec<()> {
 +        let test_vec: Vec<()> = vec![];
 +        let mut vec_2: Vec<()> = vec![];
 +        let mut_ref = &mut vec_2;
 +        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
 +        collected_vec.into_iter().map(|_| mut_ref.push(())).collect()
 +    }
 +
 +    fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> {
 +        let test_vec: Vec<()> = vec![];
 +        let mut vec_2: Vec<()> = vec![];
 +        let mut_ref = &mut vec_2;
 +        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
 +        let iter = collected_vec.into_iter();
 +        iter.map(|_| mut_ref.push(())).collect()
 +    }
 +
 +    fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> {
 +        let test_vec: Vec<()> = vec![];
 +        let mut vec_2: Vec<()> = vec![];
 +        let mut_ref = &mut vec_2;
 +        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
 +        let iter = collected_vec.into_iter();
 +        let mapped_iter = iter.map(|_| mut_ref.push(()));
 +        mapped_iter.collect()
 +    }
 +}
 +
 +fn allow_test() {
 +    #[allow(clippy::needless_collect)]
 +    let v = [1].iter().collect::<Vec<_>>();
 +    v.into_iter().collect::<HashSet<_>>();
 +}
 +
 +mod issue_8553 {
 +    fn test_for() {
 +        let vec = vec![1, 2];
 +        let w: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +
 +        for i in 0..2 {
 +            // Do not lint, because this method call is in the loop
 +            w.contains(&i);
 +        }
 +
 +        for i in 0..2 {
 +            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +            // Do lint
 +            y.contains(&i);
 +            for j in 0..2 {
 +                // Do not lint, because this method call is in the loop
 +                z.contains(&j);
 +            }
 +        }
 +
 +        // Do not lint, because this variable is used.
 +        w.contains(&0);
 +    }
 +
 +    fn test_while() {
 +        let vec = vec![1, 2];
 +        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let mut n = 0;
 +        while n > 1 {
 +            // Do not lint, because this method call is in the loop
 +            x.contains(&n);
 +            n += 1;
 +        }
 +
 +        while n > 2 {
 +            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +            // Do lint
 +            y.contains(&n);
 +            n += 1;
 +            while n > 4 {
 +                // Do not lint, because this method call is in the loop
 +                z.contains(&n);
 +                n += 1;
 +            }
 +        }
 +    }
 +
 +    fn test_loop() {
 +        let vec = vec![1, 2];
 +        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let mut n = 0;
 +        loop {
 +            if n < 1 {
 +                // Do not lint, because this method call is in the loop
 +                x.contains(&n);
 +                n += 1;
 +            } else {
 +                break;
 +            }
 +        }
 +
 +        loop {
 +            if n < 2 {
 +                let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +                let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +                // Do lint
 +                y.contains(&n);
 +                n += 1;
 +                loop {
 +                    if n < 4 {
 +                        // Do not lint, because this method call is in the loop
 +                        z.contains(&n);
 +                        n += 1;
 +                    } else {
 +                        break;
 +                    }
 +                }
 +            } else {
 +                break;
 +            }
 +        }
 +    }
 +
 +    fn test_while_let() {
 +        let vec = vec![1, 2];
 +        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let optional = Some(0);
 +        let mut n = 0;
 +        while let Some(value) = optional {
 +            if n < 1 {
 +                // Do not lint, because this method call is in the loop
 +                x.contains(&n);
 +                n += 1;
 +            } else {
 +                break;
 +            }
 +        }
 +
 +        while let Some(value) = optional {
 +            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +            if n < 2 {
 +                // Do lint
 +                y.contains(&n);
 +                n += 1;
 +            } else {
 +                break;
 +            }
 +
 +            while let Some(value) = optional {
 +                if n < 4 {
 +                    // Do not lint, because this method call is in the loop
 +                    z.contains(&n);
 +                    n += 1;
 +                } else {
 +                    break;
 +                }
 +            }
 +        }
 +    }
 +
 +    fn test_if_cond() {
 +        let vec = vec![1, 2];
 +        let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let w = v.iter().collect::<Vec<_>>();
 +        // Do lint
 +        for _ in 0..w.len() {
 +            todo!();
 +        }
 +    }
 +
 +    fn test_if_cond_false_case() {
 +        let vec = vec![1, 2];
 +        let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let w = v.iter().collect::<Vec<_>>();
 +        // Do not lint, because w is used.
 +        for _ in 0..w.len() {
 +            todo!();
 +        }
 +
 +        w.len();
 +    }
 +
 +    fn test_while_cond() {
 +        let mut vec = vec![1, 2];
 +        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let mut w = v.iter().collect::<Vec<_>>();
 +        // Do lint
 +        while 1 == w.len() {
 +            todo!();
 +        }
 +    }
 +
 +    fn test_while_cond_false_case() {
 +        let mut vec = vec![1, 2];
 +        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let mut w = v.iter().collect::<Vec<_>>();
 +        // Do not lint, because w is used.
 +        while 1 == w.len() {
 +            todo!();
 +        }
 +
 +        w.len();
 +    }
 +
 +    fn test_while_let_cond() {
 +        let mut vec = vec![1, 2];
 +        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let mut w = v.iter().collect::<Vec<_>>();
 +        // Do lint
 +        while let Some(i) = Some(w.len()) {
 +            todo!();
 +        }
 +    }
 +
 +    fn test_while_let_cond_false_case() {
 +        let mut vec = vec![1, 2];
 +        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
 +        let mut w = v.iter().collect::<Vec<_>>();
 +        // Do not lint, because w is used.
 +        while let Some(i) = Some(w.len()) {
 +            todo!();
 +        }
 +        w.len();
 +    }
 +}
index 99e1b91d8fea2bd2022424c2b7c88ae2d1d3286f,0000000000000000000000000000000000000000..790d725907f322d2e1d4248aa21002607b553c8f
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,246 @@@
-   --> $DIR/needless_collect_indirect.rs:7:39
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:9:38
++  --> $DIR/needless_collect_indirect.rs:8:39
 +   |
 +LL |     let indirect_iter = sample.iter().collect::<Vec<_>>();
 +   |                                       ^^^^^^^
 +LL |     indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +   |     ------------------------- the iterator could be used here instead
 +   |
 +   = note: `-D clippy::needless-collect` implied by `-D warnings`
 +help: use the original Iterator instead of collecting it and then producing a new one
 +   |
 +LL ~     
 +LL ~     sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:11:40
++  --> $DIR/needless_collect_indirect.rs:10:38
 +   |
 +LL |     let indirect_len = sample.iter().collect::<VecDeque<_>>();
 +   |                                      ^^^^^^^
 +LL |     indirect_len.len();
 +   |     ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~     
 +LL ~     sample.iter().count();
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:13:43
++  --> $DIR/needless_collect_indirect.rs:12:40
 +   |
 +LL |     let indirect_empty = sample.iter().collect::<VecDeque<_>>();
 +   |                                        ^^^^^^^
 +LL |     indirect_empty.is_empty();
 +   |     ------------------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator has anything instead of collecting it and seeing if it's empty
 +   |
 +LL ~     
 +LL ~     sample.iter().next().is_none();
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:25:48
++  --> $DIR/needless_collect_indirect.rs:14:43
 +   |
 +LL |     let indirect_contains = sample.iter().collect::<VecDeque<_>>();
 +   |                                           ^^^^^^^
 +LL |     indirect_contains.contains(&&5);
 +   |     ------------------------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~     
 +LL ~     sample.iter().any(|x| x == &5);
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:54:51
++  --> $DIR/needless_collect_indirect.rs:26:48
 +   |
 +LL |     let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
 +   |                                                ^^^^^^^
 +LL |     non_copy_contains.contains(&a);
 +   |     ------------------------------ the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~     
 +LL ~     sample.into_iter().any(|x| x == a);
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:59:55
++  --> $DIR/needless_collect_indirect.rs:55:51
 +   |
 +LL |         let buffer: Vec<&str> = string.split('/').collect();
 +   |                                                   ^^^^^^^
 +LL |         buffer.len()
 +   |         ------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         string.split('/').count()
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:64:57
++  --> $DIR/needless_collect_indirect.rs:60:55
 +   |
 +LL |         let indirect_len: VecDeque<_> = sample.iter().collect();
 +   |                                                       ^^^^^^^
 +LL |         indirect_len.len()
 +   |         ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         sample.iter().count()
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:69:57
++  --> $DIR/needless_collect_indirect.rs:65:57
 +   |
 +LL |         let indirect_len: LinkedList<_> = sample.iter().collect();
 +   |                                                         ^^^^^^^
 +LL |         indirect_len.len()
 +   |         ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         sample.iter().count()
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:129:59
++  --> $DIR/needless_collect_indirect.rs:70:57
 +   |
 +LL |         let indirect_len: BinaryHeap<_> = sample.iter().collect();
 +   |                                                         ^^^^^^^
 +LL |         indirect_len.len()
 +   |         ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         sample.iter().count()
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:154:59
++  --> $DIR/needless_collect_indirect.rs:130:59
 +   |
 +LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +   |                                                           ^^^^^^^
 +...
 +LL |             y.contains(&i);
 +   |             -------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~             
 +LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +LL |             // Do lint
 +LL ~             vec.iter().map(|k| k * k).any(|x| x == i);
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:183:63
++  --> $DIR/needless_collect_indirect.rs:155:59
 +   |
 +LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +   |                                                           ^^^^^^^
 +...
 +LL |             y.contains(&n);
 +   |             -------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~             
 +LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +LL |             // Do lint
 +LL ~             vec.iter().map(|k| k * k).any(|x| x == n);
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:219:59
++  --> $DIR/needless_collect_indirect.rs:184:63
 +   |
 +LL |                 let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +   |                                                               ^^^^^^^
 +...
 +LL |                 y.contains(&n);
 +   |                 -------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~                 
 +LL |                 let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +LL |                 // Do lint
 +LL ~                 vec.iter().map(|k| k * k).any(|x| x == n);
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:244:26
++  --> $DIR/needless_collect_indirect.rs:220:59
 +   |
 +LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +   |                                                           ^^^^^^^
 +...
 +LL |                 y.contains(&n);
 +   |                 -------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~             
 +LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
 +LL |             if n < 2 {
 +LL |                 // Do lint
 +LL ~                 vec.iter().map(|k| k * k).any(|x| x == n);
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:266:30
++  --> $DIR/needless_collect_indirect.rs:245:26
 +   |
 +LL |         let w = v.iter().collect::<Vec<_>>();
 +   |                          ^^^^^^^
 +LL |         // Do lint
 +LL |         for _ in 0..w.len() {
 +   |                     ------- the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL |         // Do lint
 +LL ~         for _ in 0..v.iter().count() {
 +   |
 +
 +error: avoid using `collect()` when not needed
-   --> $DIR/needless_collect_indirect.rs:288:30
++  --> $DIR/needless_collect_indirect.rs:267:30
 +   |
 +LL |         let mut w = v.iter().collect::<Vec<_>>();
 +   |                              ^^^^^^^
 +LL |         // Do lint
 +LL |         while 1 == w.len() {
 +   |                    ------- the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL |         // Do lint
 +LL ~         while 1 == v.iter().count() {
 +   |
 +
 +error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:289:30
 +   |
 +LL |         let mut w = v.iter().collect::<Vec<_>>();
 +   |                              ^^^^^^^
 +LL |         // Do lint
 +LL |         while let Some(i) = Some(w.len()) {
 +   |                                  ------- the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL |         // Do lint
 +LL ~         while let Some(i) = Some(v.iter().count()) {
 +   |
 +
 +error: aborting due to 16 previous errors
 +
index fc686b1dac0e135900213be13f191040abcd10fb,0000000000000000000000000000000000000000..2efc936752ef9071c7ce814128d1d9b91b87b97c
mode 100644,000000..100644
--- /dev/null
@@@ -1,422 -1,0 +1,498 @@@
- // No error; multiple input refs.
- fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
 +#![warn(clippy::needless_lifetimes)]
 +#![allow(
 +    dead_code,
 +    clippy::boxed_local,
 +    clippy::needless_pass_by_value,
 +    clippy::unnecessary_wraps,
 +    dyn_drop,
 +    clippy::get_first
 +)]
 +
 +fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
 +
 +fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
 +
 +// No error; same lifetime on two params.
 +fn same_lifetime_on_input<'a>(_x: &'a u8, _y: &'a u8) {}
 +
 +// No error; static involved.
 +fn only_static_on_input(_x: &u8, _y: &u8, _z: &'static u8) {}
 +
 +fn mut_and_static_input(_x: &mut u8, _y: &'static str) {}
 +
 +fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
 +    x
 +}
 +
 +// No error; multiple input refs.
 +fn multiple_in_and_out_1<'a>(x: &'a u8, _y: &'a u8) -> &'a u8 {
 +    x
 +}
 +
- // No error.
- fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
++// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
++//   fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8
++//                                                ^^^
++fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
 +    x
 +}
 +
++// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
++//   fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8
++//                                     ^^^
++fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
++    y
++}
++
 +// No error; multiple input refs
 +async fn func<'a>(args: &[&'a str]) -> Option<&'a str> {
 +    args.get(0).cloned()
 +}
 +
 +// No error; static involved.
 +fn in_static_and_out<'a>(x: &'a u8, _y: &'static u8) -> &'a u8 {
 +    x
 +}
 +
-     // No error; multiple input refs.
-     fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
++// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
++//   fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()>
++//                                           ^^^
++fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
 +    Ok(x)
 +}
 +
++// Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
++//   fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()>
++//                                ^^^
++fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
++    Ok(y)
++}
++
 +// No error; two input refs.
 +fn deep_reference_2<'a>(x: Result<&'a u8, &'a u8>) -> &'a u8 {
 +    x.unwrap()
 +}
 +
 +fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
 +    Ok(x)
 +}
 +
 +// Where-clause, but without lifetimes.
 +fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
 +where
 +    T: Copy,
 +{
 +    Ok(x)
 +}
 +
 +type Ref<'r> = &'r u8;
 +
 +// No error; same lifetime on two params.
 +fn lifetime_param_1<'a>(_x: Ref<'a>, _y: &'a u8) {}
 +
 +fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
 +
 +// No error; bounded lifetime.
 +fn lifetime_param_3<'a, 'b: 'a>(_x: Ref<'a>, _y: &'b u8) {}
 +
 +// No error; bounded lifetime.
 +fn lifetime_param_4<'a, 'b>(_x: Ref<'a>, _y: &'b u8)
 +where
 +    'b: 'a,
 +{
 +}
 +
 +struct Lt<'a, I: 'static> {
 +    x: &'a I,
 +}
 +
 +// No error; fn bound references `'a`.
 +fn fn_bound<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
 +where
 +    F: Fn(Lt<'a, I>) -> Lt<'a, I>,
 +{
 +    unreachable!()
 +}
 +
 +fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
 +where
 +    for<'x> F: Fn(Lt<'x, I>) -> Lt<'x, I>,
 +{
 +    unreachable!()
 +}
 +
 +// No error; see below.
 +fn fn_bound_3<'a, F: FnOnce(&'a i32)>(x: &'a i32, f: F) {
 +    f(x);
 +}
 +
 +fn fn_bound_3_cannot_elide() {
 +    let x = 42;
 +    let p = &x;
 +    let mut q = &x;
 +    // This will fail if we elide lifetimes of `fn_bound_3`.
 +    fn_bound_3(p, |y| q = y);
 +}
 +
 +// No error; multiple input refs.
 +fn fn_bound_4<'a, F: FnOnce() -> &'a ()>(cond: bool, x: &'a (), f: F) -> &'a () {
 +    if cond { x } else { f() }
 +}
 +
 +struct X {
 +    x: u8,
 +}
 +
 +impl X {
 +    fn self_and_out<'s>(&'s self) -> &'s u8 {
 +        &self.x
 +    }
 +
- // No warning; two input lifetimes.
- fn struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
++    // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
++    //   fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8
++    //                                          ^^^
++    fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
 +        &self.x
 +    }
 +
++    // Error; multiple input refs, but the output lifetime is not elided, i.e., the following is valid:
++    //   fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8
++    //                            ^^^^^
++    fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
++        x
++    }
++
 +    fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
 +
 +    // No error; same lifetimes on two params.
 +    fn self_and_same_in<'s>(&'s self, _x: &'s u8) {}
 +}
 +
 +struct Foo<'a>(&'a u8);
 +
 +impl<'a> Foo<'a> {
 +    // No error; lifetime `'a` not defined in method.
 +    fn self_shared_lifetime(&self, _: &'a u8) {}
 +    // No error; bounds exist.
 +    fn self_bound_lifetime<'b: 'a>(&self, _: &'b u8) {}
 +}
 +
 +fn already_elided<'a>(_: &u8, _: &'a u8) -> &'a u8 {
 +    unimplemented!()
 +}
 +
 +fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
 +    unimplemented!()
 +}
 +
 +// No warning; two input lifetimes (named on the reference, anonymous on `Foo`).
 +fn struct_with_lt2<'a>(_foo: &'a Foo) -> &'a str {
 +    unimplemented!()
 +}
 +
 +// No warning; two input lifetimes (anonymous on the reference, named on `Foo`).
 +fn struct_with_lt3<'a>(_foo: &Foo<'a>) -> &'a str {
 +    unimplemented!()
 +}
 +
- // No warning; two input lifetimes.
- fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
++// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
++// valid:
++//   fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str
++//                                         ^^
++fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
++    unimplemented!()
++}
++
++// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
++// valid:
++//   fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str
++//                                 ^^^^
++fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
 +    unimplemented!()
 +}
 +
 +trait WithLifetime<'a> {}
 +
 +type WithLifetimeAlias<'a> = dyn WithLifetime<'a>;
 +
 +// Should not warn because it won't build without the lifetime.
 +fn trait_obj_elided<'a>(_arg: &'a dyn WithLifetime) -> &'a str {
 +    unimplemented!()
 +}
 +
 +// Should warn because there is no lifetime on `Drop`, so this would be
 +// unambiguous if we elided the lifetime.
 +fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
 +    unimplemented!()
 +}
 +
 +type FooAlias<'a> = Foo<'a>;
 +
 +fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
 +    unimplemented!()
 +}
 +
 +// No warning; two input lifetimes (named on the reference, anonymous on `FooAlias`).
 +fn alias_with_lt2<'a>(_foo: &'a FooAlias) -> &'a str {
 +    unimplemented!()
 +}
 +
 +// No warning; two input lifetimes (anonymous on the reference, named on `FooAlias`).
 +fn alias_with_lt3<'a>(_foo: &FooAlias<'a>) -> &'a str {
 +    unimplemented!()
 +}
 +
++// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
++// valid:
++//   fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str
++//                                             ^^
++fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
++    unimplemented!()
++}
++
++// Warning; two input lifetimes, but the output lifetime is not elided, i.e., the following is
++// valid:
++//   fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str
++//                                ^^^^^^^^^
++fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
 +    unimplemented!()
 +}
 +
 +fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
 +    unimplemented!()
 +}
 +
 +fn elided_input_named_output<'a>(_arg: &str) -> &'a str {
 +    unimplemented!()
 +}
 +
 +fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
 +    unimplemented!()
 +}
 +fn trait_bound<'a, T: WithLifetime<'a>>(_: &'a u8, _: T) {
 +    unimplemented!()
 +}
 +
 +// Don't warn on these; see issue #292.
 +fn trait_bound_bug<'a, T: WithLifetime<'a>>() {
 +    unimplemented!()
 +}
 +
 +// See issue #740.
 +struct Test {
 +    vec: Vec<usize>,
 +}
 +
 +impl Test {
 +    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = usize> + 'a> {
 +        unimplemented!()
 +    }
 +}
 +
 +trait LintContext<'a> {}
 +
 +fn f<'a, T: LintContext<'a>>(_: &T) {}
 +
 +fn test<'a>(x: &'a [u8]) -> u8 {
 +    let y: &'a u8 = &x[5];
 +    *y
 +}
 +
 +// Issue #3284: give hint regarding lifetime in return type.
 +struct Cow<'a> {
 +    x: &'a str,
 +}
 +fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
 +    unimplemented!()
 +}
 +
 +// Make sure we still warn on implementations
 +mod issue4291 {
 +    trait BadTrait {
 +        fn needless_lt<'a>(x: &'a u8) {}
 +    }
 +
 +    impl BadTrait for () {
 +        fn needless_lt<'a>(_x: &'a u8) {}
 +    }
 +}
 +
 +mod issue2944 {
 +    trait Foo {}
 +    struct Bar;
 +    struct Baz<'a> {
 +        bar: &'a Bar,
 +    }
 +
 +    impl<'a> Foo for Baz<'a> {}
 +    impl Bar {
 +        fn baz<'a>(&'a self) -> impl Foo + 'a {
 +            Baz { bar: self }
 +        }
 +    }
 +}
 +
 +mod nested_elision_sites {
 +    // issue #issue2944
 +
 +    // closure trait bounds subject to nested elision
 +    // don't lint because they refer to outer lifetimes
 +    fn trait_fn<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
 +        move || i
 +    }
 +    fn trait_fn_mut<'a>(i: &'a i32) -> impl FnMut() -> &'a i32 {
 +        move || i
 +    }
 +    fn trait_fn_once<'a>(i: &'a i32) -> impl FnOnce() -> &'a i32 {
 +        move || i
 +    }
 +
 +    // don't lint
 +    fn impl_trait_in_input_position<'a>(f: impl Fn() -> &'a i32) -> &'a i32 {
 +        f()
 +    }
 +    fn impl_trait_in_output_position<'a>(i: &'a i32) -> impl Fn() -> &'a i32 {
 +        move || i
 +    }
 +    // lint
 +    fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
 +        f(i)
 +    }
 +    fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
 +        f(i)
 +    }
 +
 +    // don't lint
 +    fn generics_not_elidable<'a, T: Fn() -> &'a i32>(f: T) -> &'a i32 {
 +        f()
 +    }
 +    // lint
 +    fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
 +        f(i)
 +    }
 +
 +    // don't lint
 +    fn where_clause_not_elidable<'a, T>(f: T) -> &'a i32
 +    where
 +        T: Fn() -> &'a i32,
 +    {
 +        f()
 +    }
 +    // lint
 +    fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
 +    where
 +        T: Fn(&i32) -> &i32,
 +    {
 +        f(i)
 +    }
 +
 +    // don't lint
 +    fn pointer_fn_in_input_position<'a>(f: fn(&'a i32) -> &'a i32, i: &'a i32) -> &'a i32 {
 +        f(i)
 +    }
 +    fn pointer_fn_in_output_position<'a>(_: &'a i32) -> fn(&'a i32) -> &'a i32 {
 +        |i| i
 +    }
 +    // lint
 +    fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
 +        f(i)
 +    }
 +
 +    // don't lint
 +    fn nested_fn_pointer_1<'a>(_: &'a i32) -> fn(fn(&'a i32) -> &'a i32) -> i32 {
 +        |f| 42
 +    }
 +    fn nested_fn_pointer_2<'a>(_: &'a i32) -> impl Fn(fn(&'a i32)) {
 +        |f| ()
 +    }
 +
 +    // lint
 +    fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
 +        |f| 42
 +    }
 +    fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
 +        |f| ()
 +    }
 +}
 +
 +mod issue6159 {
 +    use std::ops::Deref;
 +    pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
 +    where
 +        T: Deref,
 +        F: FnOnce(&'a T::Target) -> R,
 +    {
 +        f(x.deref())
 +    }
 +}
 +
 +mod issue7296 {
 +    use std::rc::Rc;
 +    use std::sync::Arc;
 +
 +    struct Foo;
 +    impl Foo {
 +        fn implicit<'a>(&'a self) -> &'a () {
 +            &()
 +        }
 +        fn implicit_mut<'a>(&'a mut self) -> &'a () {
 +            &()
 +        }
 +
 +        fn explicit<'a>(self: &'a Arc<Self>) -> &'a () {
 +            &()
 +        }
 +        fn explicit_mut<'a>(self: &'a mut Rc<Self>) -> &'a () {
 +            &()
 +        }
 +
 +        fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
 +            &()
 +        }
 +    }
 +
 +    trait Bar {
 +        fn implicit<'a>(&'a self) -> &'a ();
 +        fn implicit_provided<'a>(&'a self) -> &'a () {
 +            &()
 +        }
 +
 +        fn explicit<'a>(self: &'a Arc<Self>) -> &'a ();
 +        fn explicit_provided<'a>(self: &'a Arc<Self>) -> &'a () {
 +            &()
 +        }
 +
 +        fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
 +        fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
 +            &()
 +        }
 +    }
 +}
 +
++mod pr_9743_false_negative_fix {
++    #![allow(unused)]
++
++    fn foo<'a>(x: &'a u8, y: &'_ u8) {}
++
++    fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
++}
++
++mod pr_9743_output_lifetime_checks {
++    #![allow(unused)]
++
++    // lint: only one input
++    fn one_input<'a>(x: &'a u8) -> &'a u8 {
++        unimplemented!()
++    }
++
++    // lint: multiple inputs, output would not be elided
++    fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
++        unimplemented!()
++    }
++
++    // don't lint: multiple inputs, output would be elided (which would create an ambiguity)
++    fn multiple_inputs_output_would_be_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'a u8 {
++        unimplemented!()
++    }
++}
++
 +fn main() {}
index 3c428fd4674ce754b9ebc30f36961d0959a0923a,0000000000000000000000000000000000000000..5a7cf13c86dded256b80b69b5dbb75aa51e85318
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,316 @@@
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a, 'b
 +  --> $DIR/needless_lifetimes.rs:11:1
 +   |
 +LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::needless-lifetimes` implied by `-D warnings`
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a, 'b
 +  --> $DIR/needless_lifetimes.rs:13:1
 +   |
 +LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
++error: the following explicit lifetimes could be elided: 'a
 +  --> $DIR/needless_lifetimes.rs:23:1
 +   |
 +LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:57:1
++error: the following explicit lifetimes could be elided: 'b
++  --> $DIR/needless_lifetimes.rs:35:1
++   |
++LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:42:1
++   |
++LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'b
++  --> $DIR/needless_lifetimes.rs:59:1
++   |
++LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:66:1
++   |
++LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:75:1
 +   |
 +LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:62:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:80:1
 +   |
 +LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()>
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:74:1
++error: the following explicit lifetimes could be elided: 'a, 'b
++  --> $DIR/needless_lifetimes.rs:92:1
 +   |
 +LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:92:37
++   |
++LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {}
++   |                                     ^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:98:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:116:1
 +   |
 +LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:116:32
++   |
++LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I>
++   |                                ^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:128:5
++error: the following explicit lifetimes could be elided: 's
++  --> $DIR/needless_lifetimes.rs:146:5
 +   |
 +LL |     fn self_and_out<'s>(&'s self) -> &'s u8 {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:137:5
++error: the following explicit lifetimes could be elided: 't
++  --> $DIR/needless_lifetimes.rs:153:5
++   |
++LL |     fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 's
++  --> $DIR/needless_lifetimes.rs:160:5
++   |
++LL |     fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 's, 't
++  --> $DIR/needless_lifetimes.rs:164:5
 +   |
 +LL |     fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {}
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:156:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:183:1
 +   |
 +LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:183:33
++   |
++LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str {
++   |                                 ^^
++
++error: the following explicit lifetimes could be elided: 'b
++  --> $DIR/needless_lifetimes.rs:201:1
++   |
++LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:201:43
++   |
++LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str {
++   |                                           ^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:209:1
++   |
++LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:186:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:224:1
 +   |
 +LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:192:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:230:1
 +   |
 +LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:230:37
++   |
++LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str {
++   |                                     ^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:211:1
++error: the following explicit lifetimes could be elided: 'b
++  --> $DIR/needless_lifetimes.rs:248:1
++   |
++LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:248:47
++   |
++LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str {
++   |                                               ^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:256:1
++   |
++LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str {
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:260:1
 +   |
 +LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:219:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:268:1
 +   |
 +LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:255:1
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:304:1
 +   |
 +LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: replace with `'_` in generic arguments such as here
++  --> $DIR/needless_lifetimes.rs:304:47
++   |
++LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> {
++   |                                               ^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:262:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:311:9
 +   |
 +LL |         fn needless_lt<'a>(x: &'a u8) {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:266:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:315:9
 +   |
 +LL |         fn needless_lt<'a>(_x: &'a u8) {}
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:279:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:328:9
 +   |
 +LL |         fn baz<'a>(&'a self) -> impl Foo + 'a {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:311:5
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:360:5
 +   |
 +LL |     fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:320:5
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:369:5
 +   |
 +LL |     fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:332:5
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:381:5
 +   |
 +LL |     fn where_clause_elidadable<'a, T>(i: &'a i32, f: T) -> &'a i32
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:347:5
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:396:5
 +   |
 +LL |     fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:360:5
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:409:5
 +   |
 +LL |     fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:363:5
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:412:5
 +   |
 +LL |     fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) {
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:385:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:434:9
 +   |
 +LL |         fn implicit<'a>(&'a self) -> &'a () {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:388:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:437:9
 +   |
 +LL |         fn implicit_mut<'a>(&'a mut self) -> &'a () {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:399:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:448:9
 +   |
 +LL |         fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:405:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:454:9
 +   |
 +LL |         fn implicit<'a>(&'a self) -> &'a ();
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:406:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:455:9
 +   |
 +LL |         fn implicit_provided<'a>(&'a self) -> &'a () {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:415:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:464:9
 +   |
 +LL |         fn lifetime_elsewhere<'a>(self: Box<Self>, here: &'a ()) -> &'a ();
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:416:9
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:465:9
 +   |
 +LL |         fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
- error: aborting due to 31 previous errors
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:474:5
++   |
++LL |     fn foo<'a>(x: &'a u8, y: &'_ u8) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:476:5
++   |
++LL |     fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {}
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:483:5
++   |
++LL |     fn one_input<'a>(x: &'a u8) -> &'a u8 {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: the following explicit lifetimes could be elided: 'a
++  --> $DIR/needless_lifetimes.rs:488:5
++   |
++LL |     fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 45 previous errors
 +
index 3dbef19890e9ce14e5db4b57ab2aa1bc449eb33a,0000000000000000000000000000000000000000..28e8f459d4429cae47078cd349795a6509b17045
mode 100644,000000..100644
--- /dev/null
@@@ -1,247 -1,0 +1,268 @@@
 +#![allow(
 +    clippy::single_match,
 +    unused_assignments,
 +    unused_variables,
 +    clippy::while_immutable_condition
 +)]
 +
 +fn test1() {
 +    let mut x = 0;
 +    loop {
 +        // clippy::never_loop
 +        x += 1;
 +        if x == 1 {
 +            return;
 +        }
 +        break;
 +    }
 +}
 +
 +fn test2() {
 +    let mut x = 0;
 +    loop {
 +        x += 1;
 +        if x == 1 {
 +            break;
 +        }
 +    }
 +}
 +
 +fn test3() {
 +    let mut x = 0;
 +    loop {
 +        // never loops
 +        x += 1;
 +        break;
 +    }
 +}
 +
 +fn test4() {
 +    let mut x = 1;
 +    loop {
 +        x += 1;
 +        match x {
 +            5 => return,
 +            _ => (),
 +        }
 +    }
 +}
 +
 +fn test5() {
 +    let i = 0;
 +    loop {
 +        // never loops
 +        while i == 0 {
 +            // never loops
 +            break;
 +        }
 +        return;
 +    }
 +}
 +
 +fn test6() {
 +    let mut x = 0;
 +    'outer: loop {
 +        x += 1;
 +        loop {
 +            // never loops
 +            if x == 5 {
 +                break;
 +            }
 +            continue 'outer;
 +        }
 +        return;
 +    }
 +}
 +
 +fn test7() {
 +    let mut x = 0;
 +    loop {
 +        x += 1;
 +        match x {
 +            1 => continue,
 +            _ => (),
 +        }
 +        return;
 +    }
 +}
 +
 +fn test8() {
 +    let mut x = 0;
 +    loop {
 +        x += 1;
 +        match x {
 +            5 => return,
 +            _ => continue,
 +        }
 +    }
 +}
 +
 +fn test9() {
 +    let x = Some(1);
 +    while let Some(y) = x {
 +        // never loops
 +        return;
 +    }
 +}
 +
 +fn test10() {
 +    for x in 0..10 {
 +        // never loops
 +        match x {
 +            1 => break,
 +            _ => return,
 +        }
 +    }
 +}
 +
 +fn test11<F: FnMut() -> i32>(mut f: F) {
 +    loop {
 +        return match f() {
 +            1 => continue,
 +            _ => (),
 +        };
 +    }
 +}
 +
 +pub fn test12(a: bool, b: bool) {
 +    'label: loop {
 +        loop {
 +            if a {
 +                continue 'label;
 +            }
 +            if b {
 +                break;
 +            }
 +        }
 +        break;
 +    }
 +}
 +
 +pub fn test13() {
 +    let mut a = true;
 +    loop {
 +        // infinite loop
 +        while a {
 +            if true {
 +                a = false;
 +                continue;
 +            }
 +            return;
 +        }
 +    }
 +}
 +
 +pub fn test14() {
 +    let mut a = true;
 +    'outer: while a {
 +        // never loops
 +        while a {
 +            if a {
 +                a = false;
 +                continue;
 +            }
 +        }
 +        break 'outer;
 +    }
 +}
 +
 +// Issue #1991: the outer loop should not warn.
 +pub fn test15() {
 +    'label: loop {
 +        while false {
 +            break 'label;
 +        }
 +    }
 +}
 +
 +// Issue #4058: `continue` in `break` expression
 +pub fn test16() {
 +    let mut n = 1;
 +    loop {
 +        break if n != 5 {
 +            n += 1;
 +            continue;
 +        };
 +    }
 +}
 +
 +// Issue #9001: `continue` in struct expression fields
 +pub fn test17() {
 +    struct Foo {
 +        f: (),
 +    }
 +
 +    let mut n = 0;
 +    let _ = loop {
 +        break Foo {
 +            f: if n < 5 {
 +                n += 1;
 +                continue;
 +            },
 +        };
 +    };
 +}
 +
 +// Issue #9356: `continue` in else branch of let..else
 +pub fn test18() {
 +    let x = Some(0);
 +    let y = 0;
 +    // might loop
 +    let _ = loop {
 +        let Some(x) = x else {
 +            if y > 0 {
 +                continue;
 +            } else {
 +                return;
 +            }
 +        };
 +
 +        break x;
 +    };
 +    // never loops
 +    let _ = loop {
 +        let Some(x) = x else {
 +            return;
 +        };
 +
 +        break x;
 +    };
 +}
 +
++// Issue #9831: unconditional break to internal labeled block
++pub fn test19() {
++    fn thing(iter: impl Iterator) {
++        for _ in iter {
++            'b: {
++                break 'b;
++            }
++        }
++    }
++}
++
++pub fn test20() {
++    'a: loop {
++        'b: {
++            break 'b 'c: {
++                break 'a;
++            };
++        }
++    }
++}
++
 +fn main() {
 +    test1();
 +    test2();
 +    test3();
 +    test4();
 +    test5();
 +    test6();
 +    test7();
 +    test8();
 +    test9();
 +    test10();
 +    test11(|| 0);
 +    test12(true, false);
 +    test13();
 +    test14();
 +}
index 3033f019244a8409befc6706f4887f2b9a99f48e,0000000000000000000000000000000000000000..b7029bf8bed47578f21e65d6c70a78264eb9bbf5
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,130 @@@
- error: aborting due to 10 previous errors
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:10:5
 +   |
 +LL | /     loop {
 +LL | |         // clippy::never_loop
 +LL | |         x += 1;
 +LL | |         if x == 1 {
 +...  |
 +LL | |         break;
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `#[deny(clippy::never_loop)]` on by default
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:32:5
 +   |
 +LL | /     loop {
 +LL | |         // never loops
 +LL | |         x += 1;
 +LL | |         break;
 +LL | |     }
 +   | |_____^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:52:5
 +   |
 +LL | /     loop {
 +LL | |         // never loops
 +LL | |         while i == 0 {
 +LL | |             // never loops
 +...  |
 +LL | |         return;
 +LL | |     }
 +   | |_____^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:54:9
 +   |
 +LL | /         while i == 0 {
 +LL | |             // never loops
 +LL | |             break;
 +LL | |         }
 +   | |_________^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:66:9
 +   |
 +LL | /         loop {
 +LL | |             // never loops
 +LL | |             if x == 5 {
 +LL | |                 break;
 +LL | |             }
 +LL | |             continue 'outer;
 +LL | |         }
 +   | |_________^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:102:5
 +   |
 +LL | /     while let Some(y) = x {
 +LL | |         // never loops
 +LL | |         return;
 +LL | |     }
 +   | |_____^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:109:5
 +   |
 +LL | /     for x in 0..10 {
 +LL | |         // never loops
 +LL | |         match x {
 +LL | |             1 => break,
 +LL | |             _ => return,
 +LL | |         }
 +LL | |     }
 +   | |_____^
 +   |
 +help: if you need the first element of the iterator, try writing
 +   |
 +LL |     if let Some(x) = (0..10).next() {
 +   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:157:5
 +   |
 +LL | /     'outer: while a {
 +LL | |         // never loops
 +LL | |         while a {
 +LL | |             if a {
 +...  |
 +LL | |         break 'outer;
 +LL | |     }
 +   | |_____^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:172:9
 +   |
 +LL | /         while false {
 +LL | |             break 'label;
 +LL | |         }
 +   | |_________^
 +
 +error: this loop never actually loops
 +  --> $DIR/never_loop.rs:223:13
 +   |
 +LL |       let _ = loop {
 +   |  _____________^
 +LL | |         let Some(x) = x else {
 +LL | |             return;
 +LL | |         };
 +LL | |
 +LL | |         break x;
 +LL | |     };
 +   | |_____^
 +
++error: this loop never actually loops
++  --> $DIR/never_loop.rs:244:5
++   |
++LL | /     'a: loop {
++LL | |         'b: {
++LL | |             break 'b 'c: {
++LL | |                 break 'a;
++LL | |             };
++LL | |         }
++LL | |     }
++   | |_____^
++
++error: aborting due to 11 previous errors
 +
index 2f315ffe2983ebebc506b2a5eb33bf7d2f87647b,0000000000000000000000000000000000000000..f69982d63a898d17ba3b9f6ff417c42ea8442bd8
mode 100644,000000..100644
--- /dev/null
@@@ -1,352 -1,0 +1,402 @@@
 +#![warn(clippy::new_ret_no_self)]
 +#![allow(dead_code)]
 +
 +fn main() {}
 +
 +trait R {
 +    type Item;
 +}
 +
 +trait Q {
 +    type Item;
 +    type Item2;
 +}
 +
 +struct S;
 +
 +impl R for S {
 +    type Item = Self;
 +}
 +
 +impl S {
 +    // should not trigger the lint
 +    pub fn new() -> impl R<Item = Self> {
 +        S
 +    }
 +}
 +
 +struct S2;
 +
 +impl R for S2 {
 +    type Item = Self;
 +}
 +
 +impl S2 {
 +    // should not trigger the lint
 +    pub fn new(_: String) -> impl R<Item = Self> {
 +        S2
 +    }
 +}
 +
 +struct S3;
 +
 +impl R for S3 {
 +    type Item = u32;
 +}
 +
 +impl S3 {
 +    // should trigger the lint
 +    pub fn new(_: String) -> impl R<Item = u32> {
 +        S3
 +    }
 +}
 +
 +struct S4;
 +
 +impl Q for S4 {
 +    type Item = u32;
 +    type Item2 = Self;
 +}
 +
 +impl S4 {
 +    // should not trigger the lint
 +    pub fn new(_: String) -> impl Q<Item = u32, Item2 = Self> {
 +        S4
 +    }
 +}
 +
 +struct T;
 +
 +impl T {
 +    // should not trigger lint
 +    pub fn new() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +struct U;
 +
 +impl U {
 +    // should trigger lint
 +    pub fn new() -> u32 {
 +        unimplemented!();
 +    }
 +}
 +
 +struct V;
 +
 +impl V {
 +    // should trigger lint
 +    pub fn new(_: String) -> u32 {
 +        unimplemented!();
 +    }
 +}
 +
 +struct TupleReturnerOk;
 +
 +impl TupleReturnerOk {
 +    // should not trigger lint
 +    pub fn new() -> (Self, u32) {
 +        unimplemented!();
 +    }
 +}
 +
 +struct TupleReturnerOk2;
 +
 +impl TupleReturnerOk2 {
 +    // should not trigger lint (it doesn't matter which element in the tuple is Self)
 +    pub fn new() -> (u32, Self) {
 +        unimplemented!();
 +    }
 +}
 +
 +struct TupleReturnerOk3;
 +
 +impl TupleReturnerOk3 {
 +    // should not trigger lint (tuple can contain multiple Self)
 +    pub fn new() -> (Self, Self) {
 +        unimplemented!();
 +    }
 +}
 +
 +struct TupleReturnerBad;
 +
 +impl TupleReturnerBad {
 +    // should trigger lint
 +    pub fn new() -> (u32, u32) {
 +        unimplemented!();
 +    }
 +}
 +
 +struct MutPointerReturnerOk;
 +
 +impl MutPointerReturnerOk {
 +    // should not trigger lint
 +    pub fn new() -> *mut Self {
 +        unimplemented!();
 +    }
 +}
 +
 +struct ConstPointerReturnerOk2;
 +
 +impl ConstPointerReturnerOk2 {
 +    // should not trigger lint
 +    pub fn new() -> *const Self {
 +        unimplemented!();
 +    }
 +}
 +
 +struct MutPointerReturnerBad;
 +
 +impl MutPointerReturnerBad {
 +    // should trigger lint
 +    pub fn new() -> *mut V {
 +        unimplemented!();
 +    }
 +}
 +
 +struct GenericReturnerOk;
 +
 +impl GenericReturnerOk {
 +    // should not trigger lint
 +    pub fn new() -> Option<Self> {
 +        unimplemented!();
 +    }
 +}
 +
 +struct GenericReturnerBad;
 +
 +impl GenericReturnerBad {
 +    // should trigger lint
 +    pub fn new() -> Option<u32> {
 +        unimplemented!();
 +    }
 +}
 +
 +struct NestedReturnerOk;
 +
 +impl NestedReturnerOk {
 +    // should not trigger lint
 +    pub fn new() -> (Option<Self>, u32) {
 +        unimplemented!();
 +    }
 +}
 +
 +struct NestedReturnerOk2;
 +
 +impl NestedReturnerOk2 {
 +    // should not trigger lint
 +    pub fn new() -> ((Self, u32), u32) {
 +        unimplemented!();
 +    }
 +}
 +
 +struct NestedReturnerOk3;
 +
 +impl NestedReturnerOk3 {
 +    // should not trigger lint
 +    pub fn new() -> Option<(Self, u32)> {
 +        unimplemented!();
 +    }
 +}
 +
 +struct WithLifetime<'a> {
 +    cat: &'a str,
 +}
 +
 +impl<'a> WithLifetime<'a> {
 +    // should not trigger the lint, because the lifetimes are different
 +    pub fn new<'b: 'a>(s: &'b str) -> WithLifetime<'b> {
 +        unimplemented!();
 +    }
 +}
 +
 +mod issue5435 {
 +    struct V;
 +
 +    pub trait TraitRetSelf {
 +        // should not trigger lint
 +        fn new() -> Self;
 +    }
 +
 +    pub trait TraitRet {
 +        // should trigger lint as we are in trait definition
 +        fn new() -> String;
 +    }
 +    pub struct StructRet;
 +    impl TraitRet for StructRet {
 +        // should not trigger lint as we are in the impl block
 +        fn new() -> String {
 +            unimplemented!();
 +        }
 +    }
 +
 +    pub trait TraitRet2 {
 +        // should trigger lint
 +        fn new(_: String) -> String;
 +    }
 +
 +    trait TupleReturnerOk {
 +        // should not trigger lint
 +        fn new() -> (Self, u32)
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait TupleReturnerOk2 {
 +        // should not trigger lint (it doesn't matter which element in the tuple is Self)
 +        fn new() -> (u32, Self)
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait TupleReturnerOk3 {
 +        // should not trigger lint (tuple can contain multiple Self)
 +        fn new() -> (Self, Self)
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait TupleReturnerBad {
 +        // should trigger lint
 +        fn new() -> (u32, u32) {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait MutPointerReturnerOk {
 +        // should not trigger lint
 +        fn new() -> *mut Self
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait ConstPointerReturnerOk2 {
 +        // should not trigger lint
 +        fn new() -> *const Self
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait MutPointerReturnerBad {
 +        // should trigger lint
 +        fn new() -> *mut V {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait GenericReturnerOk {
 +        // should not trigger lint
 +        fn new() -> Option<Self>
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait NestedReturnerOk {
 +        // should not trigger lint
 +        fn new() -> (Option<Self>, u32)
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait NestedReturnerOk2 {
 +        // should not trigger lint
 +        fn new() -> ((Self, u32), u32)
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +
 +    trait NestedReturnerOk3 {
 +        // should not trigger lint
 +        fn new() -> Option<(Self, u32)>
 +        where
 +            Self: Sized,
 +        {
 +            unimplemented!();
 +        }
 +    }
 +}
 +
 +// issue #1724
 +struct RetOtherSelf<T>(T);
 +struct RetOtherSelfWrapper<T>(T);
 +
 +impl RetOtherSelf<T> {
 +    fn new(t: T) -> RetOtherSelf<RetOtherSelfWrapper<T>> {
 +        RetOtherSelf(RetOtherSelfWrapper(t))
 +    }
 +}
++
++mod issue7344 {
++    struct RetImplTraitSelf<T>(T);
++
++    impl<T> RetImplTraitSelf<T> {
++        // should not trigger lint
++        fn new(t: T) -> impl Into<Self> {
++            Self(t)
++        }
++    }
++
++    struct RetImplTraitNoSelf<T>(T);
++
++    impl<T> RetImplTraitNoSelf<T> {
++        // should trigger lint
++        fn new(t: T) -> impl Into<i32> {
++            1
++        }
++    }
++
++    trait Trait2<T, U> {}
++    impl<T, U> Trait2<T, U> for () {}
++
++    struct RetImplTraitSelf2<T>(T);
++
++    impl<T> RetImplTraitSelf2<T> {
++        // should not trigger lint
++        fn new(t: T) -> impl Trait2<(), Self> {
++            unimplemented!()
++        }
++    }
++
++    struct RetImplTraitNoSelf2<T>(T);
++
++    impl<T> RetImplTraitNoSelf2<T> {
++        // should trigger lint
++        fn new(t: T) -> impl Trait2<(), i32> {
++            unimplemented!()
++        }
++    }
++
++    struct RetImplTraitSelfAdt<'a>(&'a str);
++
++    impl<'a> RetImplTraitSelfAdt<'a> {
++        // should not trigger lint
++        fn new<'b: 'a>(s: &'b str) -> impl Into<RetImplTraitSelfAdt<'b>> {
++            RetImplTraitSelfAdt(s)
++        }
++    }
++}
index 8217bc6187f93aa5cfce1ddbc5ce7ed4788b6173,0000000000000000000000000000000000000000..bc13be47927b1c3fa37a75795a316f5c521319ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,96 @@@
- error: aborting due to 10 previous errors
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:49:5
 +   |
 +LL | /     pub fn new(_: String) -> impl R<Item = u32> {
 +LL | |         S3
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:81:5
 +   |
 +LL | /     pub fn new() -> u32 {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:90:5
 +   |
 +LL | /     pub fn new(_: String) -> u32 {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:126:5
 +   |
 +LL | /     pub fn new() -> (u32, u32) {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:153:5
 +   |
 +LL | /     pub fn new() -> *mut V {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:171:5
 +   |
 +LL | /     pub fn new() -> Option<u32> {
 +LL | |         unimplemented!();
 +LL | |     }
 +   | |_____^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:224:9
 +   |
 +LL |         fn new() -> String;
 +   |         ^^^^^^^^^^^^^^^^^^^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:236:9
 +   |
 +LL |         fn new(_: String) -> String;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:271:9
 +   |
 +LL | /         fn new() -> (u32, u32) {
 +LL | |             unimplemented!();
 +LL | |         }
 +   | |_________^
 +
 +error: methods called `new` usually return `Self`
 +  --> $DIR/new_ret_no_self.rs:298:9
 +   |
 +LL | /         fn new() -> *mut V {
 +LL | |             unimplemented!();
 +LL | |         }
 +   | |_________^
 +
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:368:9
++   |
++LL | /         fn new(t: T) -> impl Into<i32> {
++LL | |             1
++LL | |         }
++   | |_________^
++
++error: methods called `new` usually return `Self`
++  --> $DIR/new_ret_no_self.rs:389:9
++   |
++LL | /         fn new(t: T) -> impl Trait2<(), i32> {
++LL | |             unimplemented!()
++LL | |         }
++   | |_________^
++
++error: aborting due to 12 previous errors
 +
index f15ac551bb3ccfeefa774ae183e91f98a66f6ce0,0000000000000000000000000000000000000000..0456005dce4965c5c04b4a4aa85736bb090444eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,191 -1,0 +1,200 @@@
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
 +#![allow(
 +    unused_tuple_struct_fields,
 +    clippy::redundant_closure,
 +    clippy::ref_option_ref,
 +    clippy::equatable_if_let,
 +    clippy::let_unit_value
 +)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    string.map_or((false, "hello"), |x| (true, x))
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = string.map_or(0, |s| s.len());
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.map_or(0, |mut s| {
 +        s += 1;
 +        s
 +    });
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    arg.map_or(13, |x| {
 +        let y = x * x;
 +        y * y
 +    })
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = arg.map_or_else(|| side_effect(), |x| x);
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = arg.map_or_else(|| {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    }, |x| x * x * x * x);
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +// #7973
 +fn pattern_to_vec(pattern: &str) -> Vec<String> {
 +    pattern
 +        .trim_matches('/')
 +        .split('/')
 +        .flat_map(|s| {
 +            s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])
 +        })
 +        .collect::<Vec<_>>()
 +}
 +
 +enum DummyEnum {
 +    One(u8),
 +    Two,
 +}
 +
 +// should not warn since there is a compled complex subpat
 +// see #7991
 +fn complex_subpat() -> DummyEnum {
 +    let x = Some(DummyEnum::One(1));
 +    let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
 +    DummyEnum::Two
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = optional.map_or(5, |x| x + 2);
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
 +
 +    let _ = Some(0).map_or(0, |x| loop {
 +            if x == 0 {
 +                break x;
 +            }
 +        });
 +
 +    // #7576
 +    const fn _f(x: Option<u32>) -> u32 {
 +        // Don't lint, `map_or` isn't const
 +        if let Some(x) = x { x } else { 10 }
 +    }
 +
 +    // #5822
 +    let s = String::new();
 +    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        s.len()
 +    };
 +
 +    let s = String::new();
 +    // Lint, both branches immutably borrow `s`.
 +    let _ = Some(0).map_or(s.len(), |x| s.len() + x);
 +
 +    let s = String::new();
 +    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
 +    let _ = Some(0).map_or(1, |x| {
 +        let s = s;
 +        s.len() + x
 +    });
 +
 +    let s = Some(String::new());
 +    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
 +    let _ = if let Some(x) = &s {
 +        x.len()
 +    } else {
 +        let _s = s;
 +        10
 +    };
 +
 +    let mut s = Some(String::new());
 +    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
 +    let _ = if let Some(x) = &mut s {
 +        x.push_str("test");
 +        x.len()
 +    } else {
 +        let _s = &s;
 +        10
 +    };
 +
 +    async fn _f1(x: u32) -> u32 {
 +        x
 +    }
 +
 +    async fn _f2() {
 +        // Don't lint. `await` can't be moved into a closure.
 +        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
 +    }
 +
 +    let _ = pattern_to_vec("hello world");
 +    let _ = complex_subpat();
 +
 +    // issue #8492
 +    let _ = s.map_or(1, |string| string.len());
 +    let _ = Some(10).map_or(5, |a| a + 1);
 +
 +    let res: Result<i32, i32> = Ok(5);
 +    let _ = res.map_or(1, |a| a + 1);
 +    let _ = res.map_or(1, |a| a + 1);
 +    let _ = res.map_or(5, |a| a + 1);
 +}
++
++#[allow(dead_code)]
++fn issue9742() -> Option<&'static str> {
++    // should not lint because of guards
++    match Some("foo  ") {
++        Some(name) if name.starts_with("foo") => Some(name.trim()),
++        _ => None,
++    }
++}
index 9eeaea12d3bc91e615cb2fac5f3d3b10c0ffd0c9,0000000000000000000000000000000000000000..23b148752cbfac64b3981024cfcc708cc90606e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,232 -1,0 +1,241 @@@
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
 +#![allow(
 +    unused_tuple_struct_fields,
 +    clippy::redundant_closure,
 +    clippy::ref_option_ref,
 +    clippy::equatable_if_let,
 +    clippy::let_unit_value
 +)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    if let Some(x) = string {
 +        (true, x)
 +    } else {
 +        (false, "hello")
 +    }
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = if let Some(s) = *string { s.len() } else { 0 };
 +    let _ = if let Some(s) = &num { s } else { &0 };
 +    let _ = if let Some(s) = &mut num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +    let _ = if let Some(ref s) = num { s } else { &0 };
 +    let _ = if let Some(mut s) = num {
 +        s += 1;
 +        s
 +    } else {
 +        0
 +    };
 +    let _ = if let Some(ref mut s) = num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    if let Some(x) = arg {
 +        let y = x * x;
 +        y * y
 +    } else {
 +        13
 +    }
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = if let Some(x) = arg {
 +        x
 +    } else {
 +        // map_or_else must be suggested
 +        side_effect()
 +    };
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = if let Some(x) = arg {
 +        x * x * x * x
 +    } else {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    };
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +// #7973
 +fn pattern_to_vec(pattern: &str) -> Vec<String> {
 +    pattern
 +        .trim_matches('/')
 +        .split('/')
 +        .flat_map(|s| {
 +            if let Some(idx) = s.find('.') {
 +                vec![s[..idx].to_string(), s[idx..].to_string()]
 +            } else {
 +                vec![s.to_string()]
 +            }
 +        })
 +        .collect::<Vec<_>>()
 +}
 +
 +enum DummyEnum {
 +    One(u8),
 +    Two,
 +}
 +
 +// should not warn since there is a compled complex subpat
 +// see #7991
 +fn complex_subpat() -> DummyEnum {
 +    let x = Some(DummyEnum::One(1));
 +    let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
 +    DummyEnum::Two
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = if let Some(x) = optional { x + 2 } else { 5 };
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
 +
 +    let _ = if let Some(x) = Some(0) {
 +        loop {
 +            if x == 0 {
 +                break x;
 +            }
 +        }
 +    } else {
 +        0
 +    };
 +
 +    // #7576
 +    const fn _f(x: Option<u32>) -> u32 {
 +        // Don't lint, `map_or` isn't const
 +        if let Some(x) = x { x } else { 10 }
 +    }
 +
 +    // #5822
 +    let s = String::new();
 +    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        s.len()
 +    };
 +
 +    let s = String::new();
 +    // Lint, both branches immutably borrow `s`.
 +    let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
 +
 +    let s = String::new();
 +    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        1
 +    };
 +
 +    let s = Some(String::new());
 +    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
 +    let _ = if let Some(x) = &s {
 +        x.len()
 +    } else {
 +        let _s = s;
 +        10
 +    };
 +
 +    let mut s = Some(String::new());
 +    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
 +    let _ = if let Some(x) = &mut s {
 +        x.push_str("test");
 +        x.len()
 +    } else {
 +        let _s = &s;
 +        10
 +    };
 +
 +    async fn _f1(x: u32) -> u32 {
 +        x
 +    }
 +
 +    async fn _f2() {
 +        // Don't lint. `await` can't be moved into a closure.
 +        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
 +    }
 +
 +    let _ = pattern_to_vec("hello world");
 +    let _ = complex_subpat();
 +
 +    // issue #8492
 +    let _ = match s {
 +        Some(string) => string.len(),
 +        None => 1,
 +    };
 +    let _ = match Some(10) {
 +        Some(a) => a + 1,
 +        None => 5,
 +    };
 +
 +    let res: Result<i32, i32> = Ok(5);
 +    let _ = match res {
 +        Ok(a) => a + 1,
 +        _ => 1,
 +    };
 +    let _ = match res {
 +        Err(_) => 1,
 +        Ok(a) => a + 1,
 +    };
 +    let _ = if let Ok(a) = res { a + 1 } else { 5 };
 +}
++
++#[allow(dead_code)]
++fn issue9742() -> Option<&'static str> {
++    // should not lint because of guards
++    match Some("foo  ") {
++        Some(name) if name.starts_with("foo") => Some(name.trim()),
++        _ => None,
++    }
++}
index 23b1aa8bebd53749cf05d23ce27ff7a4195b42d1,0000000000000000000000000000000000000000..be9a65506e13168d6f00f0da19f277abebf6a86f
mode 100644,000000..100644
--- /dev/null
@@@ -1,239 -1,0 +1,255 @@@
 +// run-rustfix
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or_else(make);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_default();
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(|_| make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_default();
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_default();
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or_else(<FakeDefault>::default);
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or_default();
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or_default();
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or_else(Foo::new);
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_default();
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_default();
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_default();
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_default();
 +
 +    let stringy = Some(String::new());
 +    let _ = stringy.unwrap_or_default();
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +mod issue6675 {
 +    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
 +        #[allow(unused)]
 +        let x = vec![0; 1000]; // future-proofing, make this function expensive.
 +        &*p
 +    }
 +
 +    unsafe fn foo() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
 +        #[rustfmt::skip]
 +        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or_default();
 +    }
 +}
 +
 +mod issue9608 {
 +    fn sig_drop() {
 +        enum X {
 +            X(std::fs::File),
 +            Y(u32),
 +        }
 +
 +        let _ = None.unwrap_or(X::Y(0));
 +    }
 +}
 +
++mod issue8993 {
++    fn g() -> i32 {
++        3
++    }
++
++    fn f(n: i32) -> i32 {
++        n
++    }
++
++    fn test_map_or() {
++        let _ = Some(4).map_or_else(g, |v| v);
++        let _ = Some(4).map_or_else(g, f);
++        let _ = Some(4).map_or(0, f);
++    }
++}
++
 +fn main() {}
index 039998f22dd7147b9caf5134f1fcb5e139b11d1d,0000000000000000000000000000000000000000..628c970463890db8d636e86de6f2694372e9c4b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,239 -1,0 +1,255 @@@
 +// run-rustfix
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::borrow_as_ptr, clippy::uninlined_format_args, clippy::unnecessary_wraps)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or(make());
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or(Vec::new());
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or(Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or(make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or(Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or(Default::default());
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or(u64::default());
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or(<FakeDefault>::default());
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or(<FakeDefault as Default>::default());
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or(vec![]);
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or(Foo::new());
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_insert(String::new());
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_insert(vec![]);
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_insert(String::new());
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_insert(vec![]);
 +
 +    let stringy = Some(String::new());
 +    let _ = stringy.unwrap_or(String::new());
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or(map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or(map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or(Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +mod issue6675 {
 +    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
 +        #[allow(unused)]
 +        let x = vec![0; 1000]; // future-proofing, make this function expensive.
 +        &*p
 +    }
 +
 +    unsafe fn foo() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or(ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or(unsafe { ptr_to_ref(s) });
 +        #[rustfmt::skip]
 +        None.unwrap_or( unsafe { ptr_to_ref(s) }    );
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or(String::new());
 +    }
 +}
 +
 +mod issue9608 {
 +    fn sig_drop() {
 +        enum X {
 +            X(std::fs::File),
 +            Y(u32),
 +        }
 +
 +        let _ = None.unwrap_or(X::Y(0));
 +    }
 +}
 +
++mod issue8993 {
++    fn g() -> i32 {
++        3
++    }
++
++    fn f(n: i32) -> i32 {
++        n
++    }
++
++    fn test_map_or() {
++        let _ = Some(4).map_or(g(), |v| v);
++        let _ = Some(4).map_or(g(), f);
++        let _ = Some(4).map_or(0, f);
++    }
++}
++
 +fn main() {}
index 113ba150c619276469b6886fb7495ac46f2a20f7,0000000000000000000000000000000000000000..ba3001db7a5f47ed934090edb0003c73a1f18098
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,172 @@@
- error: aborting due to 26 previous errors
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:48:22
 +   |
 +LL |     with_constructor.unwrap_or(make());
 +   |                      ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
 +   |
 +   = note: `-D clippy::or-fun-call` implied by `-D warnings`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:51:14
 +   |
 +LL |     with_new.unwrap_or(Vec::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:54:21
 +   |
 +LL |     with_const_args.unwrap_or(Vec::with_capacity(12));
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:57:14
 +   |
 +LL |     with_err.unwrap_or(make());
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:60:19
 +   |
 +LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:63:24
 +   |
 +LL |     with_default_trait.unwrap_or(Default::default());
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:66:23
 +   |
 +LL |     with_default_type.unwrap_or(u64::default());
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:69:18
 +   |
 +LL |     self_default.unwrap_or(<FakeDefault>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:72:18
 +   |
 +LL |     real_default.unwrap_or(<FakeDefault as Default>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:75:14
 +   |
 +LL |     with_vec.unwrap_or(vec![]);
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:78:21
 +   |
 +LL |     without_default.unwrap_or(Foo::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 +
 +error: use of `or_insert` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:81:19
 +   |
 +LL |     map.entry(42).or_insert(String::new());
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
 +
 +error: use of `or_insert` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:84:23
 +   |
 +LL |     map_vec.entry(42).or_insert(vec![]);
 +   |                       ^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
 +
 +error: use of `or_insert` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:87:21
 +   |
 +LL |     btree.entry(42).or_insert(String::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
 +
 +error: use of `or_insert` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:90:25
 +   |
 +LL |     btree_vec.entry(42).or_insert(vec![]);
 +   |                         ^^^^^^^^^^^^^^^^^ help: try this: `or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:93:21
 +   |
 +LL |     let _ = stringy.unwrap_or(String::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:101:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:103:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `or` followed by a function call
 +  --> $DIR/or_fun_call.rs:127:35
 +   |
 +LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:166:14
 +   |
 +LL |         None.unwrap_or(ptr_to_ref(s));
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:172:14
 +   |
 +LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:174:14
 +   |
 +LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:188:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:201:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:213:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:224:10
 +   |
 +LL |         .unwrap_or(String::new());
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
++error: use of `map_or` followed by a function call
++  --> $DIR/or_fun_call.rs:249:25
++   |
++LL |         let _ = Some(4).map_or(g(), |v| v);
++   |                         ^^^^^^^^^^^^^^^^^^ help: try this: `map_or_else(g, |v| v)`
++
++error: use of `map_or` followed by a function call
++  --> $DIR/or_fun_call.rs:250:25
++   |
++LL |         let _ = Some(4).map_or(g(), f);
++   |                         ^^^^^^^^^^^^^^ help: try this: `map_or_else(g, f)`
++
++error: aborting due to 28 previous errors
 +
index 993389232cc2955fc79bff5a28d0bdc0b0a8d57c,0000000000000000000000000000000000000000..5c49d46da7260853a690c4becc093ae6e4998a37
mode 100644,000000..100644
--- /dev/null
@@@ -1,234 -1,0 +1,237 @@@
 +// run-rustfix
 +#![allow(unreachable_code)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    a?;
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        (self.opt)?;
 +
 +        self.opt?;
 +
 +        let _ = Some(self.opt?);
 +
 +        let _ = self.opt?;
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = self.opt.as_ref()?;
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = self.opt?;
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    f()?;
 +
 +    Some(0)
 +}
 +
 +fn func_returning_result() -> Result<i32, i32> {
 +    Ok(1)
 +}
 +
 +fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 +    let _ = x?;
 +
 +    x?;
 +
 +    // No warning
 +    let y = if let Ok(x) = x {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // issue #7859
 +    // no warning
 +    let _ = if let Ok(x) = func_returning_result() {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // no warning
 +    if func_returning_result().is_err() {
 +        return func_returning_result();
 +    }
 +
++    // no warning
++    let _ = if let Err(e) = x { Err(e) } else { Ok(0) };
++
 +    Ok(y)
 +}
 +
 +// see issue #8019
 +pub enum NotOption {
 +    None,
 +    First,
 +    AfterFirst,
 +}
 +
 +fn obj(_: i32) -> Result<(), NotOption> {
 +    Err(NotOption::First)
 +}
 +
 +fn f() -> NotOption {
 +    if obj(2).is_err() {
 +        return NotOption::None;
 +    }
 +    NotOption::First
 +}
 +
 +fn do_something() {}
 +
 +fn err_immediate_return() -> Result<i32, i32> {
 +    func_returning_result()?;
 +    Ok(1)
 +}
 +
 +fn err_immediate_return_and_do_something() -> Result<i32, i32> {
 +    func_returning_result()?;
 +    do_something();
 +    Ok(1)
 +}
 +
 +// No warning
 +fn no_immediate_return() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        do_something();
 +        return Err(err);
 +    }
 +    Ok(1)
 +}
 +
 +// No warning
 +fn mixed_result_and_option() -> Option<i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Some(err);
 +    }
 +    None
 +}
 +
 +// No warning
 +fn else_if_check() -> Result<i32, i32> {
 +    if true {
 +        Ok(1)
 +    } else if let Err(e) = func_returning_result() {
 +        Err(e)
 +    } else {
 +        Err(-1)
 +    }
 +}
 +
 +// No warning
 +#[allow(clippy::manual_map)]
 +#[rustfmt::skip]
 +fn option_map() -> Option<bool> {
 +    if let Some(a) = Some(false) {
 +        Some(!a)
 +    } else {
 +        None
 +    }
 +}
 +
 +pub struct PatternedError {
 +    flag: bool,
 +}
 +
 +// No warning
 +fn pattern() -> Result<(), PatternedError> {
 +    let res = Ok(());
 +
 +    if let Err(err @ PatternedError { flag: true }) = res {
 +        return Err(err);
 +    }
 +
 +    res
 +}
 +
 +fn main() {}
 +
 +// should not lint, `?` operator not available in const context
 +const fn issue9175(option: Option<()>) -> Option<()> {
 +    if option.is_none() {
 +        return None;
 +    }
 +    //stuff
 +    Some(())
 +}
index 9ae0d88829af55cb91d387cadd0bc84a83a119e8,0000000000000000000000000000000000000000..d057df6a9b35de793d2954b89dfedd0be6878c45
mode 100644,000000..100644
--- /dev/null
@@@ -1,270 -1,0 +1,273 @@@
 +// run-rustfix
 +#![allow(unreachable_code)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    }
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        if (self.opt).is_none() {
 +            return None;
 +        }
 +
 +        if self.opt.is_none() {
 +            return None
 +        }
 +
 +        let _ = if self.opt.is_none() {
 +            return None;
 +        } else {
 +            self.opt
 +        };
 +
 +        let _ = if let Some(x) = self.opt {
 +            x
 +        } else {
 +            return None;
 +        };
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = if let Some(ref v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = if let Some(v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    if f().is_none() {
 +        return None;
 +    }
 +
 +    Some(0)
 +}
 +
 +fn func_returning_result() -> Result<i32, i32> {
 +    Ok(1)
 +}
 +
 +fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 +    let _ = if let Ok(x) = x { x } else { return x };
 +
 +    if x.is_err() {
 +        return x;
 +    }
 +
 +    // No warning
 +    let y = if let Ok(x) = x {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // issue #7859
 +    // no warning
 +    let _ = if let Ok(x) = func_returning_result() {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // no warning
 +    if func_returning_result().is_err() {
 +        return func_returning_result();
 +    }
 +
++    // no warning
++    let _ = if let Err(e) = x { Err(e) } else { Ok(0) };
++
 +    Ok(y)
 +}
 +
 +// see issue #8019
 +pub enum NotOption {
 +    None,
 +    First,
 +    AfterFirst,
 +}
 +
 +fn obj(_: i32) -> Result<(), NotOption> {
 +    Err(NotOption::First)
 +}
 +
 +fn f() -> NotOption {
 +    if obj(2).is_err() {
 +        return NotOption::None;
 +    }
 +    NotOption::First
 +}
 +
 +fn do_something() {}
 +
 +fn err_immediate_return() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Err(err);
 +    }
 +    Ok(1)
 +}
 +
 +fn err_immediate_return_and_do_something() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Err(err);
 +    }
 +    do_something();
 +    Ok(1)
 +}
 +
 +// No warning
 +fn no_immediate_return() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        do_something();
 +        return Err(err);
 +    }
 +    Ok(1)
 +}
 +
 +// No warning
 +fn mixed_result_and_option() -> Option<i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Some(err);
 +    }
 +    None
 +}
 +
 +// No warning
 +fn else_if_check() -> Result<i32, i32> {
 +    if true {
 +        Ok(1)
 +    } else if let Err(e) = func_returning_result() {
 +        Err(e)
 +    } else {
 +        Err(-1)
 +    }
 +}
 +
 +// No warning
 +#[allow(clippy::manual_map)]
 +#[rustfmt::skip]
 +fn option_map() -> Option<bool> {
 +    if let Some(a) = Some(false) {
 +        Some(!a)
 +    } else {
 +        None
 +    }
 +}
 +
 +pub struct PatternedError {
 +    flag: bool,
 +}
 +
 +// No warning
 +fn pattern() -> Result<(), PatternedError> {
 +    let res = Ok(());
 +
 +    if let Err(err @ PatternedError { flag: true }) = res {
 +        return Err(err);
 +    }
 +
 +    res
 +}
 +
 +fn main() {}
 +
 +// should not lint, `?` operator not available in const context
 +const fn issue9175(option: Option<()>) -> Option<()> {
 +    if option.is_none() {
 +        return None;
 +    }
 +    //stuff
 +    Some(())
 +}
index 1b6cd524b2f2339484878443a9e09f2acc00917a,0000000000000000000000000000000000000000..23172d7e535dd484e165d8ab896c271f70116127
mode 100644,000000..100644
--- /dev/null
@@@ -1,134 -1,0 +1,134 @@@
-   --> $DIR/question_mark.rs:193:5
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:7:5
 +   |
 +LL | /     if a.is_none() {
 +LL | |         return None;
 +LL | |     }
 +   | |_____^ help: replace it with: `a?;`
 +   |
 +   = note: `-D clippy::question-mark` implied by `-D warnings`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:52:9
 +   |
 +LL | /         if (self.opt).is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `(self.opt)?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:56:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:60:17
 +   |
 +LL |           let _ = if self.opt.is_none() {
 +   |  _________________^
 +LL | |             return None;
 +LL | |         } else {
 +LL | |             self.opt
 +LL | |         };
 +   | |_________^ help: replace it with: `Some(self.opt?)`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:66:17
 +   |
 +LL |           let _ = if let Some(x) = self.opt {
 +   |  _________________^
 +LL | |             x
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt?`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:83:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:91:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:99:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:106:26
 +   |
 +LL |           let v: &Vec<_> = if let Some(ref v) = self.opt {
 +   |  __________________________^
 +LL | |             v
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt.as_ref()?`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:116:17
 +   |
 +LL |           let v = if let Some(v) = self.opt {
 +   |  _________________^
 +LL | |             v
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt?`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:131:5
 +   |
 +LL | /     if f().is_none() {
 +LL | |         return None;
 +LL | |     }
 +   | |_____^ help: replace it with: `f()?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:143:13
 +   |
 +LL |     let _ = if let Ok(x) = x { x } else { return x };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:145:5
 +   |
 +LL | /     if x.is_err() {
 +LL | |         return x;
 +LL | |     }
 +   | |_____^ help: replace it with: `x?;`
 +
 +error: this block may be rewritten with the `?` operator
-   --> $DIR/question_mark.rs:200:5
++  --> $DIR/question_mark.rs:196:5
 +   |
 +LL | /     if let Err(err) = func_returning_result() {
 +LL | |         return Err(err);
 +LL | |     }
 +   | |_____^ help: replace it with: `func_returning_result()?;`
 +
 +error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:203:5
 +   |
 +LL | /     if let Err(err) = func_returning_result() {
 +LL | |         return Err(err);
 +LL | |     }
 +   | |_____^ help: replace it with: `func_returning_result()?;`
 +
 +error: aborting due to 15 previous errors
 +
index 8beae8dee08542e993e40136bfe0404fe8b5ff13,0000000000000000000000000000000000000000..689928f0479468fb8c0305b11d1299e01fd4f4ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,81 @@@
- #![allow(for_loops_over_fallibles)]
 +// 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.
 +
 +// run-rustfix
 +
 +#![allow(clippy::disallowed_names)]
 +#![allow(clippy::blocks_in_if_conditions)]
 +#![allow(clippy::box_collection)]
 +#![allow(clippy::redundant_static_lifetimes)]
 +#![allow(clippy::cognitive_complexity)]
 +#![allow(clippy::disallowed_methods)]
 +#![allow(clippy::disallowed_types)]
 +#![allow(clippy::mixed_read_write_in_expression)]
- #![warn(for_loops_over_fallibles)]
- #![warn(for_loops_over_fallibles)]
 +#![allow(clippy::useless_conversion)]
 +#![allow(clippy::match_result_ok)]
 +#![allow(clippy::overly_complex_bool_expr)]
 +#![allow(clippy::new_without_default)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::expect_used)]
 +#![allow(clippy::map_unwrap_or)]
 +#![allow(clippy::unwrap_used)]
 +#![allow(clippy::needless_borrow)]
 +#![allow(clippy::single_char_add_str)]
 +#![allow(clippy::module_name_repetitions)]
 +#![allow(clippy::recursive_format_impl)]
 +#![allow(clippy::invisible_characters)]
 +#![allow(drop_bounds)]
++#![allow(for_loops_over_fallibles)]
 +#![allow(array_into_iter)]
 +#![allow(invalid_atomic_ordering)]
 +#![allow(invalid_value)]
++#![allow(let_underscore_drop)]
 +#![allow(enum_intrinsics_non_enums)]
 +#![allow(non_fmt_panics)]
 +#![allow(named_arguments_used_positionally)]
 +#![allow(temporary_cstring_as_ptr)]
 +#![allow(unknown_lints)]
 +#![allow(unused_labels)]
 +#![warn(clippy::disallowed_names)]
 +#![warn(clippy::blocks_in_if_conditions)]
 +#![warn(clippy::blocks_in_if_conditions)]
 +#![warn(clippy::box_collection)]
 +#![warn(clippy::redundant_static_lifetimes)]
 +#![warn(clippy::cognitive_complexity)]
 +#![warn(clippy::disallowed_methods)]
 +#![warn(clippy::disallowed_types)]
 +#![warn(clippy::mixed_read_write_in_expression)]
 +#![warn(clippy::useless_conversion)]
 +#![warn(clippy::match_result_ok)]
 +#![warn(clippy::overly_complex_bool_expr)]
 +#![warn(clippy::new_without_default)]
 +#![warn(clippy::bind_instead_of_map)]
 +#![warn(clippy::expect_used)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::unwrap_used)]
 +#![warn(clippy::needless_borrow)]
 +#![warn(clippy::expect_used)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::unwrap_used)]
 +#![warn(clippy::single_char_add_str)]
 +#![warn(clippy::module_name_repetitions)]
 +#![warn(clippy::recursive_format_impl)]
 +#![warn(clippy::invisible_characters)]
 +#![warn(drop_bounds)]
 +#![warn(for_loops_over_fallibles)]
++#![warn(for_loops_over_fallibles)]
++#![warn(for_loops_over_fallibles)]
 +#![warn(array_into_iter)]
 +#![warn(invalid_atomic_ordering)]
 +#![warn(invalid_value)]
++#![warn(let_underscore_drop)]
 +#![warn(enum_intrinsics_non_enums)]
 +#![warn(non_fmt_panics)]
 +#![warn(named_arguments_used_positionally)]
 +#![warn(temporary_cstring_as_ptr)]
 +#![warn(unknown_lints)]
 +#![warn(unused_labels)]
 +
 +fn main() {}
index 9e665047baaeb93e8c779239a0e61aba8a8d41c9,0000000000000000000000000000000000000000..b74aa650ffd475946adc1f595d0e2d21b4d0fa15
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,81 @@@
- #![allow(for_loops_over_fallibles)]
 +// 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.
 +
 +// run-rustfix
 +
 +#![allow(clippy::disallowed_names)]
 +#![allow(clippy::blocks_in_if_conditions)]
 +#![allow(clippy::box_collection)]
 +#![allow(clippy::redundant_static_lifetimes)]
 +#![allow(clippy::cognitive_complexity)]
 +#![allow(clippy::disallowed_methods)]
 +#![allow(clippy::disallowed_types)]
 +#![allow(clippy::mixed_read_write_in_expression)]
- #![warn(clippy::for_loop_over_option)]
- #![warn(clippy::for_loop_over_result)]
 +#![allow(clippy::useless_conversion)]
 +#![allow(clippy::match_result_ok)]
 +#![allow(clippy::overly_complex_bool_expr)]
 +#![allow(clippy::new_without_default)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::expect_used)]
 +#![allow(clippy::map_unwrap_or)]
 +#![allow(clippy::unwrap_used)]
 +#![allow(clippy::needless_borrow)]
 +#![allow(clippy::single_char_add_str)]
 +#![allow(clippy::module_name_repetitions)]
 +#![allow(clippy::recursive_format_impl)]
 +#![allow(clippy::invisible_characters)]
 +#![allow(drop_bounds)]
++#![allow(for_loops_over_fallibles)]
 +#![allow(array_into_iter)]
 +#![allow(invalid_atomic_ordering)]
 +#![allow(invalid_value)]
++#![allow(let_underscore_drop)]
 +#![allow(enum_intrinsics_non_enums)]
 +#![allow(non_fmt_panics)]
 +#![allow(named_arguments_used_positionally)]
 +#![allow(temporary_cstring_as_ptr)]
 +#![allow(unknown_lints)]
 +#![allow(unused_labels)]
 +#![warn(clippy::blacklisted_name)]
 +#![warn(clippy::block_in_if_condition_expr)]
 +#![warn(clippy::block_in_if_condition_stmt)]
 +#![warn(clippy::box_vec)]
 +#![warn(clippy::const_static_lifetime)]
 +#![warn(clippy::cyclomatic_complexity)]
 +#![warn(clippy::disallowed_method)]
 +#![warn(clippy::disallowed_type)]
 +#![warn(clippy::eval_order_dependence)]
 +#![warn(clippy::identity_conversion)]
 +#![warn(clippy::if_let_some_result)]
 +#![warn(clippy::logic_bug)]
 +#![warn(clippy::new_without_default_derive)]
 +#![warn(clippy::option_and_then_some)]
 +#![warn(clippy::option_expect_used)]
 +#![warn(clippy::option_map_unwrap_or)]
 +#![warn(clippy::option_map_unwrap_or_else)]
 +#![warn(clippy::option_unwrap_used)]
 +#![warn(clippy::ref_in_deref)]
 +#![warn(clippy::result_expect_used)]
 +#![warn(clippy::result_map_unwrap_or_else)]
 +#![warn(clippy::result_unwrap_used)]
 +#![warn(clippy::single_char_push_str)]
 +#![warn(clippy::stutter)]
 +#![warn(clippy::to_string_in_display)]
 +#![warn(clippy::zero_width_space)]
 +#![warn(clippy::drop_bounds)]
++#![warn(clippy::for_loop_over_option)]
++#![warn(clippy::for_loop_over_result)]
 +#![warn(clippy::for_loops_over_fallibles)]
 +#![warn(clippy::into_iter_on_array)]
 +#![warn(clippy::invalid_atomic_ordering)]
 +#![warn(clippy::invalid_ref)]
++#![warn(clippy::let_underscore_drop)]
 +#![warn(clippy::mem_discriminant_non_enum)]
 +#![warn(clippy::panic_params)]
 +#![warn(clippy::positional_named_format_parameters)]
 +#![warn(clippy::temporary_cstring_as_ptr)]
 +#![warn(clippy::unknown_clippy_lints)]
 +#![warn(clippy::unused_label)]
 +
 +fn main() {}
index 63eb565185f07b31af72b755650758dcc6f5eeae,0000000000000000000000000000000000000000..622a32c5908aeaa3689041a42a6fa6e6f8920be7
mode 100644,000000..100644
--- /dev/null
@@@ -1,238 -1,0 +1,244 @@@
-   --> $DIR/rename.rs:39:9
 +error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-   --> $DIR/rename.rs:40:9
++  --> $DIR/rename.rs:40:9
 +   |
 +LL | #![warn(clippy::blacklisted_name)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 +   |
 +   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 +
 +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-   --> $DIR/rename.rs:41:9
++  --> $DIR/rename.rs:41:9
 +   |
 +LL | #![warn(clippy::block_in_if_condition_expr)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 +
 +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-   --> $DIR/rename.rs:42:9
++  --> $DIR/rename.rs:42:9
 +   |
 +LL | #![warn(clippy::block_in_if_condition_stmt)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 +
 +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-   --> $DIR/rename.rs:43:9
++  --> $DIR/rename.rs:43:9
 +   |
 +LL | #![warn(clippy::box_vec)]
 +   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 +
 +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-   --> $DIR/rename.rs:44:9
++  --> $DIR/rename.rs:44:9
 +   |
 +LL | #![warn(clippy::const_static_lifetime)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 +
 +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-   --> $DIR/rename.rs:45:9
++  --> $DIR/rename.rs:45:9
 +   |
 +LL | #![warn(clippy::cyclomatic_complexity)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 +
 +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-   --> $DIR/rename.rs:46:9
++  --> $DIR/rename.rs:46:9
 +   |
 +LL | #![warn(clippy::disallowed_method)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 +
 +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-   --> $DIR/rename.rs:47:9
++  --> $DIR/rename.rs:47:9
 +   |
 +LL | #![warn(clippy::disallowed_type)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 +
 +error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
- error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-   --> $DIR/rename.rs:48:9
-    |
- LL | #![warn(clippy::for_loop_over_option)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
- error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-   --> $DIR/rename.rs:49:9
-    |
- LL | #![warn(clippy::for_loop_over_result)]
-    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
++  --> $DIR/rename.rs:48:9
 +   |
 +LL | #![warn(clippy::eval_order_dependence)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 +
-   --> $DIR/rename.rs:50:9
 +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-   --> $DIR/rename.rs:51:9
++  --> $DIR/rename.rs:49:9
 +   |
 +LL | #![warn(clippy::identity_conversion)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 +
 +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-   --> $DIR/rename.rs:52:9
++  --> $DIR/rename.rs:50:9
 +   |
 +LL | #![warn(clippy::if_let_some_result)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 +
 +error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-   --> $DIR/rename.rs:53:9
++  --> $DIR/rename.rs:51:9
 +   |
 +LL | #![warn(clippy::logic_bug)]
 +   |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 +
 +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-   --> $DIR/rename.rs:54:9
++  --> $DIR/rename.rs:52:9
 +   |
 +LL | #![warn(clippy::new_without_default_derive)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 +
 +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-   --> $DIR/rename.rs:55:9
++  --> $DIR/rename.rs:53:9
 +   |
 +LL | #![warn(clippy::option_and_then_some)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 +
 +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-   --> $DIR/rename.rs:56:9
++  --> $DIR/rename.rs:54:9
 +   |
 +LL | #![warn(clippy::option_expect_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 +
 +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-   --> $DIR/rename.rs:57:9
++  --> $DIR/rename.rs:55:9
 +   |
 +LL | #![warn(clippy::option_map_unwrap_or)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
 +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-   --> $DIR/rename.rs:58:9
++  --> $DIR/rename.rs:56:9
 +   |
 +LL | #![warn(clippy::option_map_unwrap_or_else)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
 +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-   --> $DIR/rename.rs:59:9
++  --> $DIR/rename.rs:57:9
 +   |
 +LL | #![warn(clippy::option_unwrap_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 +
 +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-   --> $DIR/rename.rs:60:9
++  --> $DIR/rename.rs:58:9
 +   |
 +LL | #![warn(clippy::ref_in_deref)]
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 +
 +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-   --> $DIR/rename.rs:61:9
++  --> $DIR/rename.rs:59:9
 +   |
 +LL | #![warn(clippy::result_expect_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 +
 +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-   --> $DIR/rename.rs:62:9
++  --> $DIR/rename.rs:60:9
 +   |
 +LL | #![warn(clippy::result_map_unwrap_or_else)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
 +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-   --> $DIR/rename.rs:63:9
++  --> $DIR/rename.rs:61:9
 +   |
 +LL | #![warn(clippy::result_unwrap_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 +
 +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-   --> $DIR/rename.rs:64:9
++  --> $DIR/rename.rs:62:9
 +   |
 +LL | #![warn(clippy::single_char_push_str)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 +
 +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-   --> $DIR/rename.rs:65:9
++  --> $DIR/rename.rs:63:9
 +   |
 +LL | #![warn(clippy::stutter)]
 +   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 +
 +error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-   --> $DIR/rename.rs:66:9
++  --> $DIR/rename.rs:64:9
 +   |
 +LL | #![warn(clippy::to_string_in_display)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 +
 +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-   --> $DIR/rename.rs:67:9
++  --> $DIR/rename.rs:65:9
 +   |
 +LL | #![warn(clippy::zero_width_space)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 +
 +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
- error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
++  --> $DIR/rename.rs:66:9
 +   |
 +LL | #![warn(clippy::drop_bounds)]
 +   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 +
-   --> $DIR/rename.rs:69:9
++error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
++  --> $DIR/rename.rs:67:9
++   |
++LL | #![warn(clippy::for_loop_over_option)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
++
++error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
 +  --> $DIR/rename.rs:68:9
 +   |
++LL | #![warn(clippy::for_loop_over_result)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
++
++error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
++  --> $DIR/rename.rs:69:9
++   |
 +LL | #![warn(clippy::for_loops_over_fallibles)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 +
 +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-   --> $DIR/rename.rs:70:9
++  --> $DIR/rename.rs:70:9
 +   |
 +LL | #![warn(clippy::into_iter_on_array)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 +
 +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-   --> $DIR/rename.rs:71:9
++  --> $DIR/rename.rs:71:9
 +   |
 +LL | #![warn(clippy::invalid_atomic_ordering)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 +
 +error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-   --> $DIR/rename.rs:72:9
++  --> $DIR/rename.rs:72:9
 +   |
 +LL | #![warn(clippy::invalid_ref)]
 +   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 +
++error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
++  --> $DIR/rename.rs:73:9
++   |
++LL | #![warn(clippy::let_underscore_drop)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
++
 +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-   --> $DIR/rename.rs:73:9
++  --> $DIR/rename.rs:74:9
 +   |
 +LL | #![warn(clippy::mem_discriminant_non_enum)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 +
 +error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-   --> $DIR/rename.rs:74:9
++  --> $DIR/rename.rs:75:9
 +   |
 +LL | #![warn(clippy::panic_params)]
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 +
 +error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-   --> $DIR/rename.rs:75:9
++  --> $DIR/rename.rs:76:9
 +   |
 +LL | #![warn(clippy::positional_named_format_parameters)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 +
 +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-   --> $DIR/rename.rs:76:9
++  --> $DIR/rename.rs:77:9
 +   |
 +LL | #![warn(clippy::temporary_cstring_as_ptr)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 +
 +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-   --> $DIR/rename.rs:77:9
++  --> $DIR/rename.rs:78:9
 +   |
 +LL | #![warn(clippy::unknown_clippy_lints)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 +
 +error: lint `clippy::unused_label` has been renamed to `unused_labels`
- error: aborting due to 39 previous errors
++  --> $DIR/rename.rs:79:9
 +   |
 +LL | #![warn(clippy::unused_label)]
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 +
++error: aborting due to 40 previous errors
 +
index f7df3b85655013f4b02e712354355cf7abbae0d5,0000000000000000000000000000000000000000..9dd27d6dc01aaf8ae40245fc024c49b0e35cea14
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,111 @@@
 +#![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(())
 +}
 +
 +fn main() {}
index bea101fe20bf25759c252e6ff7e911e1130c2855,0000000000000000000000000000000000000000..c386edfd21571ed56b329118d9f714432811386e
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,107 @@@
-    |                                  ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes
 +error: the `Err`-variant returned from this function is very large
 +  --> $DIR/result_large_err.rs:8:23
 +   |
 +LL | pub fn large_err() -> Result<(), [u8; 512]> {
 +   |                       ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
 +   = note: `-D clippy::result-large-err` implied by `-D warnings`
 +
 +error: the `Err`-variant returned from this function is very large
 +  --> $DIR/result_large_err.rs:19:21
 +   |
 +LL |     pub fn ret() -> Result<(), Self> {
 +   |                     ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 +
 +error: the `Err`-variant returned from this function is very large
 +  --> $DIR/result_large_err.rs:24:26
 +   |
 +LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 +
 +error: the `Err`-variant returned from this function is very large
 +  --> $DIR/result_large_err.rs:29:45
 +   |
 +LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
 +   |                                             ^^^^^^^ the `Err`-variant is at least 240 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
 +
 +error: the `Err`-variant returned from this function is very large
 +  --> $DIR/result_large_err.rs:37:34
 +   |
 +LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
 +   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes
 +   |
 +   = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>`
 +
 +error: the `Err`-variant returned from this function is very large
 +  --> $DIR/result_large_err.rs:48:34
 +   |
++LL |     _Omg([u8; 512]),
++   |     --------------- the largest variant contains at least 512 bytes
++...
 +LL |     pub fn large_enum_error() -> Result<(), Self> {
-   --> $DIR/result_large_err.rs:54:25
++   |                                  ^^^^^^^^^^^^^^^^
 +   |
 +   = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:73:29
++  --> $DIR/result_large_err.rs:60:30
++   |
++LL |     _Biggest([u8; 1024]),
++   |     -------------------- the largest variant contains at least 1024 bytes
++LL |     _AlsoBig([u8; 512]),
++   |     ------------------- the variant `_AlsoBig` contains at least 512 bytes
++...
++LL |     fn large_enum_error() -> Result<(), Self> {
++   |                              ^^^^^^^^^^^^^^^^
++   |
++   = help: try reducing the size of `MultipleLargeVariants`, for example by boxing large elements or replacing it with `Box<MultipleLargeVariants>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:66:25
 +   |
 +LL |     fn large_error() -> Result<(), [u8; 512]> {
 +   |                         ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:82:40
++  --> $DIR/result_large_err.rs:85:29
 +   |
 +LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:91:34
++  --> $DIR/result_large_err.rs:94:40
 +   |
 +LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
 +   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
 +   |
 +   = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
 +
 +error: the `Err`-variant returned from this function is very large
-   --> $DIR/result_large_err.rs:95:31
++  --> $DIR/result_large_err.rs:103:34
 +   |
 +LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
 +   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
 +   |
 +   = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
 +
 +error: the `Err`-variant returned from this function is very large
- error: aborting due to 11 previous errors
++  --> $DIR/result_large_err.rs:107:31
 +   |
 +LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
 +   |
 +   = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>`
 +
++error: aborting due to 12 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b5303324bc6f17e7dde1e1dc477c1eac2b3c357
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++// run-rustfix
++#![warn(clippy::seek_from_current)]
++#![feature(custom_inner_attributes)]
++
++use std::fs::File;
++use std::io::{self, Seek, SeekFrom, Write};
++
++fn _msrv_1_50() -> io::Result<()> {
++    #![clippy::msrv = "1.50"]
++    let mut f = File::create("foo.txt")?;
++    f.write_all(b"Hi!")?;
++    f.seek(SeekFrom::Current(0))?;
++    f.seek(SeekFrom::Current(1))?;
++    Ok(())
++}
++
++fn _msrv_1_51() -> io::Result<()> {
++    #![clippy::msrv = "1.51"]
++    let mut f = File::create("foo.txt")?;
++    f.write_all(b"Hi!")?;
++    f.stream_position()?;
++    f.seek(SeekFrom::Current(1))?;
++    Ok(())
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f93639261a1808241f7e24dfc1957c4c71c1907e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++// run-rustfix
++#![warn(clippy::seek_from_current)]
++#![feature(custom_inner_attributes)]
++
++use std::fs::File;
++use std::io::{self, Seek, SeekFrom, Write};
++
++fn _msrv_1_50() -> io::Result<()> {
++    #![clippy::msrv = "1.50"]
++    let mut f = File::create("foo.txt")?;
++    f.write_all(b"Hi!")?;
++    f.seek(SeekFrom::Current(0))?;
++    f.seek(SeekFrom::Current(1))?;
++    Ok(())
++}
++
++fn _msrv_1_51() -> io::Result<()> {
++    #![clippy::msrv = "1.51"]
++    let mut f = File::create("foo.txt")?;
++    f.write_all(b"Hi!")?;
++    f.seek(SeekFrom::Current(0))?;
++    f.seek(SeekFrom::Current(1))?;
++    Ok(())
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..db1125b53cdf5c2182970a9c748633f0d9c5840d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: using `SeekFrom::Current` to start from current position
++  --> $DIR/seek_from_current.rs:21: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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..464b6cdef63933acde79d463929e163049252bd8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,137 @@@
++// run-rustfix
++#![allow(unused)]
++#![feature(custom_inner_attributes)]
++#![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);
++}
++
++fn msrv_1_54() {
++    #![clippy::msrv = "1.54"]
++
++    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);
++}
++
++fn msrv_1_55() {
++    #![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.rewind();
++
++    let mut buf = String::new();
++    f.read_to_string(&mut buf).unwrap();
++
++    assert_eq!(&buf, hello);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..68e09bd7c1f0fd4d4d210e90b58e50b04c1a1d40
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,137 @@@
++// run-rustfix
++#![allow(unused)]
++#![feature(custom_inner_attributes)]
++#![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);
++}
++
++fn msrv_1_54() {
++    #![clippy::msrv = "1.54"]
++
++    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);
++}
++
++fn msrv_1_55() {
++    #![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);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..de0eec5d909cd72695def70bc63abee7eecea714
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: used `seek` to go to the start of the stream
++  --> $DIR/seek_to_start_instead_of_rewind.rs:54: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:59:7
++   |
++LL |     t.seek(SeekFrom::Start(0));
++   |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
++
++error: used `seek` to go to the start of the stream
++  --> $DIR/seek_to_start_instead_of_rewind.rs:131:7
++   |
++LL |     f.seek(SeekFrom::Start(0));
++   |       ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `rewind()`
++
++error: aborting due to 3 previous errors
++
index 509c88ac256a8c0dc99ecdbcac0a8dd6fe97529f,0000000000000000000000000000000000000000..71dcc25d6e5b8bd0c3a31cc138074cdb8f96befd
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
-   --> $DIR/single_component_path_imports.rs:23:5
 +error: this import is redundant
- LL |     use regex;
-    |     ^^^^^^^^^^ help: remove it entirely
++  --> $DIR/single_component_path_imports.rs:5:1
 +   |
-   --> $DIR/single_component_path_imports.rs:5:1
++LL | use regex;
++   | ^^^^^^^^^^ help: remove it entirely
 +   |
 +   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
 +
 +error: this import is redundant
- LL | use regex;
-    | ^^^^^^^^^^ help: remove it entirely
++  --> $DIR/single_component_path_imports.rs:23:5
 +   |
++LL |     use regex;
++   |     ^^^^^^^^^^ help: remove it entirely
 +
 +error: aborting due to 2 previous errors
 +
index 633546f6419a2f65cdea5d65e415bb90179e0fcc,0000000000000000000000000000000000000000..330f285202d0c6a4a4747610dd68baf820b9e518
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,26 @@@
-    = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
++error: this import is redundant
++  --> $DIR/single_component_path_imports_nested_first.rs:4:1
++   |
++LL | use regex;
++   | ^^^^^^^^^^ help: remove it entirely
++   |
++   = note: `-D clippy::single-component-path-imports` implied by `-D warnings`
++
 +error: this import is redundant
 +  --> $DIR/single_component_path_imports_nested_first.rs:13:10
 +   |
 +LL |     use {regex, serde};
 +   |          ^^^^^
 +   |
 +   = help: remove this import
- error: this import is redundant
-   --> $DIR/single_component_path_imports_nested_first.rs:4:1
-    |
- LL | use regex;
-    | ^^^^^^^^^^ help: remove it entirely
 +
 +error: this import is redundant
 +  --> $DIR/single_component_path_imports_nested_first.rs:13:17
 +   |
 +LL |     use {regex, serde};
 +   |                 ^^^^^
 +   |
 +   = help: remove this import
 +
 +error: aborting due to 3 previous errors
 +
index 1883a9f8325783ecb7949b9d21bf791842c701ff,0000000000000000000000000000000000000000..d200d7310fca5b5da74013bdae02e0c5aea07a17
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,35 @@@
 +// run-rustfix
 +
 +#[derive(Copy, Clone)]
 +struct HasChars;
 +
 +impl HasChars {
 +    fn chars(self) -> std::str::Chars<'static> {
 +        "HasChars".chars()
 +    }
 +}
 +
 +fn main() {
 +    let abc = "abc";
 +    let def = String::from("def");
 +    let mut s = String::new();
 +
 +    s.push_str(abc);
 +    s.push_str(abc);
 +
 +    s.push_str("abc");
 +    s.push_str("abc");
 +
 +    s.push_str(&def);
 +    s.push_str(&def);
 +
 +    s.extend(abc.chars().skip(1));
 +    s.extend("abc".chars().skip(1));
 +    s.extend(['a', 'b', 'c'].iter());
 +
 +    let f = HasChars;
 +    s.extend(f.chars());
++
++    // issue #9735
++    s.push_str(&abc[0..2]);
 +}
index 07d0baa1be6c7ee5ecbf653ec408b491d959de48,0000000000000000000000000000000000000000..0dd96a3b21035e2228698b69c01d21b5c7623e16
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,35 @@@
 +// run-rustfix
 +
 +#[derive(Copy, Clone)]
 +struct HasChars;
 +
 +impl HasChars {
 +    fn chars(self) -> std::str::Chars<'static> {
 +        "HasChars".chars()
 +    }
 +}
 +
 +fn main() {
 +    let abc = "abc";
 +    let def = String::from("def");
 +    let mut s = String::new();
 +
 +    s.push_str(abc);
 +    s.extend(abc.chars());
 +
 +    s.push_str("abc");
 +    s.extend("abc".chars());
 +
 +    s.push_str(&def);
 +    s.extend(def.chars());
 +
 +    s.extend(abc.chars().skip(1));
 +    s.extend("abc".chars().skip(1));
 +    s.extend(['a', 'b', 'c'].iter());
 +
 +    let f = HasChars;
 +    s.extend(f.chars());
++
++    // issue #9735
++    s.extend(abc[0..2].chars());
 +}
index 6af8c9e1662b5b970237dfe206e83ce6b13cc8be,0000000000000000000000000000000000000000..b35c77fd96113292e16f15933899b09316777edb
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,28 @@@
- error: aborting due to 3 previous errors
 +error: calling `.extend(_.chars())`
 +  --> $DIR/string_extend.rs:18:5
 +   |
 +LL |     s.extend(abc.chars());
 +   |     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(abc)`
 +   |
 +   = note: `-D clippy::string-extend-chars` implied by `-D warnings`
 +
 +error: calling `.extend(_.chars())`
 +  --> $DIR/string_extend.rs:21:5
 +   |
 +LL |     s.extend("abc".chars());
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str("abc")`
 +
 +error: calling `.extend(_.chars())`
 +  --> $DIR/string_extend.rs:24:5
 +   |
 +LL |     s.extend(def.chars());
 +   |     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&def)`
 +
++error: calling `.extend(_.chars())`
++  --> $DIR/string_extend.rs:34:5
++   |
++LL |     s.extend(abc[0..2].chars());
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `s.push_str(&abc[0..2])`
++
++error: aborting due to 4 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..eb9fc63fb1d4692f2da82445f19857d79a4200c2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++#![allow(unused)]
++#![warn(clippy::suspicious_xor_used_as_pow)]
++#![allow(clippy::eq_op)]
++
++macro_rules! macro_test {
++    () => {
++        13
++    };
++}
++
++macro_rules! macro_test_inside {
++    () => {
++        1 ^ 2 // should warn even if inside macro
++    };
++}
++
++fn main() {
++    // Should warn:
++    let _ = 2 ^ 5;
++    let _ = 2i32 ^ 9i32;
++    let _ = 2i32 ^ 2i32;
++    let _ = 50i32 ^ 3i32;
++    let _ = 5i32 ^ 8i32;
++    let _ = 2i32 ^ 32i32;
++    macro_test_inside!();
++
++    // Should not warn:
++    let x = 0x02;
++    let _ = x ^ 2;
++    let _ = 2 ^ x;
++    let _ = x ^ 5;
++    let _ = 10 ^ 0b0101;
++    let _ = 2i32 ^ macro_test!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8bb3c8fbeebd3efb8c74522ce9d9bdd962b14b8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:19:13
++   |
++LL |     let _ = 2 ^ 5;
++   |             ^^^^^ help: did you mean to write: `2.pow(5)`
++   |
++   = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings`
++
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:20:13
++   |
++LL |     let _ = 2i32 ^ 9i32;
++   |             ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(9_i32)`
++
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:21:13
++   |
++LL |     let _ = 2i32 ^ 2i32;
++   |             ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(2_i32)`
++
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:22:13
++   |
++LL |     let _ = 50i32 ^ 3i32;
++   |             ^^^^^^^^^^^^ help: did you mean to write: `50_i32.pow(3_i32)`
++
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:23:13
++   |
++LL |     let _ = 5i32 ^ 8i32;
++   |             ^^^^^^^^^^^ help: did you mean to write: `5_i32.pow(8_i32)`
++
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:24:13
++   |
++LL |     let _ = 2i32 ^ 32i32;
++   |             ^^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(32_i32)`
++
++error: `^` is not the exponentiation operator
++  --> $DIR/suspicious_xor_used_as_pow.rs:13:9
++   |
++LL |         1 ^ 2 // should warn even if inside macro
++   |         ^^^^^ help: did you mean to write: `1.pow(2)`
++...
++LL |     macro_test_inside!();
++   |     -------------------- in this macro invocation
++   |
++   = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: aborting due to 7 previous errors
++
index 24b229235d33a476f5d7ba5e45d7112a188658be,0000000000000000000000000000000000000000..805a2ba5a59844543ecc2471c0c5f18d3de751cc
mode 100644,000000..100644
--- /dev/null
@@@ -1,157 -1,0 +1,166 @@@
 +// run-rustfix
 +
 +#![warn(clippy::all)]
 +#![allow(
 +    clippy::disallowed_names,
 +    clippy::no_effect,
 +    clippy::redundant_clone,
 +    redundant_semicolons,
 +    dead_code,
 +    unused_assignments
 +)]
 +
 +struct Foo(u32);
 +
 +#[derive(Clone)]
 +struct Bar {
 +    a: u32,
 +    b: u32,
 +}
 +
 +fn field() {
 +    let mut bar = Bar { a: 1, b: 2 };
 +
 +    std::mem::swap(&mut bar.a, &mut bar.b);
 +
 +    let mut baz = vec![bar.clone(), bar.clone()];
 +    let temp = baz[0].a;
 +    baz[0].a = baz[1].a;
 +    baz[1].a = temp;
 +}
 +
 +fn array() {
 +    let mut foo = [1, 2];
 +    foo.swap(0, 1);
 +
 +    foo.swap(0, 1);
 +}
 +
 +fn slice() {
 +    let foo = &mut [1, 2];
 +    foo.swap(0, 1);
 +
 +    foo.swap(0, 1);
 +}
 +
 +fn unswappable_slice() {
 +    let foo = &mut [vec![1, 2], vec![3, 4]];
 +    let temp = foo[0][1];
 +    foo[0][1] = foo[1][0];
 +    foo[1][0] = temp;
 +
 +    // swap(foo[0][1], foo[1][0]) would fail
 +    // this could use split_at_mut and mem::swap, but that is not much simpler.
 +}
 +
 +fn vec() {
 +    let mut foo = vec![1, 2];
 +    foo.swap(0, 1);
 +
 +    foo.swap(0, 1);
 +}
 +
 +fn xor_swap_locals() {
 +    // This is an xor-based swap of local variables.
 +    let mut a = 0;
 +    let mut b = 1;
 +    std::mem::swap(&mut a, &mut b)
 +}
 +
 +fn xor_field_swap() {
 +    // This is an xor-based swap of fields in a struct.
 +    let mut bar = Bar { a: 0, b: 1 };
 +    std::mem::swap(&mut bar.a, &mut bar.b)
 +}
 +
 +fn xor_slice_swap() {
 +    // This is an xor-based swap of a slice
 +    let foo = &mut [1, 2];
 +    foo.swap(0, 1)
 +}
 +
 +fn xor_no_swap() {
 +    // This is a sequence of xor-assignment statements that doesn't result in a swap.
 +    let mut a = 0;
 +    let mut b = 1;
 +    let mut c = 2;
 +    a ^= b;
 +    b ^= c;
 +    a ^= c;
 +    c ^= a;
 +}
 +
 +fn xor_unswappable_slice() {
 +    let foo = &mut [vec![1, 2], vec![3, 4]];
 +    foo[0][1] ^= foo[1][0];
 +    foo[1][0] ^= foo[0][0];
 +    foo[0][1] ^= foo[1][0];
 +
 +    // swap(foo[0][1], foo[1][0]) would fail
 +    // this could use split_at_mut and mem::swap, but that is not much simpler.
 +}
 +
 +fn distinct_slice() {
 +    let foo = &mut [vec![1, 2], vec![3, 4]];
 +    let bar = &mut [vec![1, 2], vec![3, 4]];
 +    std::mem::swap(&mut foo[0][1], &mut bar[1][0]);
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +
 +    let mut a = 42;
 +    let mut b = 1337;
 +
 +    std::mem::swap(&mut a, &mut b);
 +
 +    ; std::mem::swap(&mut a, &mut b);
 +
 +    let mut c = Foo(42);
 +
 +    std::mem::swap(&mut c.0, &mut a);
 +
 +    ; std::mem::swap(&mut c.0, &mut a);
 +}
 +
 +fn issue_8154() {
 +    struct S1 {
 +        x: i32,
 +        y: i32,
 +    }
 +    struct S2(S1);
 +    struct S3<'a, 'b>(&'a mut &'b mut S1);
 +
 +    impl std::ops::Deref for S2 {
 +        type Target = S1;
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    impl std::ops::DerefMut for S2 {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            &mut self.0
 +        }
 +    }
 +
 +    // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl.
 +    let mut s = S2(S1 { x: 0, y: 0 });
 +    let t = s.x;
 +    s.x = s.y;
 +    s.y = t;
 +
 +    // Accessing through a mutable reference is fine
 +    let mut s = S1 { x: 0, y: 0 };
 +    let mut s = &mut s;
 +    let s = S3(&mut s);
 +    std::mem::swap(&mut s.0.x, &mut s.0.y);
 +}
++
++const fn issue_9864(mut u: u32) -> u32 {
++    let mut v = 10;
++
++    let temp = u;
++    u = v;
++    v = temp;
++    u + v
++}
index a318c27919c8a4fbdb972f59d48496c695f9e590,0000000000000000000000000000000000000000..a8c878479523f6f9933d7542222e534860bcbbca
mode 100644,000000..100644
--- /dev/null
@@@ -1,181 -1,0 +1,190 @@@
 +// run-rustfix
 +
 +#![warn(clippy::all)]
 +#![allow(
 +    clippy::disallowed_names,
 +    clippy::no_effect,
 +    clippy::redundant_clone,
 +    redundant_semicolons,
 +    dead_code,
 +    unused_assignments
 +)]
 +
 +struct Foo(u32);
 +
 +#[derive(Clone)]
 +struct Bar {
 +    a: u32,
 +    b: u32,
 +}
 +
 +fn field() {
 +    let mut bar = Bar { a: 1, b: 2 };
 +
 +    let temp = bar.a;
 +    bar.a = bar.b;
 +    bar.b = temp;
 +
 +    let mut baz = vec![bar.clone(), bar.clone()];
 +    let temp = baz[0].a;
 +    baz[0].a = baz[1].a;
 +    baz[1].a = temp;
 +}
 +
 +fn array() {
 +    let mut foo = [1, 2];
 +    let temp = foo[0];
 +    foo[0] = foo[1];
 +    foo[1] = temp;
 +
 +    foo.swap(0, 1);
 +}
 +
 +fn slice() {
 +    let foo = &mut [1, 2];
 +    let temp = foo[0];
 +    foo[0] = foo[1];
 +    foo[1] = temp;
 +
 +    foo.swap(0, 1);
 +}
 +
 +fn unswappable_slice() {
 +    let foo = &mut [vec![1, 2], vec![3, 4]];
 +    let temp = foo[0][1];
 +    foo[0][1] = foo[1][0];
 +    foo[1][0] = temp;
 +
 +    // swap(foo[0][1], foo[1][0]) would fail
 +    // this could use split_at_mut and mem::swap, but that is not much simpler.
 +}
 +
 +fn vec() {
 +    let mut foo = vec![1, 2];
 +    let temp = foo[0];
 +    foo[0] = foo[1];
 +    foo[1] = temp;
 +
 +    foo.swap(0, 1);
 +}
 +
 +fn xor_swap_locals() {
 +    // This is an xor-based swap of local variables.
 +    let mut a = 0;
 +    let mut b = 1;
 +    a ^= b;
 +    b ^= a;
 +    a ^= b;
 +}
 +
 +fn xor_field_swap() {
 +    // This is an xor-based swap of fields in a struct.
 +    let mut bar = Bar { a: 0, b: 1 };
 +    bar.a ^= bar.b;
 +    bar.b ^= bar.a;
 +    bar.a ^= bar.b;
 +}
 +
 +fn xor_slice_swap() {
 +    // This is an xor-based swap of a slice
 +    let foo = &mut [1, 2];
 +    foo[0] ^= foo[1];
 +    foo[1] ^= foo[0];
 +    foo[0] ^= foo[1];
 +}
 +
 +fn xor_no_swap() {
 +    // This is a sequence of xor-assignment statements that doesn't result in a swap.
 +    let mut a = 0;
 +    let mut b = 1;
 +    let mut c = 2;
 +    a ^= b;
 +    b ^= c;
 +    a ^= c;
 +    c ^= a;
 +}
 +
 +fn xor_unswappable_slice() {
 +    let foo = &mut [vec![1, 2], vec![3, 4]];
 +    foo[0][1] ^= foo[1][0];
 +    foo[1][0] ^= foo[0][0];
 +    foo[0][1] ^= foo[1][0];
 +
 +    // swap(foo[0][1], foo[1][0]) would fail
 +    // this could use split_at_mut and mem::swap, but that is not much simpler.
 +}
 +
 +fn distinct_slice() {
 +    let foo = &mut [vec![1, 2], vec![3, 4]];
 +    let bar = &mut [vec![1, 2], vec![3, 4]];
 +    let temp = foo[0][1];
 +    foo[0][1] = bar[1][0];
 +    bar[1][0] = temp;
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +
 +    let mut a = 42;
 +    let mut b = 1337;
 +
 +    a = b;
 +    b = a;
 +
 +    ; let t = a;
 +    a = b;
 +    b = t;
 +
 +    let mut c = Foo(42);
 +
 +    c.0 = a;
 +    a = c.0;
 +
 +    ; let t = c.0;
 +    c.0 = a;
 +    a = t;
 +}
 +
 +fn issue_8154() {
 +    struct S1 {
 +        x: i32,
 +        y: i32,
 +    }
 +    struct S2(S1);
 +    struct S3<'a, 'b>(&'a mut &'b mut S1);
 +
 +    impl std::ops::Deref for S2 {
 +        type Target = S1;
 +        fn deref(&self) -> &Self::Target {
 +            &self.0
 +        }
 +    }
 +    impl std::ops::DerefMut for S2 {
 +        fn deref_mut(&mut self) -> &mut Self::Target {
 +            &mut self.0
 +        }
 +    }
 +
 +    // Don't lint. `s.0` is mutably borrowed by `s.x` and `s.y` via the deref impl.
 +    let mut s = S2(S1 { x: 0, y: 0 });
 +    let t = s.x;
 +    s.x = s.y;
 +    s.y = t;
 +
 +    // Accessing through a mutable reference is fine
 +    let mut s = S1 { x: 0, y: 0 };
 +    let mut s = &mut s;
 +    let s = S3(&mut s);
 +    let t = s.0.x;
 +    s.0.x = s.0.y;
 +    s.0.y = t;
 +}
++
++const fn issue_9864(mut u: u32) -> u32 {
++    let mut v = 10;
++
++    let temp = u;
++    u = v;
++    v = temp;
++    u + v
++}
index 001c910239affa60e9476ea9c0ed71844340db86,0000000000000000000000000000000000000000..1cbacf0feab543bdf3df61bc270965713d747d76
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,162 @@@
- #![allow(dead_code, clippy::borrow_as_ptr)]
++#![allow(dead_code, clippy::borrow_as_ptr, clippy::needless_lifetimes)]
 +
 +extern crate core;
 +
 +use std::mem::transmute as my_transmute;
 +use std::vec::Vec as MyVec;
 +
 +fn my_int() -> Usize {
 +    Usize(42)
 +}
 +
 +fn my_vec() -> MyVec<i32> {
 +    vec![]
 +}
 +
 +#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)]
 +#[warn(clippy::useless_transmute)]
 +unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
 +    // FIXME: should lint
 +    // let _: &'a T = core::intrinsics::transmute(t);
 +
 +    let _: &'a U = core::intrinsics::transmute(t);
 +
 +    let _: *const T = core::intrinsics::transmute(t);
 +
 +    let _: *mut T = core::intrinsics::transmute(t);
 +
 +    let _: *const U = core::intrinsics::transmute(t);
 +}
 +
 +#[warn(clippy::useless_transmute)]
 +fn useless() {
 +    unsafe {
 +        let _: Vec<i32> = core::intrinsics::transmute(my_vec());
 +
 +        let _: Vec<i32> = core::mem::transmute(my_vec());
 +
 +        let _: Vec<i32> = std::intrinsics::transmute(my_vec());
 +
 +        let _: Vec<i32> = std::mem::transmute(my_vec());
 +
 +        let _: Vec<i32> = my_transmute(my_vec());
 +
 +        let _: *const usize = std::mem::transmute(5_isize);
 +
 +        let _ = 5_isize as *const usize;
 +
 +        let _: *const usize = std::mem::transmute(1 + 1usize);
 +
 +        let _ = (1 + 1_usize) as *const usize;
 +    }
 +
 +    unsafe fn _f<'a, 'b>(x: &'a u32) -> &'b u32 {
 +        std::mem::transmute(x)
 +    }
 +
 +    unsafe fn _f2<'a, 'b>(x: *const (dyn Iterator<Item = u32> + 'a)) -> *const (dyn Iterator<Item = u32> + 'b) {
 +        std::mem::transmute(x)
 +    }
 +
 +    unsafe fn _f3<'a, 'b>(x: fn(&'a u32)) -> fn(&'b u32) {
 +        std::mem::transmute(x)
 +    }
 +
 +    unsafe fn _f4<'a, 'b>(x: std::borrow::Cow<'a, str>) -> std::borrow::Cow<'b, str> {
 +        std::mem::transmute(x)
 +    }
 +}
 +
 +struct Usize(usize);
 +
 +#[warn(clippy::crosspointer_transmute)]
 +fn crosspointer() {
 +    let mut int: Usize = Usize(0);
 +    let int_const_ptr: *const Usize = &int as *const Usize;
 +    let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
 +
 +    unsafe {
 +        let _: Usize = core::intrinsics::transmute(int_const_ptr);
 +
 +        let _: Usize = core::intrinsics::transmute(int_mut_ptr);
 +
 +        let _: *const Usize = core::intrinsics::transmute(my_int());
 +
 +        let _: *mut Usize = core::intrinsics::transmute(my_int());
 +    }
 +}
 +
 +#[warn(clippy::transmute_int_to_char)]
 +fn int_to_char() {
 +    let _: char = unsafe { std::mem::transmute(0_u32) };
 +    let _: char = unsafe { std::mem::transmute(0_i32) };
 +
 +    // These shouldn't warn
 +    const _: char = unsafe { std::mem::transmute(0_u32) };
 +    const _: char = unsafe { std::mem::transmute(0_i32) };
 +}
 +
 +#[warn(clippy::transmute_int_to_bool)]
 +fn int_to_bool() {
 +    let _: bool = unsafe { std::mem::transmute(0_u8) };
 +}
 +
 +#[warn(clippy::transmute_int_to_float)]
 +mod int_to_float {
 +    fn test() {
 +        let _: f32 = unsafe { std::mem::transmute(0_u32) };
 +        let _: f32 = unsafe { std::mem::transmute(0_i32) };
 +        let _: f64 = unsafe { std::mem::transmute(0_u64) };
 +        let _: f64 = unsafe { std::mem::transmute(0_i64) };
 +    }
 +
 +    mod issue_5747 {
 +        const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) };
 +        const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) };
 +
 +        const fn from_bits_32(v: i32) -> f32 {
 +            unsafe { std::mem::transmute(v) }
 +        }
 +
 +        const fn from_bits_64(v: u64) -> f64 {
 +            unsafe { std::mem::transmute(v) }
 +        }
 +    }
 +}
 +
 +mod num_to_bytes {
 +    fn test() {
 +        unsafe {
 +            let _: [u8; 1] = std::mem::transmute(0u8);
 +            let _: [u8; 4] = std::mem::transmute(0u32);
 +            let _: [u8; 16] = std::mem::transmute(0u128);
 +            let _: [u8; 1] = std::mem::transmute(0i8);
 +            let _: [u8; 4] = std::mem::transmute(0i32);
 +            let _: [u8; 16] = std::mem::transmute(0i128);
 +            let _: [u8; 4] = std::mem::transmute(0.0f32);
 +            let _: [u8; 8] = std::mem::transmute(0.0f64);
 +        }
 +    }
 +    const fn test_const() {
 +        unsafe {
 +            let _: [u8; 1] = std::mem::transmute(0u8);
 +            let _: [u8; 4] = std::mem::transmute(0u32);
 +            let _: [u8; 16] = std::mem::transmute(0u128);
 +            let _: [u8; 1] = std::mem::transmute(0i8);
 +            let _: [u8; 4] = std::mem::transmute(0i32);
 +            let _: [u8; 16] = std::mem::transmute(0i128);
 +            let _: [u8; 4] = std::mem::transmute(0.0f32);
 +            let _: [u8; 8] = std::mem::transmute(0.0f64);
 +        }
 +    }
 +}
 +
 +fn bytes_to_str(mb: &mut [u8]) {
 +    const B: &[u8] = b"";
 +
 +    let _: &str = unsafe { std::mem::transmute(B) };
 +    let _: &mut str = unsafe { std::mem::transmute(mb) };
 +    const _: &str = unsafe { std::mem::transmute(B) };
 +}
 +
 +fn main() {}
index af4f3b18443bdeeee4da62135941e2ea67f40aba,0000000000000000000000000000000000000000..c0af011d33d06a3f912914df7623a0f4df39766a
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,172 @@@
 +// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
 +// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
 +#![deny(clippy::trivially_copy_pass_by_ref)]
 +#![allow(
 +    clippy::disallowed_names,
++    clippy::needless_lifetimes,
 +    clippy::redundant_field_names,
 +    clippy::uninlined_format_args
 +)]
 +
 +#[derive(Copy, Clone)]
 +struct Foo(u32);
 +
 +#[derive(Copy, Clone)]
 +struct Bar([u8; 24]);
 +
 +#[derive(Copy, Clone)]
 +pub struct Color {
 +    pub r: u8,
 +    pub g: u8,
 +    pub b: u8,
 +    pub a: u8,
 +}
 +
 +struct FooRef<'a> {
 +    foo: &'a Foo,
 +}
 +
 +type Baz = u32;
 +
 +fn good(a: &mut u32, b: u32, c: &Bar) {}
 +
 +fn good_return_implicit_lt_ref(foo: &Foo) -> &u32 {
 +    &foo.0
 +}
 +
 +#[allow(clippy::needless_lifetimes)]
 +fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
 +    &foo.0
 +}
 +
 +fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
 +    FooRef { foo }
 +}
 +
 +#[allow(clippy::needless_lifetimes)]
 +fn good_return_explicit_lt_struct<'a>(foo: &'a Foo) -> FooRef<'a> {
 +    FooRef { foo }
 +}
 +
 +fn bad(x: &u32, y: &Foo, z: &Baz) {}
 +
 +impl Foo {
 +    fn good(self, a: &mut u32, b: u32, c: &Bar) {}
 +
 +    fn good2(&mut self) {}
 +
 +    fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
 +
 +    fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +
 +    fn bad_issue7518(self, other: &Self) {}
 +}
 +
 +impl AsRef<u32> for Foo {
 +    fn as_ref(&self) -> &u32 {
 +        &self.0
 +    }
 +}
 +
 +impl Bar {
 +    fn good(&self, a: &mut u32, b: u32, c: &Bar) {}
 +
 +    fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +}
 +
 +trait MyTrait {
 +    fn trait_method(&self, _foo: &Foo);
 +}
 +
 +pub trait MyTrait2 {
 +    fn trait_method2(&self, _color: &Color);
 +}
 +
 +impl MyTrait for Foo {
 +    fn trait_method(&self, _foo: &Foo) {
 +        unimplemented!()
 +    }
 +}
 +
 +#[allow(unused_variables)]
 +mod issue3992 {
 +    pub trait A {
 +        #[allow(clippy::trivially_copy_pass_by_ref)]
 +        fn a(b: &u16) {}
 +    }
 +
 +    #[allow(clippy::trivially_copy_pass_by_ref)]
 +    pub fn c(d: &u16) {}
 +}
 +
 +mod issue5876 {
 +    // Don't lint here as it is always inlined
 +    #[inline(always)]
 +    fn foo_always(x: &i32) {
 +        println!("{}", x);
 +    }
 +
 +    #[inline(never)]
 +    fn foo_never(x: &i32) {
 +        println!("{}", x);
 +    }
 +
 +    #[inline]
 +    fn foo(x: &i32) {
 +        println!("{}", x);
 +    }
 +}
 +
 +fn _ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
 +    Some(x)
 +}
 +
 +#[allow(clippy::needless_lifetimes)]
 +fn _ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
 +    Some(x)
 +}
 +
 +fn _with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
 +    if true { x } else { y }
 +}
 +
 +async fn _async_implicit(x: &u32) -> &u32 {
 +    x
 +}
 +
 +#[allow(clippy::needless_lifetimes)]
 +async fn _async_explicit<'a>(x: &'a u32) -> &'a u32 {
 +    x
 +}
 +
 +fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
 +    y
 +}
 +
 +fn _return_ptr(x: &u32) -> *const u32 {
 +    x
 +}
 +
 +fn _return_field_ptr(x: &(u32, u32)) -> *const u32 {
 +    &x.0
 +}
 +
 +fn _return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 {
 +    core::ptr::addr_of!(x.0)
 +}
 +
 +fn main() {
 +    let (mut foo, bar) = (Foo(0), Bar([0; 24]));
 +    let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
 +    good(&mut a, b, &c);
 +    good_return_implicit_lt_ref(&y);
 +    good_return_explicit_lt_ref(&y);
 +    bad(&x, &y, &z);
 +    foo.good(&mut a, b, &c);
 +    foo.good2();
 +    foo.bad(&x, &y, &z);
 +    Foo::bad2(&x, &y, &z);
 +    bar.good(&mut a, b, &c);
 +    Bar::bad2(&x, &y, &z);
 +    foo.as_ref();
 +}
index 6a8eca9655343a6e57bac72cdfb7b8c59afc4e94,0000000000000000000000000000000000000000..8c5cfa8a0f17c83844d31b9cb4a486b5cf572f88
mode 100644,000000..100644
--- /dev/null
@@@ -1,116 -1,0 +1,116 @@@
-   --> $DIR/trivially_copy_pass_by_ref.rs:50:11
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:50:20
++  --> $DIR/trivially_copy_pass_by_ref.rs:51:11
 +   |
 +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
 +   |           ^^^^ help: consider passing by value instead: `u32`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/trivially_copy_pass_by_ref.rs:3:9
 +   |
 +LL | #![deny(clippy::trivially_copy_pass_by_ref)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:50:29
++  --> $DIR/trivially_copy_pass_by_ref.rs:51:20
 +   |
 +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
 +   |                    ^^^^ help: consider passing by value instead: `Foo`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:57:12
++  --> $DIR/trivially_copy_pass_by_ref.rs:51:29
 +   |
 +LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
 +   |                             ^^^^ help: consider passing by value instead: `Baz`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:57:22
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:12
 +   |
 +LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
 +   |            ^^^^^ help: consider passing by value instead: `self`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:57:31
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:22
 +   |
 +LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
 +   |                      ^^^^ help: consider passing by value instead: `u32`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:57:40
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:31
 +   |
 +LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
 +   |                               ^^^^ help: consider passing by value instead: `Foo`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:59:16
++  --> $DIR/trivially_copy_pass_by_ref.rs:58:40
 +   |
 +LL |     fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
 +   |                                        ^^^^ help: consider passing by value instead: `Baz`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:59:25
++  --> $DIR/trivially_copy_pass_by_ref.rs:60:16
 +   |
 +LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +   |                ^^^^ help: consider passing by value instead: `u32`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:59:34
++  --> $DIR/trivially_copy_pass_by_ref.rs:60:25
 +   |
 +LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +   |                         ^^^^ help: consider passing by value instead: `Foo`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:61:35
++  --> $DIR/trivially_copy_pass_by_ref.rs:60:34
 +   |
 +LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +   |                                  ^^^^ help: consider passing by value instead: `Baz`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:73:16
++  --> $DIR/trivially_copy_pass_by_ref.rs:62:35
 +   |
 +LL |     fn bad_issue7518(self, other: &Self) {}
 +   |                                   ^^^^^ help: consider passing by value instead: `Self`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:73:25
++  --> $DIR/trivially_copy_pass_by_ref.rs:74:16
 +   |
 +LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +   |                ^^^^ help: consider passing by value instead: `u32`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:73:34
++  --> $DIR/trivially_copy_pass_by_ref.rs:74:25
 +   |
 +LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +   |                         ^^^^ help: consider passing by value instead: `Foo`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:77:34
++  --> $DIR/trivially_copy_pass_by_ref.rs:74:34
 +   |
 +LL |     fn bad2(x: &u32, y: &Foo, z: &Baz) {}
 +   |                                  ^^^^ help: consider passing by value instead: `Baz`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:109:21
++  --> $DIR/trivially_copy_pass_by_ref.rs:78:34
 +   |
 +LL |     fn trait_method(&self, _foo: &Foo);
 +   |                                  ^^^^ help: consider passing by value instead: `Foo`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:114:15
++  --> $DIR/trivially_copy_pass_by_ref.rs:110:21
 +   |
 +LL |     fn foo_never(x: &i32) {
 +   |                     ^^^^ help: consider passing by value instead: `i32`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
-   --> $DIR/trivially_copy_pass_by_ref.rs:141:37
++  --> $DIR/trivially_copy_pass_by_ref.rs:115:15
 +   |
 +LL |     fn foo(x: &i32) {
 +   |               ^^^^ help: consider passing by value instead: `i32`
 +
 +error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
++  --> $DIR/trivially_copy_pass_by_ref.rs:142:37
 +   |
 +LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
 +   |                                     ^^^^^^^ help: consider passing by value instead: `u32`
 +
 +error: aborting due to 18 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a0e49a8beb1ede7fba56edf5405971721939ccdf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-rustfix
++#![warn(clippy::unchecked_duration_subtraction)]
++
++use std::time::{Duration, Instant};
++
++fn main() {
++    let _first = Instant::now();
++    let second = Duration::from_secs(3);
++
++    let _ = _first.checked_sub(second).unwrap();
++
++    let _ = Instant::now().checked_sub(Duration::from_secs(5)).unwrap();
++
++    let _ = _first.checked_sub(Duration::from_secs(5)).unwrap();
++
++    let _ = Instant::now().checked_sub(second).unwrap();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a14a7ea57cc5052766486d7fbe62a9c363054e5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,17 @@@
++// run-rustfix
++#![warn(clippy::unchecked_duration_subtraction)]
++
++use std::time::{Duration, Instant};
++
++fn main() {
++    let _first = Instant::now();
++    let second = Duration::from_secs(3);
++
++    let _ = _first - second;
++
++    let _ = Instant::now() - Duration::from_secs(5);
++
++    let _ = _first - Duration::from_secs(5);
++
++    let _ = Instant::now() - second;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a2e0aa1d7c0892142e7ace042167613343619873
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: unchecked subtraction of a 'Duration' from an 'Instant'
++  --> $DIR/unchecked_duration_subtraction.rs:10:13
++   |
++LL |     let _ = _first - second;
++   |             ^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(second).unwrap()`
++   |
++   = note: `-D clippy::unchecked-duration-subtraction` implied by `-D warnings`
++
++error: unchecked subtraction of a 'Duration' from an 'Instant'
++  --> $DIR/unchecked_duration_subtraction.rs:12:13
++   |
++LL |     let _ = Instant::now() - Duration::from_secs(5);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(Duration::from_secs(5)).unwrap()`
++
++error: unchecked subtraction of a 'Duration' from an 'Instant'
++  --> $DIR/unchecked_duration_subtraction.rs:14:13
++   |
++LL |     let _ = _first - Duration::from_secs(5);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `_first.checked_sub(Duration::from_secs(5)).unwrap()`
++
++error: unchecked subtraction of a 'Duration' from an 'Instant'
++  --> $DIR/unchecked_duration_subtraction.rs:16:13
++   |
++LL |     let _ = Instant::now() - second;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Instant::now().checked_sub(second).unwrap()`
++
++error: aborting due to 4 previous errors
++
index 08aee4332151446a6fdd17603bd651cae7ee7306,0000000000000000000000000000000000000000..cbc6768033ec822bb7b4bfe6447579585a85000d
mode 100644,000000..100644
--- /dev/null
@@@ -1,493 -1,0 +1,512 @@@
 +// aux-build:proc_macro_unsafe.rs
 +
 +#![warn(clippy::undocumented_unsafe_blocks)]
 +#![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 2c466ff5c733b6f10aae80204b535b18594e0d4b,0000000000000000000000000000000000000000..ba4de9806d17523a4675a4d9250b7fe94695dfc9
mode 100644,000000..100644
--- /dev/null
@@@ -1,267 -1,0 +1,291 @@@
- error: aborting due to 31 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: 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: 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 34 previous errors
 +
index 0b14b143affd67db55a42ed5b3d094b0c4aa7815,0000000000000000000000000000000000000000..e919a6d1d8aa395c40ab36cea5c3d8d4635c506f
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,20 @@@
- error: called `.collect<Vec<String>>().join("")` on an iterator
++error: called `.collect::<Vec<String>>().join("")` on an iterator
 +  --> $DIR/unnecessary_join.rs:11:10
 +   |
 +LL |           .collect::<Vec<String>>()
 +   |  __________^
 +LL | |         .join("");
 +   | |_________________^ help: try using: `collect::<String>()`
 +   |
 +   = note: `-D clippy::unnecessary-join` implied by `-D warnings`
 +
- error: called `.collect<Vec<String>>().join("")` on an iterator
++error: called `.collect::<Vec<String>>().join("")` on an iterator
 +  --> $DIR/unnecessary_join.rs:20:10
 +   |
 +LL |           .collect::<Vec<_>>()
 +   |  __________^
 +LL | |         .join("");
 +   | |_________________^ help: try using: `collect::<String>()`
 +
 +error: aborting due to 2 previous errors
 +
index 54f85806ac3b3e5c402e18a573896fc14aa2acc2,0000000000000000000000000000000000000000..38fe6c34cfec177e90a1f644cc215a200e476ceb
mode 100644,000000..100644
--- /dev/null
@@@ -1,9 -1,0 +1,14 @@@
 +// 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;
 +}
index 8d007bc4a1dc8f3333ab72ac2f230fa56808b011,0000000000000000000000000000000000000000..a5cac64d023ae1db417a8ac95f61436cee6e5cb8
mode 100644,000000..100644
--- /dev/null
@@@ -1,9 -1,0 +1,14 @@@
 +// 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();
 +}
index 6cfb02e040283f615f1162618993fe7709dbe05c,0000000000000000000000000000000000000000..1eeb5d1de883212f1dcc1aa25a3a4c21defbca5d
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,28 @@@
- error: aborting due to 3 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: aborting due to 4 previous errors
 +
index 7bb43cf7ae82ded49dc035f3e03f798993547cc6,0000000000000000000000000000000000000000..3dd640b86f0b38992d49a132b1a6fa069816053d
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,96 @@@
 +// run-rustfix
 +
 +// The output for humans should just highlight the whole span without showing
 +// the suggested replacement, but we also want to test that suggested
 +// replacement only removes one set of parentheses, rather than naïvely
 +// stripping away any starting or ending parenthesis characters—hence this
 +// test of the JSON error format.
 +
 +#![feature(custom_inner_attributes)]
++#![feature(closure_lifetime_binder)]
 +#![rustfmt::skip]
 +
 +#![deny(clippy::unused_unit)]
 +#![allow(dead_code)]
 +#![allow(clippy::from_over_into)]
 +
 +struct Unitter;
 +impl Unitter {
 +    #[allow(clippy::no_effect)]
 +    pub fn get_unit<F: Fn(), G>(&self, f: F, _g: G)
 +    where G: Fn() {
 +        let _y: &dyn Fn() = &f;
 +        (); // this should not lint, as it's not in return type position
 +    }
 +}
 +
 +impl Into<()> for Unitter {
 +    #[rustfmt::skip]
 +    fn into(self) {
 +        
 +    }
 +}
 +
 +trait Trait {
 +    fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut(),
 +        H: Fn();
 +}
 +
 +impl Trait for Unitter {
 +    fn redundant<F: FnOnce(), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut(),
 +        H: Fn() {}
 +}
 +
 +fn return_unit() {  }
 +
 +#[allow(clippy::needless_return)]
 +#[allow(clippy::never_loop)]
 +#[allow(clippy::unit_cmp)]
 +fn main() {
 +    let u = Unitter;
 +    assert_eq!(u.get_unit(|| {}, return_unit), u.into());
 +    return_unit();
 +    loop {
 +        break;
 +    }
 +    return;
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/4076
 +fn foo() {
 +    macro_rules! foo {
 +        (recv($r:expr) -> $res:pat => $body:expr) => {
 +            $body
 +        }
 +    }
 +
 +    foo! {
 +        recv(rx) -> _x => ()
 +    }
 +}
 +
 +#[rustfmt::skip]
 +fn test(){}
 +
 +#[rustfmt::skip]
 +fn test2(){}
 +
 +#[rustfmt::skip]
 +fn test3(){}
 +
 +fn macro_expr() {
 +    macro_rules! e {
 +        () => (());
 +    }
 +    e!()
 +}
++
++mod issue9748 {
++    fn main() {
++        let _ = for<'a> |_: &'a u32| -> () {};
++    }
++}
index 21073fb802adab910d8832a307472ef04cafd341,0000000000000000000000000000000000000000..bddecf06fb76d9add6ede29d4fbdc1fde8e6e777
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,96 @@@
 +// run-rustfix
 +
 +// The output for humans should just highlight the whole span without showing
 +// the suggested replacement, but we also want to test that suggested
 +// replacement only removes one set of parentheses, rather than naïvely
 +// stripping away any starting or ending parenthesis characters—hence this
 +// test of the JSON error format.
 +
 +#![feature(custom_inner_attributes)]
++#![feature(closure_lifetime_binder)]
 +#![rustfmt::skip]
 +
 +#![deny(clippy::unused_unit)]
 +#![allow(dead_code)]
 +#![allow(clippy::from_over_into)]
 +
 +struct Unitter;
 +impl Unitter {
 +    #[allow(clippy::no_effect)]
 +    pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
 +    where G: Fn() -> () {
 +        let _y: &dyn Fn() -> () = &f;
 +        (); // this should not lint, as it's not in return type position
 +    }
 +}
 +
 +impl Into<()> for Unitter {
 +    #[rustfmt::skip]
 +    fn into(self) -> () {
 +        ()
 +    }
 +}
 +
 +trait Trait {
 +    fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut() -> (),
 +        H: Fn() -> ();
 +}
 +
 +impl Trait for Unitter {
 +    fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
 +    where
 +        G: FnMut() -> (),
 +        H: Fn() -> () {}
 +}
 +
 +fn return_unit() -> () { () }
 +
 +#[allow(clippy::needless_return)]
 +#[allow(clippy::never_loop)]
 +#[allow(clippy::unit_cmp)]
 +fn main() {
 +    let u = Unitter;
 +    assert_eq!(u.get_unit(|| {}, return_unit), u.into());
 +    return_unit();
 +    loop {
 +        break();
 +    }
 +    return();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/4076
 +fn foo() {
 +    macro_rules! foo {
 +        (recv($r:expr) -> $res:pat => $body:expr) => {
 +            $body
 +        }
 +    }
 +
 +    foo! {
 +        recv(rx) -> _x => ()
 +    }
 +}
 +
 +#[rustfmt::skip]
 +fn test()->(){}
 +
 +#[rustfmt::skip]
 +fn test2() ->(){}
 +
 +#[rustfmt::skip]
 +fn test3()-> (){}
 +
 +fn macro_expr() {
 +    macro_rules! e {
 +        () => (());
 +    }
 +    e!()
 +}
++
++mod issue9748 {
++    fn main() {
++        let _ = for<'a> |_: &'a u32| -> () {};
++    }
++}
index 0d2cb77855be1198a45f9ee31bb930a56312e151,0000000000000000000000000000000000000000..ce06738cfe473fe49f2c5b3f38868c5662dfd906
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-   --> $DIR/unused_unit.rs:19:58
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:12:9
++  --> $DIR/unused_unit.rs:20:58
 +   |
 +LL |     pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
 +   |                                                          ^^^^^^ help: remove the `-> ()`
 +   |
 +note: the lint level is defined here
-   --> $DIR/unused_unit.rs:19:28
++  --> $DIR/unused_unit.rs:13:9
 +   |
 +LL | #![deny(clippy::unused_unit)]
 +   |         ^^^^^^^^^^^^^^^^^^^
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:20:18
++  --> $DIR/unused_unit.rs:20:28
 +   |
 +LL |     pub fn get_unit<F: Fn() -> (), G>(&self, f: F, _g: G) -> ()
 +   |                            ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:21:26
++  --> $DIR/unused_unit.rs:21:18
 +   |
 +LL |     where G: Fn() -> () {
 +   |                  ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:28:18
++  --> $DIR/unused_unit.rs:22:26
 +   |
 +LL |         let _y: &dyn Fn() -> () = &f;
 +   |                          ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:29:9
++  --> $DIR/unused_unit.rs:29:18
 +   |
 +LL |     fn into(self) -> () {
 +   |                  ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit expression
-   --> $DIR/unused_unit.rs:34:29
++  --> $DIR/unused_unit.rs:30:9
 +   |
 +LL |         ()
 +   |         ^^ help: remove the final `()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:36:19
++  --> $DIR/unused_unit.rs:35:29
 +   |
 +LL |     fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
 +   |                             ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:37:16
++  --> $DIR/unused_unit.rs:37:19
 +   |
 +LL |         G: FnMut() -> (),
 +   |                   ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:41:29
++  --> $DIR/unused_unit.rs:38:16
 +   |
 +LL |         H: Fn() -> ();
 +   |                ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:43:19
++  --> $DIR/unused_unit.rs:42:29
 +   |
 +LL |     fn redundant<F: FnOnce() -> (), G, H>(&self, _f: F, _g: G, _h: H)
 +   |                             ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:44:16
++  --> $DIR/unused_unit.rs:44:19
 +   |
 +LL |         G: FnMut() -> (),
 +   |                   ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:47:17
++  --> $DIR/unused_unit.rs:45:16
 +   |
 +LL |         H: Fn() -> () {}
 +   |                ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:47:26
++  --> $DIR/unused_unit.rs:48:17
 +   |
 +LL | fn return_unit() -> () { () }
 +   |                 ^^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit expression
-   --> $DIR/unused_unit.rs:57:14
++  --> $DIR/unused_unit.rs:48:26
 +   |
 +LL | fn return_unit() -> () { () }
 +   |                          ^^ help: remove the final `()`
 +
 +error: unneeded `()`
-   --> $DIR/unused_unit.rs:59:11
++  --> $DIR/unused_unit.rs:58:14
 +   |
 +LL |         break();
 +   |              ^^ help: remove the `()`
 +
 +error: unneeded `()`
-   --> $DIR/unused_unit.rs:76:10
++  --> $DIR/unused_unit.rs:60:11
 +   |
 +LL |     return();
 +   |           ^^ help: remove the `()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:79:11
++  --> $DIR/unused_unit.rs:77:10
 +   |
 +LL | fn test()->(){}
 +   |          ^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
-   --> $DIR/unused_unit.rs:82:11
++  --> $DIR/unused_unit.rs:80:11
 +   |
 +LL | fn test2() ->(){}
 +   |           ^^^^^ help: remove the `-> ()`
 +
 +error: unneeded unit return type
++  --> $DIR/unused_unit.rs:83:11
 +   |
 +LL | fn test3()-> (){}
 +   |           ^^^^^ help: remove the `-> ()`
 +
 +error: aborting due to 19 previous errors
 +
index e88d580f7bd28dfd464c82723ebd9564ed9f351c,0000000000000000000000000000000000000000..d49bf2b322837aed3f889898fd61fdb1e8933703
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,27 @@@
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap.rs:5:13
 +   |
 +LL |     let _ = opt.unwrap();
 +   |             ^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +   = note: `-D clippy::unwrap-used` implied by `-D warnings`
 +
- error: used `unwrap()` on `a Result` value
++error: used `unwrap()` on a `Result` value
 +  --> $DIR/unwrap.rs:10:13
 +   |
 +LL |     let _ = res.unwrap();
 +   |             ^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
 +
- error: used `unwrap_err()` on `a Result` value
++error: used `unwrap_err()` on a `Result` value
 +  --> $DIR/unwrap.rs:11:13
 +   |
 +LL |     let _ = res.unwrap_err();
 +   |             ^^^^^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `Ok` case gracefully, consider using `expect_err()` to provide a better panic message
 +
 +error: aborting due to 3 previous errors
 +
index 211d2be18342d7da6e5f7803e733af55e983c8a1,0000000000000000000000000000000000000000..fe4ecef11453553809f4a869e6ce66a899ab5124
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
- error: used `unwrap()` on `an Option` value
++error: used `unwrap()` on an `Option` value
 +  --> $DIR/unwrap_expect_used.rs:23:5
 +   |
 +LL |     Some(3).unwrap();
 +   |     ^^^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is `None`, it will panic
 +   = note: `-D clippy::unwrap-used` implied by `-D warnings`
 +
- error: used `expect()` on `an Option` value
++error: used `expect()` on an `Option` value
 +  --> $DIR/unwrap_expect_used.rs:24:5
 +   |
 +LL |     Some(3).expect("Hello world!");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is `None`, it will panic
 +   = note: `-D clippy::expect-used` implied by `-D warnings`
 +
- error: used `unwrap()` on `a Result` value
++error: used `unwrap()` on a `Result` value
 +  --> $DIR/unwrap_expect_used.rs:31:5
 +   |
 +LL |     a.unwrap();
 +   |     ^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
- error: used `expect()` on `a Result` value
++error: used `expect()` on a `Result` value
 +  --> $DIR/unwrap_expect_used.rs:32:5
 +   |
 +LL |     a.expect("Hello world!");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
- error: used `unwrap_err()` on `a Result` value
++error: used `unwrap_err()` on a `Result` value
 +  --> $DIR/unwrap_expect_used.rs:33:5
 +   |
 +LL |     a.unwrap_err();
 +   |     ^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Ok`, it will panic
 +
- error: used `expect_err()` on `a Result` value
++error: used `expect_err()` on a `Result` value
 +  --> $DIR/unwrap_expect_used.rs:34:5
 +   |
 +LL |     a.expect_err("Hello error!");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Ok`, it will panic
 +
 +error: aborting due to 6 previous errors
 +
index bfb41e4394731e03969834ae9815017d6a91fcd2,0000000000000000000000000000000000000000..a0c003f5b1eaca44b507a864d53344568b78fb9a
mode 100644,000000..100644
--- /dev/null
@@@ -1,9 -1,0 +1,9 @@@
- #![warn(clippy::all)]
++#![warn(clippy::all, clippy::or_fun_call)]
 +
 +fn main() {
 +    let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
 +}
 +
 +fn new_lines() {
 +    let s = Some(String::from("test string")).unwrap_or("Fail".to_string()).len();
 +}
index 9bcd692fb3511d805c24f6b49902eb31300936de,0000000000000000000000000000000000000000..4e779308d024187acf9b20903b58f038ef62a08a
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,152 @@@
-         // FIXME: applicable here
-         Bad
 +// run-rustfix
 +
 +#![warn(clippy::use_self)]
 +#![allow(dead_code)]
 +#![allow(clippy::should_implement_trait, clippy::boxed_local)]
 +
 +use std::ops::Mul;
 +
 +trait SelfTrait {
 +    fn refs(p1: &Self) -> &Self;
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
 +    fn mut_refs(p1: &mut Self) -> &mut Self;
 +    fn nested(p1: Box<Self>, p2: (&u8, &Self));
 +    fn vals(r: Self) -> Self;
 +}
 +
 +#[derive(Default)]
 +struct Bad;
 +
 +impl SelfTrait for Bad {
 +    fn refs(p1: &Self) -> &Self {
 +        p1
 +    }
 +
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
 +        p1
 +    }
 +
 +    fn mut_refs(p1: &mut Self) -> &mut Self {
 +        p1
 +    }
 +
 +    fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
 +
 +    fn vals(_: Self) -> Self {
 +        Self::default()
 +    }
 +}
 +
 +impl Mul for Bad {
 +    type Output = Self;
 +
 +    fn mul(self, rhs: Self) -> Self {
 +        rhs
 +    }
 +}
 +
 +impl Clone for Bad {
 +    fn clone(&self) -> Self {
++        Self
 +    }
 +}
 +
 +#[derive(Default)]
 +struct Good;
 +
 +impl SelfTrait for Good {
 +    fn refs(p1: &Self) -> &Self {
 +        p1
 +    }
 +
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
 +        p1
 +    }
 +
 +    fn mut_refs(p1: &mut Self) -> &mut Self {
 +        p1
 +    }
 +
 +    fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
 +
 +    fn vals(_: Self) -> Self {
 +        Self::default()
 +    }
 +}
 +
 +impl Mul for Good {
 +    type Output = Self;
 +
 +    fn mul(self, rhs: Self) -> Self {
 +        rhs
 +    }
 +}
 +
 +trait NameTrait {
 +    fn refs(p1: &u8) -> &u8;
 +    fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
 +    fn mut_refs(p1: &mut u8) -> &mut u8;
 +    fn nested(p1: Box<u8>, p2: (&u8, &u8));
 +    fn vals(p1: u8) -> u8;
 +}
 +
 +// Using `Self` instead of the type name is OK
 +impl NameTrait for u8 {
 +    fn refs(p1: &Self) -> &Self {
 +        p1
 +    }
 +
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
 +        p1
 +    }
 +
 +    fn mut_refs(p1: &mut Self) -> &mut Self {
 +        p1
 +    }
 +
 +    fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
 +
 +    fn vals(_: Self) -> Self {
 +        Self::default()
 +    }
 +}
 +
++mod impl_in_macro {
++    macro_rules! parse_ip_impl {
++        // minimized from serde=1.0.118
++        ($ty:ty) => {
++            impl FooTrait for $ty {
++                fn new() -> Self {
++                    <$ty>::bar()
++                }
++            }
++        };
++    }
++
++    struct Foo;
++
++    trait FooTrait {
++        fn new() -> Self;
++    }
++
++    impl Foo {
++        fn bar() -> Self {
++            Self
++        }
++    }
++    parse_ip_impl!(Foo); // Should not lint
++}
++
++mod full_path_replacement {
++    trait Error {
++        fn custom<T: std::fmt::Display>(_msg: T) -> Self;
++    }
++
++    impl Error for std::fmt::Error {
++        fn custom<T: std::fmt::Display>(_msg: T) -> Self {
++            Self // Should lint
++        }
++    }
++}
++
 +fn main() {}
index de305d40f330b1a4707995a0f286a844b6da0449,0000000000000000000000000000000000000000..325dc73b21ea9565a9e304e551fffe7dd12e62f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,152 @@@
-         // FIXME: applicable here
 +// run-rustfix
 +
 +#![warn(clippy::use_self)]
 +#![allow(dead_code)]
 +#![allow(clippy::should_implement_trait, clippy::boxed_local)]
 +
 +use std::ops::Mul;
 +
 +trait SelfTrait {
 +    fn refs(p1: &Self) -> &Self;
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self;
 +    fn mut_refs(p1: &mut Self) -> &mut Self;
 +    fn nested(p1: Box<Self>, p2: (&u8, &Self));
 +    fn vals(r: Self) -> Self;
 +}
 +
 +#[derive(Default)]
 +struct Bad;
 +
 +impl SelfTrait for Bad {
 +    fn refs(p1: &Bad) -> &Bad {
 +        p1
 +    }
 +
 +    fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
 +        p1
 +    }
 +
 +    fn mut_refs(p1: &mut Bad) -> &mut Bad {
 +        p1
 +    }
 +
 +    fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
 +
 +    fn vals(_: Bad) -> Bad {
 +        Bad::default()
 +    }
 +}
 +
 +impl Mul for Bad {
 +    type Output = Bad;
 +
 +    fn mul(self, rhs: Bad) -> Bad {
 +        rhs
 +    }
 +}
 +
 +impl Clone for Bad {
 +    fn clone(&self) -> Self {
 +        Bad
 +    }
 +}
 +
 +#[derive(Default)]
 +struct Good;
 +
 +impl SelfTrait for Good {
 +    fn refs(p1: &Self) -> &Self {
 +        p1
 +    }
 +
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
 +        p1
 +    }
 +
 +    fn mut_refs(p1: &mut Self) -> &mut Self {
 +        p1
 +    }
 +
 +    fn nested(_p1: Box<Self>, _p2: (&u8, &Self)) {}
 +
 +    fn vals(_: Self) -> Self {
 +        Self::default()
 +    }
 +}
 +
 +impl Mul for Good {
 +    type Output = Self;
 +
 +    fn mul(self, rhs: Self) -> Self {
 +        rhs
 +    }
 +}
 +
 +trait NameTrait {
 +    fn refs(p1: &u8) -> &u8;
 +    fn ref_refs<'a>(p1: &'a &'a u8) -> &'a &'a u8;
 +    fn mut_refs(p1: &mut u8) -> &mut u8;
 +    fn nested(p1: Box<u8>, p2: (&u8, &u8));
 +    fn vals(p1: u8) -> u8;
 +}
 +
 +// Using `Self` instead of the type name is OK
 +impl NameTrait for u8 {
 +    fn refs(p1: &Self) -> &Self {
 +        p1
 +    }
 +
 +    fn ref_refs<'a>(p1: &'a &'a Self) -> &'a &'a Self {
 +        p1
 +    }
 +
 +    fn mut_refs(p1: &mut Self) -> &mut Self {
 +        p1
 +    }
 +
 +    fn nested(_p1: Box<Self>, _p2: (&Self, &Self)) {}
 +
 +    fn vals(_: Self) -> Self {
 +        Self::default()
 +    }
 +}
 +
++mod impl_in_macro {
++    macro_rules! parse_ip_impl {
++        // minimized from serde=1.0.118
++        ($ty:ty) => {
++            impl FooTrait for $ty {
++                fn new() -> Self {
++                    <$ty>::bar()
++                }
++            }
++        };
++    }
++
++    struct Foo;
++
++    trait FooTrait {
++        fn new() -> Self;
++    }
++
++    impl Foo {
++        fn bar() -> Self {
++            Self
++        }
++    }
++    parse_ip_impl!(Foo); // Should not lint
++}
++
++mod full_path_replacement {
++    trait Error {
++        fn custom<T: std::fmt::Display>(_msg: T) -> Self;
++    }
++
++    impl Error for std::fmt::Error {
++        fn custom<T: std::fmt::Display>(_msg: T) -> Self {
++            std::fmt::Error // Should lint
++        }
++    }
++}
++
 +fn main() {}
index 55af3ff2a93d9af515ccfea3db0cf2afd2a0be11,0000000000000000000000000000000000000000..090729b9c3d54175c675db9c078aed3d7f0f5794
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,100 @@@
- error: aborting due to 14 previous errors
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:21:18
 +   |
 +LL |     fn refs(p1: &Bad) -> &Bad {
 +   |                  ^^^ help: use the applicable keyword: `Self`
 +   |
 +   = note: `-D clippy::use-self` implied by `-D warnings`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:21:27
 +   |
 +LL |     fn refs(p1: &Bad) -> &Bad {
 +   |                           ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:25:33
 +   |
 +LL |     fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
 +   |                                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:25:49
 +   |
 +LL |     fn ref_refs<'a>(p1: &'a &'a Bad) -> &'a &'a Bad {
 +   |                                                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:29:26
 +   |
 +LL |     fn mut_refs(p1: &mut Bad) -> &mut Bad {
 +   |                          ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:29:39
 +   |
 +LL |     fn mut_refs(p1: &mut Bad) -> &mut Bad {
 +   |                                       ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:33:24
 +   |
 +LL |     fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
 +   |                        ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:33:42
 +   |
 +LL |     fn nested(_p1: Box<Bad>, _p2: (&u8, &Bad)) {}
 +   |                                          ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:35:16
 +   |
 +LL |     fn vals(_: Bad) -> Bad {
 +   |                ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:35:24
 +   |
 +LL |     fn vals(_: Bad) -> Bad {
 +   |                        ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:36:9
 +   |
 +LL |         Bad::default()
 +   |         ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:41:19
 +   |
 +LL |     type Output = Bad;
 +   |                   ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:43:23
 +   |
 +LL |     fn mul(self, rhs: Bad) -> Bad {
 +   |                       ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self_trait.rs:43:31
 +   |
 +LL |     fn mul(self, rhs: Bad) -> Bad {
 +   |                               ^^^ help: use the applicable keyword: `Self`
 +
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:50:9
++   |
++LL |         Bad
++   |         ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self_trait.rs:147:13
++   |
++LL |             std::fmt::Error // Should lint
++   |             ^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 16 previous errors
 +
index c23231a99e9f049015d44694fbd8d58ee2b8c1c3,0000000000000000000000000000000000000000..871e4fb5c3a9fad8fd815758892f08ffd01423d8
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,86 @@@
- fn test_indented_attr() {
-     #![allow(clippy::almost_swapped)]
-     use std::collections::HashSet;
-     let _ = HashSet::<u32>::default();
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
++#![allow(unused)]
 +#![warn(clippy::useless_attribute)]
 +#![warn(unreachable_pub)]
 +#![feature(rustc_private)]
 +
 +#![allow(dead_code)]
 +#![cfg_attr(feature = "cargo-clippy", allow(dead_code))]
 +#[rustfmt::skip]
 +#[allow(unused_imports)]
 +#[allow(unused_extern_crates)]
 +#[macro_use]
 +extern crate rustc_middle;
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
++fn test_indented_attr() {
++    #![allow(clippy::almost_swapped)]
++    use std::collections::HashSet;
++
++    let _ = HashSet::<u32>::default();
++}
++
 +// don't lint on unused_import for `use` items
 +#[allow(unused_imports)]
 +use std::collections;
 +
 +// don't lint on unused for `use` items
 +#[allow(unused)]
 +use std::option;
 +
 +// don't lint on deprecated for `use` items
 +mod foo {
 +    #[deprecated]
 +    pub struct Bar;
 +}
 +#[allow(deprecated)]
 +pub use foo::Bar;
 +
 +// This should not trigger the lint. There's lint level definitions inside the external derive
 +// that would trigger the useless_attribute lint.
 +#[derive(DeriveSomething)]
 +struct Baz;
 +
 +// don't lint on unreachable_pub for `use` items
 +mod a {
 +    mod b {
 +        #[allow(dead_code)]
 +        #[allow(unreachable_pub)]
 +        pub struct C;
 +    }
 +
 +    #[allow(unreachable_pub)]
 +    pub use self::b::C;
 +}
 +
 +// don't lint on clippy::wildcard_imports for `use` items
 +#[allow(clippy::wildcard_imports)]
 +pub use std::io::prelude::*;
 +
 +// don't lint on clippy::enum_glob_use for `use` items
 +#[allow(clippy::enum_glob_use)]
 +pub use std::cmp::Ordering::*;
 +
 +// don't lint on clippy::redundant_pub_crate
 +mod c {
 +    #[allow(clippy::redundant_pub_crate)]
 +    pub(crate) struct S;
 +}
 +
++// https://github.com/rust-lang/rust-clippy/issues/7511
++pub mod split {
++    #[allow(clippy::module_name_repetitions)]
++    pub use regex::SplitN;
 +}
 +
++// https://github.com/rust-lang/rust-clippy/issues/8768
++#[allow(clippy::single_component_path_imports)]
++use regex;
++
 +fn main() {
 +    test_indented_attr();
 +}
index 7a7b198ea6078e701ce33c9faf5269f72820f5c4,0000000000000000000000000000000000000000..cb50736ba395a8aed5e295e98d653339a7408920
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,86 @@@
- fn test_indented_attr() {
-     #[allow(clippy::almost_swapped)]
-     use std::collections::HashSet;
-     let _ = HashSet::<u32>::default();
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
++#![allow(unused)]
 +#![warn(clippy::useless_attribute)]
 +#![warn(unreachable_pub)]
 +#![feature(rustc_private)]
 +
 +#[allow(dead_code)]
 +#[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
 +#[rustfmt::skip]
 +#[allow(unused_imports)]
 +#[allow(unused_extern_crates)]
 +#[macro_use]
 +extern crate rustc_middle;
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
++fn test_indented_attr() {
++    #[allow(clippy::almost_swapped)]
++    use std::collections::HashSet;
++
++    let _ = HashSet::<u32>::default();
++}
++
 +// don't lint on unused_import for `use` items
 +#[allow(unused_imports)]
 +use std::collections;
 +
 +// don't lint on unused for `use` items
 +#[allow(unused)]
 +use std::option;
 +
 +// don't lint on deprecated for `use` items
 +mod foo {
 +    #[deprecated]
 +    pub struct Bar;
 +}
 +#[allow(deprecated)]
 +pub use foo::Bar;
 +
 +// This should not trigger the lint. There's lint level definitions inside the external derive
 +// that would trigger the useless_attribute lint.
 +#[derive(DeriveSomething)]
 +struct Baz;
 +
 +// don't lint on unreachable_pub for `use` items
 +mod a {
 +    mod b {
 +        #[allow(dead_code)]
 +        #[allow(unreachable_pub)]
 +        pub struct C;
 +    }
 +
 +    #[allow(unreachable_pub)]
 +    pub use self::b::C;
 +}
 +
 +// don't lint on clippy::wildcard_imports for `use` items
 +#[allow(clippy::wildcard_imports)]
 +pub use std::io::prelude::*;
 +
 +// don't lint on clippy::enum_glob_use for `use` items
 +#[allow(clippy::enum_glob_use)]
 +pub use std::cmp::Ordering::*;
 +
 +// don't lint on clippy::redundant_pub_crate
 +mod c {
 +    #[allow(clippy::redundant_pub_crate)]
 +    pub(crate) struct S;
 +}
 +
++// https://github.com/rust-lang/rust-clippy/issues/7511
++pub mod split {
++    #[allow(clippy::module_name_repetitions)]
++    pub use regex::SplitN;
 +}
 +
++// https://github.com/rust-lang/rust-clippy/issues/8768
++#[allow(clippy::single_component_path_imports)]
++use regex;
++
 +fn main() {
 +    test_indented_attr();
 +}
index 255d2876355316ed987b39a86231806c7f122d31,0000000000000000000000000000000000000000..a7ea0df22945a192556c7ddc77cd2a56b1c2b60a
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,22 @@@
-   --> $DIR/useless_attribute.rs:8:1
 +error: useless lint attribute
-   --> $DIR/useless_attribute.rs:9:1
++  --> $DIR/useless_attribute.rs:9:1
 +   |
 +LL | #[allow(dead_code)]
 +   | ^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(dead_code)]`
 +   |
 +   = note: `-D clippy::useless-attribute` implied by `-D warnings`
 +
 +error: useless lint attribute
-   --> $DIR/useless_attribute.rs:67:5
++  --> $DIR/useless_attribute.rs:10:1
 +   |
 +LL | #[cfg_attr(feature = "cargo-clippy", allow(dead_code))]
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![cfg_attr(feature = "cargo-clippy", allow(dead_code)`
 +
 +error: useless lint attribute
++  --> $DIR/useless_attribute.rs:21:5
 +   |
 +LL |     #[allow(clippy::almost_swapped)]
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if you just forgot a `!`, use: `#![allow(clippy::almost_swapped)]`
 +
 +error: aborting due to 3 previous errors
 +
index a6d8d0307ce536900c51a6986b034afc2690fa74,0000000000000000000000000000000000000000..7a85386a3df4b8fc9934fe30010e5f87820fe12d
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,94 @@@
- fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +#![allow(clippy::single_match_else)]
 +
 +use rustc_tools_util::VersionInfo;
 +use std::fs;
 +
 +#[test]
-     let clippy_lints_version = read_version("clippy_lints/Cargo.toml");
-     let clippy_utils_version = read_version("clippy_utils/Cargo.toml");
++fn consistent_clippy_crate_versions() {
 +    fn read_version(path: &str) -> String {
 +        let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{path}`: {e:?}"));
 +        contents
 +            .lines()
 +            .filter_map(|l| l.split_once('='))
 +            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))
 +            .unwrap_or_else(|| panic!("error finding version in `{path}`"))
 +            .to_string()
 +    }
 +
 +    // do not run this test inside the upstream rustc repo:
 +    // https://github.com/rust-lang/rust-clippy/issues/6683
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    let clippy_version = read_version("Cargo.toml");
-     assert_eq!(clippy_version, clippy_lints_version);
-     assert_eq!(clippy_version, clippy_utils_version);
 +
++    let paths = [
++        "declare_clippy_lint/Cargo.toml",
++        "clippy_lints/Cargo.toml",
++        "clippy_utils/Cargo.toml",
++    ];
++
++    for path in paths {
++        assert_eq!(clippy_version, read_version(path), "{path} version differs");
++    }
 +}
 +
 +#[test]
 +fn check_that_clippy_has_the_same_major_version_as_rustc() {
 +    // do not run this test inside the upstream rustc repo:
 +    // https://github.com/rust-lang/rust-clippy/issues/6683
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    let clippy_version = rustc_tools_util::get_version_info!();
 +    let clippy_major = clippy_version.major;
 +    let clippy_minor = clippy_version.minor;
 +    let clippy_patch = clippy_version.patch;
 +
 +    // get the rustc version either from the rustc installed with the toolchain file or from
 +    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
 +    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
 +    let rustc_version = String::from_utf8(
 +        std::process::Command::new(rustc)
 +            .arg("--version")
 +            .output()
 +            .expect("failed to run `rustc --version`")
 +            .stdout,
 +    )
 +    .unwrap();
 +    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
 +    let vsplit: Vec<&str> = rustc_version
 +        .split(' ')
 +        .nth(1)
 +        .unwrap()
 +        .split('-')
 +        .next()
 +        .unwrap()
 +        .split('.')
 +        .collect();
 +    match vsplit.as_slice() {
 +        [rustc_major, rustc_minor, _rustc_patch] => {
 +            // clippy 0.1.XX should correspond to rustc 1.XX.0
 +            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
 +            assert_eq!(
 +                clippy_minor.to_string(),
 +                *rustc_major,
 +                "clippy minor version does not equal rustc major version"
 +            );
 +            assert_eq!(
 +                clippy_patch.to_string(),
 +                *rustc_minor,
 +                "clippy patch version does not equal rustc minor version"
 +            );
 +            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
 +            // we don't want our tests failing suddenly
 +        },
 +        _ => {
 +            panic!("Failed to parse rustc version: {vsplit:?}");
 +        },
 +    };
 +}