]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'd9ddce8a223cb9916389c039777b6966ea448dc8' into clippyup
authorPhilipp Krones <hello@philkrones.com>
Sat, 4 Jun 2022 11:34:07 +0000 (13:34 +0200)
committerPhilipp Krones <hello@philkrones.com>
Sat, 4 Jun 2022 11:34:07 +0000 (13:34 +0200)
258 files changed:
1  2 
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/serve.rs
src/tools/clippy/clippy_lints/src/almost_complete_letter_range.rs
src/tools/clippy/clippy_lints/src/approx_const.rs
src/tools/clippy/clippy_lints/src/as_conversions.rs
src/tools/clippy/clippy_lints/src/as_underscore.rs
src/tools/clippy/clippy_lints/src/assign_ops.rs
src/tools/clippy/clippy_lints/src/attrs.rs
src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
src/tools/clippy/clippy_lints/src/booleans.rs
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
src/tools/clippy/clippy_lints/src/bytecount.rs
src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
src/tools/clippy/clippy_lints/src/checked_conversions.rs
src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
src/tools/clippy/clippy_lints/src/collapsible_if.rs
src/tools/clippy/clippy_lints/src/comparison_chain.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
src/tools/clippy/clippy_lints/src/dbg_macro.rs
src/tools/clippy/clippy_lints/src/derivable_impls.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs
src/tools/clippy/clippy_lints/src/double_comparison.rs
src/tools/clippy/clippy_lints/src/double_parens.rs
src/tools/clippy/clippy_lints/src/duration_subsec.rs
src/tools/clippy/clippy_lints/src/else_if_without_else.rs
src/tools/clippy/clippy_lints/src/empty_enum.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/enum_variants.rs
src/tools/clippy/clippy_lints/src/eq_op.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/explicit_write.rs
src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
src/tools/clippy/clippy_lints/src/float_literal.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/formatting.rs
src/tools/clippy/clippy_lints/src/get_first.rs
src/tools/clippy/clippy_lints/src/identity_op.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/let_if_seq.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.register_style.rs
src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/mod.rs
src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
src/tools/clippy/clippy_lints/src/loops/never_loop.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
src/tools/clippy/clippy_lints/src/matches/manual_map.rs
src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs
src/tools/clippy/clippy_lints/src/matches/match_bool.rs
src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs
src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/needless_match.rs
src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
src/tools/clippy/clippy_lints/src/matches/try_err.rs
src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs
src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs
src/tools/clippy/clippy_lints/src/methods/option_map_or_none.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/minmax.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/mismatching_type_param_order.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/mutable_debug_assertion.rs
src/tools/clippy/clippy_lints/src/needless_late_init.rs
src/tools/clippy/clippy_lints/src/numeric_arithmetic.rs
src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs
src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
src/tools/clippy/clippy_lints/src/unused_rounding.rs
src/tools/clippy/clippy_lints/src/use_self.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_utils/src/consts.rs
src/tools/clippy/clippy_utils/src/higher.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/ty.rs
src/tools/clippy/doc/adding_lints.md
src/tools/clippy/doc/common_tools_writing_lints.md
src/tools/clippy/lintcheck/Cargo.toml
src/tools/clippy/lintcheck/src/config.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
src/tools/clippy/tests/ui-toml/dbg_macro/clippy.toml
src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.rs
src/tools/clippy/tests/ui-toml/dbg_macro/dbg_macro.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/almost_complete_letter_range.fixed
src/tools/clippy/tests/ui/almost_complete_letter_range.rs
src/tools/clippy/tests/ui/almost_complete_letter_range.stderr
src/tools/clippy/tests/ui/as_underscore.fixed
src/tools/clippy/tests/ui/as_underscore.rs
src/tools/clippy/tests/ui/as_underscore.stderr
src/tools/clippy/tests/ui/bind_instead_of_map_multipart.fixed
src/tools/clippy/tests/ui/bind_instead_of_map_multipart.rs
src/tools/clippy/tests/ui/bind_instead_of_map_multipart.stderr
src/tools/clippy/tests/ui/borrow_deref_ref.fixed
src/tools/clippy/tests/ui/borrow_deref_ref.rs
src/tools/clippy/tests/ui/borrow_deref_ref.stderr
src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.rs
src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr
src/tools/clippy/tests/ui/cast_abs_to_unsigned.fixed
src/tools/clippy/tests/ui/cast_abs_to_unsigned.rs
src/tools/clippy/tests/ui/cast_abs_to_unsigned.stderr
src/tools/clippy/tests/ui/collapsible_match2.rs
src/tools/clippy/tests/ui/collapsible_match2.stderr
src/tools/clippy/tests/ui/crashes/ice-8850.rs
src/tools/clippy/tests/ui/crashes/ice-8850.stderr
src/tools/clippy/tests/ui/dbg_macro.stderr
src/tools/clippy/tests/ui/debug_assert_with_mut_call.rs
src/tools/clippy/tests/ui/deref_by_slicing.fixed
src/tools/clippy/tests/ui/deref_by_slicing.rs
src/tools/clippy/tests/ui/deref_by_slicing.stderr
src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed
src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs
src/tools/clippy/tests/ui/doc_link_with_quotes.rs
src/tools/clippy/tests/ui/doc_link_with_quotes.stderr
src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs
src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr
src/tools/clippy/tests/ui/explicit_deref_methods.fixed
src/tools/clippy/tests/ui/explicit_deref_methods.rs
src/tools/clippy/tests/ui/explicit_deref_methods.stderr
src/tools/clippy/tests/ui/forget_ref.rs
src/tools/clippy/tests/ui/forget_ref.stderr
src/tools/clippy/tests/ui/get_first.fixed
src/tools/clippy/tests/ui/get_first.rs
src/tools/clippy/tests/ui/get_first.stderr
src/tools/clippy/tests/ui/get_last_with_len.fixed
src/tools/clippy/tests/ui/get_last_with_len.rs
src/tools/clippy/tests/ui/get_last_with_len.stderr
src/tools/clippy/tests/ui/get_unwrap.fixed
src/tools/clippy/tests/ui/get_unwrap.rs
src/tools/clippy/tests/ui/identity_op.fixed
src/tools/clippy/tests/ui/identity_op.rs
src/tools/clippy/tests/ui/identity_op.stderr
src/tools/clippy/tests/ui/implicit_clone.fixed
src/tools/clippy/tests/ui/implicit_clone.rs
src/tools/clippy/tests/ui/implicit_clone.stderr
src/tools/clippy/tests/ui/issue_2356.fixed
src/tools/clippy/tests/ui/issue_2356.rs
src/tools/clippy/tests/ui/issue_2356.stderr
src/tools/clippy/tests/ui/iter_next_slice.fixed
src/tools/clippy/tests/ui/iter_next_slice.rs
src/tools/clippy/tests/ui/iter_next_slice.stderr
src/tools/clippy/tests/ui/large_enum_variant.rs
src/tools/clippy/tests/ui/large_enum_variant.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_ref_pats.fixed
src/tools/clippy/tests/ui/match_ref_pats.rs
src/tools/clippy/tests/ui/match_ref_pats.stderr
src/tools/clippy/tests/ui/match_str_case_mismatch.fixed
src/tools/clippy/tests/ui/match_str_case_mismatch.rs
src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
src/tools/clippy/tests/ui/mismatching_type_param_order.rs
src/tools/clippy/tests/ui/mismatching_type_param_order.stderr
src/tools/clippy/tests/ui/modulo_one.rs
src/tools/clippy/tests/ui/modulo_one.stderr
src/tools/clippy/tests/ui/needless_late_init.fixed
src/tools/clippy/tests/ui/needless_late_init.rs
src/tools/clippy/tests/ui/needless_late_init.stderr
src/tools/clippy/tests/ui/needless_lifetimes.rs
src/tools/clippy/tests/ui/needless_lifetimes.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/needless_return.stderr
src/tools/clippy/tests/ui/no_effect_replace.rs
src/tools/clippy/tests/ui/no_effect_replace.stderr
src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
src/tools/clippy/tests/ui/range_contains.fixed
src/tools/clippy/tests/ui/range_contains.rs
src/tools/clippy/tests/ui/range_contains.stderr
src/tools/clippy/tests/ui/rc_buffer.fixed
src/tools/clippy/tests/ui/rc_buffer.rs
src/tools/clippy/tests/ui/rc_buffer.stderr
src/tools/clippy/tests/ui/rc_buffer_arc.fixed
src/tools/clippy/tests/ui/rc_buffer_arc.rs
src/tools/clippy/tests/ui/rc_buffer_arc.stderr
src/tools/clippy/tests/ui/rc_clone_in_vec_init/arc.stderr
src/tools/clippy/tests/ui/rc_clone_in_vec_init/rc.stderr
src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.rs
src/tools/clippy/tests/ui/rc_clone_in_vec_init/weak.stderr
src/tools/clippy/tests/ui/recursive_format_impl.rs
src/tools/clippy/tests/ui/recursive_format_impl.stderr
src/tools/clippy/tests/ui/shadow.rs
src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
src/tools/clippy/tests/ui/suspicious_operation_groupings.fixed
src/tools/clippy/tests/ui/suspicious_operation_groupings.rs
src/tools/clippy/tests/ui/suspicious_operation_groupings.stderr
src/tools/clippy/tests/ui/swap_ptr_to_ref.fixed
src/tools/clippy/tests/ui/swap_ptr_to_ref.rs
src/tools/clippy/tests/ui/swap_ptr_to_ref.stderr
src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.rs
src/tools/clippy/tests/ui/swap_ptr_to_ref_unfixable.stderr
src/tools/clippy/tests/ui/transmute.rs
src/tools/clippy/tests/ui/transmute.stderr
src/tools/clippy/tests/ui/transmute_undefined_repr.rs
src/tools/clippy/tests/ui/unicode.fixed
src/tools/clippy/tests/ui/unicode.rs
src/tools/clippy/tests/ui/unicode.stderr
src/tools/clippy/tests/ui/unit_arg_empty_blocks.fixed
src/tools/clippy/tests/ui/unit_arg_empty_blocks.rs
src/tools/clippy/tests/ui/unit_arg_empty_blocks.stderr
src/tools/clippy/tests/ui/unnecessary_cast.fixed
src/tools/clippy/tests/ui/unnecessary_cast.rs
src/tools/clippy/tests/ui/unnecessary_cast.stderr
src/tools/clippy/tests/ui/unused_rounding.fixed
src/tools/clippy/tests/ui/unused_rounding.rs
src/tools/clippy/tests/ui/unused_rounding.stderr
src/tools/clippy/tests/ui/use_self.fixed
src/tools/clippy/tests/ui/use_self.rs
src/tools/clippy/tests/ui/use_self.stderr
src/tools/clippy/util/gh-pages/script.js

index 10ef00fcbafa5421d6de1e94d77975e0e7615a6d,0000000000000000000000000000000000000000..6ef338b819d4dfac1cae7808eaf237a238ed1482
mode 100644,000000..100644
--- /dev/null
@@@ -1,3865 -1,0 +1,3873 @@@
- [`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](doc/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
 +[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
 +
 +## Rust 1.61
 +
 +Current stable, 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 excapes 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)
 +* [`blacklisted_name`]: Now allows blacklisted 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/doc/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)
 +* [`blacklisted_name`]: 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 `{unnnecessary,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/doc/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
 +[`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
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
++[`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
 +[`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
 +[`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_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_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
 +[`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
 +[`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_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_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_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_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_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_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
 +[`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_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_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_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_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`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_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_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
 +[`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
 +[`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_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
 +[`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
 +[`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
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`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
 +[`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
 +[`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_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
 +[`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
 +[`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_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
++[`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
 +[`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
 +[`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_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_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_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 373e720b0d5c07024e2d253cb8191bfcb54c01eb,0000000000000000000000000000000000000000..d23d681df00c1d9ac45a125ae406a8f88008cedb
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,66 @@@
- compiletest_rs = { version = "0.7.1", features = ["tmp"] }
 +[package]
 +name = "clippy"
 +version = "0.1.63"
 +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 = { path = "rustc_tools_util" }
 +tempfile = { version = "3.2", optional = true }
 +termize = "0.1"
 +
 +[dev-dependencies]
- parking_lot = "0.11.2"
++compiletest_rs = { version = "0.8", features = ["tmp"] }
 +tester = "0.9"
 +regex = "1.5"
 +# 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
++clap = { version = "3.1", features = ["derive"] }
 +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 = { version = "0.2", path = "rustc_tools_util" }
 +
 +[features]
 +deny-warnings = ["clippy_lints/deny-warnings"]
 +integration = ["tempfile"]
 +internal = ["clippy_lints/internal"]
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index 2cfbcea5034e653beb818b1be5dc89b94b856d2c,0000000000000000000000000000000000000000..b0d470a2124d86be497b1e46b7a959de4a0ef69b
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,21 @@@
- clap = "2.33"
 +[package]
 +name = "clippy_dev"
 +version = "0.0.1"
 +edition = "2021"
 +
 +[dependencies]
 +aho-corasick = "0.7"
++clap = "3.1"
 +indoc = "1.0"
 +itertools = "0.10.1"
 +opener = "0.5"
 +shell-escape = "0.1"
 +tempfile = "3.2"
 +walkdir = "2.3"
 +
 +[features]
 +deny-warnings = []
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index d5cd7ca96c0c0421cd370abda2ecb5177e03aa97,0000000000000000000000000000000000000000..ee535b1d3be8bb17d2a2283539bdc8e102a9c9f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,323 -1,0 +1,307 @@@
- use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
 +#![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)]
 +
-         ("bless", Some(matches)) => {
++use clap::{Arg, ArgMatches, Command};
 +use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
 +use indoc::indoc;
 +fn main() {
 +    let matches = get_clap_config();
 +
 +    match matches.subcommand() {
-         ("fmt", Some(matches)) => {
++        Some(("bless", matches)) => {
 +            bless::bless(matches.is_present("ignore-timestamp"));
 +        },
-         ("update_lints", Some(matches)) => {
++        Some(("fmt", matches)) => {
 +            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
 +        },
-         ("new_lint", Some(matches)) => {
++        Some(("update_lints", matches)) => {
 +            if matches.is_present("print-only") {
 +                update_lints::print_lints();
 +            } else if matches.is_present("check") {
 +                update_lints::update(update_lints::UpdateMode::Check);
 +            } else {
 +                update_lints::update(update_lints::UpdateMode::Change);
 +            }
 +        },
-         ("setup", Some(sub_command)) => match sub_command.subcommand() {
-             ("intellij", Some(matches)) => {
++        Some(("new_lint", matches)) => {
 +            match new_lint::create(
 +                matches.value_of("pass"),
 +                matches.value_of("name"),
 +                matches.value_of("category"),
 +                matches.is_present("msrv"),
 +            ) {
 +                Ok(_) => update_lints::update(update_lints::UpdateMode::Change),
 +                Err(e) => eprintln!("Unable to create lint: {}", e),
 +            }
 +        },
-             ("git-hook", Some(matches)) => {
++        Some(("setup", sub_command)) => match sub_command.subcommand() {
++            Some(("intellij", matches)) => {
 +                if matches.is_present("remove") {
 +                    setup::intellij::remove_rustc_src();
 +                } else {
 +                    setup::intellij::setup_rustc_src(
 +                        matches
 +                            .value_of("rustc-repo-path")
 +                            .expect("this field is mandatory and therefore always valid"),
 +                    );
 +                }
 +            },
-             ("vscode-tasks", Some(matches)) => {
++            Some(("git-hook", matches)) => {
 +                if matches.is_present("remove") {
 +                    setup::git_hook::remove_hook();
 +                } else {
 +                    setup::git_hook::install_hook(matches.is_present("force-override"));
 +                }
 +            },
-         ("remove", Some(sub_command)) => match sub_command.subcommand() {
-             ("git-hook", Some(_)) => setup::git_hook::remove_hook(),
-             ("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
-             ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
++            Some(("vscode-tasks", matches)) => {
 +                if matches.is_present("remove") {
 +                    setup::vscode::remove_tasks();
 +                } else {
 +                    setup::vscode::install_tasks(matches.is_present("force-override"));
 +                }
 +            },
 +            _ => {},
 +        },
-         ("serve", Some(matches)) => {
++        Some(("remove", sub_command)) => match sub_command.subcommand() {
++            Some(("git-hook", _)) => setup::git_hook::remove_hook(),
++            Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
++            Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
 +            _ => {},
 +        },
-         ("lint", Some(matches)) => {
++        Some(("serve", matches)) => {
 +            let port = matches.value_of("port").unwrap().parse().unwrap();
 +            let lint = matches.value_of("lint");
 +            serve::run(port, lint);
 +        },
-         ("rename_lint", Some(matches)) => {
++        Some(("lint", matches)) => {
 +            let path = matches.value_of("path").unwrap();
 +            let args = matches.values_of("args").into_iter().flatten();
 +            lint::run(path, args);
 +        },
- fn get_clap_config<'a>() -> ArgMatches<'a> {
-     App::new("Clippy developer tooling")
-         .setting(AppSettings::ArgRequiredElseHelp)
++        Some(("rename_lint", matches)) => {
 +            let old_name = matches.value_of("old_name").unwrap();
 +            let new_name = matches.value_of("new_name").unwrap_or(old_name);
 +            let uplift = matches.is_present("uplift");
 +            update_lints::rename(old_name, new_name, uplift);
 +        },
 +        _ => {},
 +    }
 +}
 +
-             SubCommand::with_name("bless")
-                 .about("bless the test output changes")
-                 .arg(
-                     Arg::with_name("ignore-timestamp")
-                         .long("ignore-timestamp")
-                         .help("Include files updated before clippy was built"),
-                 ),
++fn get_clap_config() -> ArgMatches {
++    Command::new("Clippy developer tooling")
++        .arg_required_else_help(true)
 +        .subcommand(
-             SubCommand::with_name("fmt")
++            Command::new("bless").about("bless the test output changes").arg(
++                Arg::new("ignore-timestamp")
++                    .long("ignore-timestamp")
++                    .help("Include files updated before clippy was built"),
++            ),
 +        )
 +        .subcommand(
-                 .arg(
-                     Arg::with_name("check")
-                         .long("check")
-                         .help("Use the rustfmt --check option"),
-                 )
-                 .arg(
-                     Arg::with_name("verbose")
-                         .short("v")
-                         .long("verbose")
-                         .help("Echo commands run"),
-                 ),
++            Command::new("fmt")
 +                .about("Run rustfmt on all projects and tests")
-             SubCommand::with_name("update_lints")
++                .arg(Arg::new("check").long("check").help("Use the rustfmt --check option"))
++                .arg(Arg::new("verbose").short('v').long("verbose").help("Echo commands run")),
 +        )
 +        .subcommand(
-                 .arg(Arg::with_name("print-only").long("print-only").help(
++            Command::new("update_lints")
 +                .about("Updates lint registration and information from the source code")
 +                .long_about(
 +                    "Makes sure that:\n \
 +                 * the lint count in README.md is correct\n \
 +                 * the changelog contains markdown link references at the bottom\n \
 +                 * all lint groups include the correct lints\n \
 +                 * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
 +                 * all lints are registered in the lint store",
 +                )
-                     Arg::with_name("check")
++                .arg(Arg::new("print-only").long("print-only").help(
 +                    "Print a table of lints to STDOUT. \
 +                 This does not include deprecated and internal lints. \
 +                 (Does not modify any files)",
 +                ))
 +                .arg(
-             SubCommand::with_name("new_lint")
++                    Arg::new("check")
 +                        .long("check")
 +                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
 +                ),
 +        )
 +        .subcommand(
-                     Arg::with_name("pass")
-                         .short("p")
++            Command::new("new_lint")
 +                .about("Create new lint and run `cargo dev update_lints`")
 +                .arg(
-                     Arg::with_name("name")
-                         .short("n")
++                    Arg::new("pass")
++                        .short('p')
 +                        .long("pass")
 +                        .help("Specify whether the lint runs during the early or late pass")
 +                        .takes_value(true)
 +                        .possible_values(&["early", "late"])
 +                        .required(true),
 +                )
 +                .arg(
-                     Arg::with_name("category")
-                         .short("c")
++                    Arg::new("name")
++                        .short('n')
 +                        .long("name")
 +                        .help("Name of the new lint in snake case, ex: fn_too_long")
 +                        .takes_value(true)
 +                        .required(true),
 +                )
 +                .arg(
-                 .arg(
-                     Arg::with_name("msrv")
-                         .long("msrv")
-                         .help("Add MSRV config code to the lint"),
-                 ),
++                    Arg::new("category")
++                        .short('c')
 +                        .long("category")
 +                        .help("What category the lint belongs to")
 +                        .default_value("nursery")
 +                        .possible_values(&[
 +                            "style",
 +                            "correctness",
 +                            "suspicious",
 +                            "complexity",
 +                            "perf",
 +                            "pedantic",
 +                            "restriction",
 +                            "cargo",
 +                            "nursery",
 +                            "internal",
 +                            "internal_warn",
 +                        ])
 +                        .takes_value(true),
 +                )
-             SubCommand::with_name("setup")
++                .arg(Arg::new("msrv").long("msrv").help("Add MSRV config code to the lint")),
 +        )
 +        .subcommand(
-                 .setting(AppSettings::ArgRequiredElseHelp)
++            Command::new("setup")
 +                .about("Support for setting up your personal development environment")
-                     SubCommand::with_name("intellij")
++                .arg_required_else_help(true)
 +                .subcommand(
-                             Arg::with_name("remove")
++                    Command::new("intellij")
 +                        .about("Alter dependencies so Intellij Rust can find rustc internals")
 +                        .arg(
-                             Arg::with_name("rustc-repo-path")
++                            Arg::new("remove")
 +                                .long("remove")
 +                                .help("Remove the dependencies added with 'cargo dev setup intellij'")
 +                                .required(false),
 +                        )
 +                        .arg(
-                                 .short("r")
++                            Arg::new("rustc-repo-path")
 +                                .long("repo-path")
-                     SubCommand::with_name("git-hook")
++                                .short('r')
 +                                .help("The path to a rustc repo that will be used for setting the dependencies")
 +                                .takes_value(true)
 +                                .value_name("path")
 +                                .conflicts_with("remove")
 +                                .required(true),
 +                        ),
 +                )
 +                .subcommand(
-                             Arg::with_name("remove")
++                    Command::new("git-hook")
 +                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
 +                        .arg(
-                             Arg::with_name("force-override")
++                            Arg::new("remove")
 +                                .long("remove")
 +                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'")
 +                                .required(false),
 +                        )
 +                        .arg(
-                                 .short("f")
++                            Arg::new("force-override")
 +                                .long("force-override")
-                     SubCommand::with_name("vscode-tasks")
++                                .short('f')
 +                                .help("Forces the override of an existing git pre-commit hook")
 +                                .required(false),
 +                        ),
 +                )
 +                .subcommand(
-                             Arg::with_name("remove")
++                    Command::new("vscode-tasks")
 +                        .about("Add several tasks to vscode for formatting, validation and testing")
 +                        .arg(
-                             Arg::with_name("force-override")
++                            Arg::new("remove")
 +                                .long("remove")
 +                                .help("Remove the tasks added with 'cargo dev setup vscode-tasks'")
 +                                .required(false),
 +                        )
 +                        .arg(
-                                 .short("f")
++                            Arg::new("force-override")
 +                                .long("force-override")
-             SubCommand::with_name("remove")
++                                .short('f')
 +                                .help("Forces the override of existing vscode tasks")
 +                                .required(false),
 +                        ),
 +                ),
 +        )
 +        .subcommand(
-                 .setting(AppSettings::ArgRequiredElseHelp)
-                 .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
-                 .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
++            Command::new("remove")
 +                .about("Support for undoing changes done by the setup command")
-                     SubCommand::with_name("intellij")
-                         .about("Removes rustc source paths added via `cargo dev setup intellij`"),
++                .arg_required_else_help(true)
++                .subcommand(Command::new("git-hook").about("Remove any existing pre-commit git hook"))
++                .subcommand(Command::new("vscode-tasks").about("Remove any existing vscode tasks"))
 +                .subcommand(
-             SubCommand::with_name("serve")
++                    Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
 +                ),
 +        )
 +        .subcommand(
-                     Arg::with_name("port")
++            Command::new("serve")
 +                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
 +                .arg(
-                         .short("p")
++                    Arg::new("port")
 +                        .long("port")
-                 .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
++                        .short('p')
 +                        .help("Local port for the http server")
 +                        .default_value("8000")
 +                        .validator_os(serve::validate_port),
 +                )
-             SubCommand::with_name("lint")
++                .arg(Arg::new("lint").help("Which lint's page to load initially (optional)")),
 +        )
 +        .subcommand(
-                     Arg::with_name("path")
++            Command::new("lint")
 +                .about("Manually run clippy on a file or package")
 +                .after_help(indoc! {"
 +                    EXAMPLES
 +                        Lint a single file:
 +                            cargo dev lint tests/ui/attrs.rs
 +
 +                        Lint a package directory:
 +                            cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
 +                            cargo dev lint ~/my-project
 +
 +                        Run rustfix:
 +                            cargo dev lint ~/my-project -- --fix
 +
 +                        Set lint levels:
 +                            cargo dev lint file.rs -- -W clippy::pedantic
 +                            cargo dev lint ~/my-project -- -- -W clippy::pedantic
 +                "})
 +                .arg(
-                     Arg::with_name("args")
-                         .multiple(true)
++                    Arg::new("path")
 +                        .required(true)
 +                        .help("The path to a file or package directory to lint"),
 +                )
 +                .arg(
-             SubCommand::with_name("rename_lint")
++                    Arg::new("args")
++                        .multiple_occurrences(true)
 +                        .help("Pass extra arguments to cargo/clippy-driver"),
 +                ),
 +        )
 +        .subcommand(
-                     Arg::with_name("old_name")
++            Command::new("rename_lint")
 +                .about("Renames the given lint")
 +                .arg(
-                     Arg::with_name("new_name")
++                    Arg::new("old_name")
 +                        .index(1)
 +                        .required(true)
 +                        .help("The name of the lint to rename"),
 +                )
 +                .arg(
-                         .required_unless("uplift")
++                    Arg::new("new_name")
 +                        .index(2)
-                     Arg::with_name("uplift")
++                        .required_unless_present("uplift")
 +                        .help("The new name of the lint"),
 +                )
 +                .arg(
++                    Arg::new("uplift")
 +                        .long("uplift")
 +                        .help("This lint will be uplifted into rustc"),
 +                ),
 +        )
 +        .get_matches()
 +}
index b36e2a28ee45450a589c1c93035c0f77b2d1edb7,0000000000000000000000000000000000000000..d55b1a354d003cba512928e27b0b4e4310b7209b
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,65 @@@
- use std::ffi::{OsStr, OsString};
++use std::ffi::OsStr;
++use std::num::ParseIntError;
 +use std::path::Path;
 +use std::process::Command;
 +use std::thread;
 +use std::time::{Duration, SystemTime};
 +
 +/// # Panics
 +///
 +/// Panics if the python commands could not be spawned
 +pub fn run(port: u16, lint: Option<&str>) -> ! {
 +    let mut url = Some(match lint {
 +        None => format!("http://localhost:{}", port),
 +        Some(lint) => format!("http://localhost:{}/#{}", port, lint),
 +    });
 +
 +    loop {
 +        if mtime("util/gh-pages/lints.json") < mtime("clippy_lints/src") {
 +            Command::new("cargo")
 +                .arg("collect-metadata")
 +                .spawn()
 +                .unwrap()
 +                .wait()
 +                .unwrap();
 +        }
 +        if let Some(url) = url.take() {
 +            thread::spawn(move || {
 +                Command::new("python3")
 +                    .arg("-m")
 +                    .arg("http.server")
 +                    .arg(port.to_string())
 +                    .current_dir("util/gh-pages")
 +                    .spawn()
 +                    .unwrap();
 +                // Give some time for python to start
 +                thread::sleep(Duration::from_millis(500));
 +                // Launch browser after first export.py has completed and http.server is up
 +                let _result = opener::open(url);
 +            });
 +        }
 +        thread::sleep(Duration::from_millis(1000));
 +    }
 +}
 +
 +fn mtime(path: impl AsRef<Path>) -> SystemTime {
 +    let path = path.as_ref();
 +    if path.is_dir() {
 +        path.read_dir()
 +            .into_iter()
 +            .flatten()
 +            .flatten()
 +            .map(|entry| mtime(&entry.path()))
 +            .max()
 +            .unwrap_or(SystemTime::UNIX_EPOCH)
 +    } else {
 +        path.metadata()
 +            .and_then(|metadata| metadata.modified())
 +            .unwrap_or(SystemTime::UNIX_EPOCH)
 +    }
 +}
 +
 +#[allow(clippy::missing_errors_doc)]
- pub fn validate_port(arg: &OsStr) -> Result<(), OsString> {
-     match arg.to_string_lossy().parse::<u16>() {
-         Ok(_port) => Ok(()),
-         Err(err) => Err(OsString::from(err.to_string())),
-     }
++pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> {
++    arg.to_string_lossy().parse::<u16>().map(|_| ())
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b364a370efab5691f6d458ebe5db811de22de196
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::source::{trim_span, walk_span_to_context};
++use clippy_utils::{meets_msrv, msrvs};
++use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
++use rustc_errors::Applicability;
++use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
++use rustc_semver::RustcVersion;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::Span;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
++    /// don't because they're a half open range.
++    ///
++    /// ### Why is this bad?
++    /// This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let _ = 'a'..'z';
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let _ = 'a'..='z';
++    /// ```
++    #[clippy::version = "1.63.0"]
++    pub ALMOST_COMPLETE_LETTER_RANGE,
++    suspicious,
++    "almost complete letter range"
++}
++impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]);
++
++pub struct AlmostCompleteLetterRange {
++    msrv: Option<RustcVersion>,
++}
++impl AlmostCompleteLetterRange {
++    pub fn new(msrv: Option<RustcVersion>) -> Self {
++        Self { msrv }
++    }
++}
++impl EarlyLintPass for AlmostCompleteLetterRange {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
++        if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
++            let ctxt = e.span.ctxt();
++            let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
++                && let Some(end) = walk_span_to_context(end.span, ctxt)
++                && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE)
++            {
++                Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
++            } else {
++                None
++            };
++            check_range(cx, e.span, start, end, sugg);
++        }
++    }
++
++    fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
++        if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
++            && matches!(kind.node, RangeEnd::Excluded)
++        {
++            let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) {
++                "..="
++            } else {
++                "..."
++            };
++            check_range(cx, p.span, start, end, Some((kind.span, sugg)));
++        }
++    }
++
++    extract_msrv_attr!(EarlyContext);
++}
++
++fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
++    if let ExprKind::Lit(start_lit) = &start.peel_parens().kind
++        && let ExprKind::Lit(end_lit) = &end.peel_parens().kind
++        && matches!(
++            (&start_lit.kind, &end_lit.kind),
++            (LitKind::Byte(b'a') | LitKind::Char('a'), LitKind::Byte(b'z') | LitKind::Char('z'))
++            | (LitKind::Byte(b'A') | LitKind::Char('A'), LitKind::Byte(b'Z') | LitKind::Char('Z'))
++        )
++    {
++        span_lint_and_then(
++            cx,
++            ALMOST_COMPLETE_LETTER_RANGE,
++            span,
++            "almost complete ascii letter range",
++            |diag| {
++                if let Some((span, sugg)) = sugg {
++                    diag.span_suggestion(
++                        span,
++                        "use an inclusive range",
++                        sugg.to_owned(),
++                        Applicability::MaybeIncorrect,
++                    );
++                }
++            }
++        );
++    }
++}
index da1b646f4777a006ee434094d0b9c0e113782508,0000000000000000000000000000000000000000..159f3b0cd014a5ce66d7994244750d5d9df4706f
mode 100644,000000..100644
--- /dev/null
@@@ -1,132 -1,0 +1,132 @@@
-     /// Use predefined constants instead:
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::{meets_msrv, msrvs};
 +use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol;
 +use std::f64::consts as f64;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for floating point literals that approximate
 +    /// constants which are defined in
 +    /// [`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
 +    /// or
 +    /// [`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
 +    /// respectively, suggesting to use the predefined constant.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually, the definition in the standard library is more
 +    /// precise than what people come up with. If you find that your definition is
 +    /// actually more precise, please [file a Rust
 +    /// issue](https://github.com/rust-lang/rust/issues).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 3.14;
 +    /// let y = 1_f64 / x;
 +    /// ```
++    /// Use instead:
 +    /// ```rust
 +    /// let x = std::f32::consts::PI;
 +    /// let y = std::f64::consts::FRAC_1_PI;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub APPROX_CONSTANT,
 +    correctness,
 +    "the approximate of a known float constant (in `std::fXX::consts`)"
 +}
 +
 +// Tuples are of the form (constant, name, min_digits, msrv)
 +const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
 +    (f64::E, "E", 4, None),
 +    (f64::FRAC_1_PI, "FRAC_1_PI", 4, None),
 +    (f64::FRAC_1_SQRT_2, "FRAC_1_SQRT_2", 5, None),
 +    (f64::FRAC_2_PI, "FRAC_2_PI", 5, None),
 +    (f64::FRAC_2_SQRT_PI, "FRAC_2_SQRT_PI", 5, None),
 +    (f64::FRAC_PI_2, "FRAC_PI_2", 5, None),
 +    (f64::FRAC_PI_3, "FRAC_PI_3", 5, None),
 +    (f64::FRAC_PI_4, "FRAC_PI_4", 5, None),
 +    (f64::FRAC_PI_6, "FRAC_PI_6", 5, None),
 +    (f64::FRAC_PI_8, "FRAC_PI_8", 5, None),
 +    (f64::LN_2, "LN_2", 5, None),
 +    (f64::LN_10, "LN_10", 5, None),
 +    (f64::LOG2_10, "LOG2_10", 5, Some(msrvs::LOG2_10)),
 +    (f64::LOG2_E, "LOG2_E", 5, None),
 +    (f64::LOG10_2, "LOG10_2", 5, Some(msrvs::LOG10_2)),
 +    (f64::LOG10_E, "LOG10_E", 5, None),
 +    (f64::PI, "PI", 3, None),
 +    (f64::SQRT_2, "SQRT_2", 5, None),
 +    (f64::TAU, "TAU", 3, Some(msrvs::TAU)),
 +];
 +
 +pub struct ApproxConstant {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ApproxConstant {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +
 +    fn check_lit(&self, cx: &LateContext<'_>, lit: &LitKind, e: &Expr<'_>) {
 +        match *lit {
 +            LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty {
 +                FloatTy::F32 => self.check_known_consts(cx, e, s, "f32"),
 +                FloatTy::F64 => self.check_known_consts(cx, e, s, "f64"),
 +            },
 +            LitKind::Float(s, LitFloatType::Unsuffixed) => self.check_known_consts(cx, e, s, "f{32, 64}"),
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_known_consts(&self, cx: &LateContext<'_>, e: &Expr<'_>, s: symbol::Symbol, module: &str) {
 +        let s = s.as_str();
 +        if s.parse::<f64>().is_ok() {
 +            for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
 +                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) {
 +                    span_lint_and_help(
 +                        cx,
 +                        APPROX_CONSTANT,
 +                        e.span,
 +                        &format!("approximate value of `{}::consts::{}` found", module, &name),
 +                        None,
 +                        "consider using the constant directly",
 +                    );
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ApproxConstant {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Lit(lit) = &e.kind {
 +            self.check_lit(cx, &lit.node, e);
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Returns `false` if the number of significant figures in `value` are
 +/// less than `min_digits`; otherwise, returns true if `value` is equal
 +/// to `constant`, rounded to the number of digits present in `value`.
 +#[must_use]
 +fn is_approx_const(constant: f64, value: &str, min_digits: usize) -> bool {
 +    if value.len() <= min_digits {
 +        false
 +    } else if constant.to_string().starts_with(value) {
 +        // The value is a truncated constant
 +        true
 +    } else {
 +        let round_const = format!("{:.*}", value.len() - 2, constant);
 +        value == round_const
 +    }
 +}
index 88b91d589074d85e300c79979bab0d89269e1b6a,0000000000000000000000000000000000000000..6e5c8f445818ebfb54bb5c45bbbe87b9a57b9cf9
mode 100644,000000..100644
--- /dev/null
@@@ -1,66 -1,0 +1,65 @@@
-     /// Usually better represents the semantics you expect:
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_ast::ast::{Expr, ExprKind};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `as` conversions.
 +    ///
 +    /// Note that this lint is specialized in linting *every single* use of `as`
 +    /// regardless of whether good alternatives exist or not.
 +    /// If you want more precise lints for `as`, please consider using these separate lints:
 +    /// `unnecessary_cast`, `cast_lossless/possible_truncation/possible_wrap/precision_loss/sign_loss`,
 +    /// `fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
 +    /// There is a good explanation the reason why this lint should work in this way and how it is useful
 +    /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
 +    ///
 +    /// ### Why is this bad?
 +    /// `as` conversions will perform many kinds of
 +    /// conversions, including silently lossy conversions and dangerous coercions.
 +    /// There are cases when it makes sense to use `as`, so the lint is
 +    /// Allow by default.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let a: u32;
 +    /// ...
 +    /// f(a as u16);
 +    /// ```
 +    ///
-     /// ```
-     /// or
-     /// ```rust,ignore
++    /// Use instead:
 +    /// ```rust,ignore
 +    /// f(a.try_into()?);
-     ///
++    ///
++    /// // or
++    ///
 +    /// f(a.try_into().expect("Unexpected u16 overflow in f"));
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub AS_CONVERSIONS,
 +    restriction,
 +    "using a potentially dangerous silent `as` conversion"
 +}
 +
 +declare_lint_pass!(AsConversions => [AS_CONVERSIONS]);
 +
 +impl EarlyLintPass for AsConversions {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let ExprKind::Cast(_, _) = expr.kind {
 +            span_lint_and_help(
 +                cx,
 +                AS_CONVERSIONS,
 +                expr.span,
 +                "using a potentially dangerous silent `as` conversion",
 +                None,
 +                "consider using a safe wrapper for this conversion",
 +            );
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..464be4218dd4d519ec7ab3dfb8880f0b5af7ed04
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,74 @@@
++use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, TyKind};
++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
++    /// 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 du 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_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
++
++impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
++        if in_external_macro(cx.sess(), expr.span) {
++            return;
++        }
++
++        if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
++
++            let ty_resolved = cx.typeck_results().expr_ty(expr);
++            if let ty::Error(_) = ty_resolved.kind() {
++                span_lint_and_help(
++                    cx,
++                AS_UNDERSCORE,
++                expr.span,
++                "using `as _` conversion",
++                None,
++                "consider giving the type explicitly",
++                );
++            } else {
++            span_lint_and_then(
++                cx,
++                AS_UNDERSCORE,
++                expr.span,
++                "using `as _` conversion",
++                |diag| {
++                    diag.span_suggestion(
++                        ty.span,
++                        "consider giving the type explicitly",
++                        format!("{}", ty_resolved),
++                        Applicability::MachineApplicable,
++                    );
++            }
++            );
++        }
++        }
++    }
++}
index 4c2d3366483af094b5d5aa539ae5042aabf1b438,0000000000000000000000000000000000000000..f81da2d42233380e9bd5a633b8b13237dfcac68a
mode 100644,000000..100644
--- /dev/null
@@@ -1,229 -1,0 +1,235 @@@
-     /// // Bad
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::{binop_traits, sugg};
 +use clippy_utils::{eq_expr_value, trait_ref_of_method};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `a = a op b` or `a = b commutative_op a`
 +    /// patterns.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written as the shorter `a op= b`.
 +    ///
 +    /// ### Known problems
 +    /// While forbidden by the spec, `OpAssign` traits may have
 +    /// implementations that differ from the regular `Op` impl.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 0;
 +    /// // ...
-     /// // Good
++    ///
 +    /// a = a + b;
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// let mut a = 5;
++    /// let b = 0;
++    /// // ...
 +    ///
 +    /// a += b;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ASSIGN_OP_PATTERN,
 +    style,
 +    "assigning the result of an operation on a variable to that same variable"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `a op= a op b` or `a op= b op a` patterns.
 +    ///
 +    /// ### Why is this bad?
 +    /// Most likely these are bugs where one meant to write `a
 +    /// op= b`.
 +    ///
 +    /// ### Known problems
 +    /// Clippy cannot know for sure if `a op= a op b` should have
 +    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
 +    /// If `a op= a op b` is really the correct behavior it should be
 +    /// written as `a = a op a op b` as it's less confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = 5;
 +    /// let b = 2;
 +    /// // ...
 +    /// a += a + b;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MISREFACTORED_ASSIGN_OP,
 +    suspicious,
 +    "having a variable on both sides of an assign op"
 +}
 +
 +declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for AssignOps {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        match &expr.kind {
 +            hir::ExprKind::AssignOp(op, lhs, rhs) => {
 +                if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
 +                    if op.node != binop.node {
 +                        return;
 +                    }
 +                    // lhs op= l op r
 +                    if eq_expr_value(cx, lhs, l) {
 +                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
 +                    }
 +                    // lhs op= l commutative_op r
 +                    if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
 +                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
 +                    }
 +                }
 +            },
 +            hir::ExprKind::Assign(assignee, e, _) => {
 +                if let hir::ExprKind::Binary(op, l, r) = &e.kind {
 +                    let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
 +                        let ty = cx.typeck_results().expr_ty(assignee);
 +                        let rty = cx.typeck_results().expr_ty(rhs);
 +                        if_chain! {
 +                            if let Some((_, lang_item)) = binop_traits(op.node);
 +                            if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
 +                            let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
 +                            if trait_ref_of_method(cx, parent_fn)
 +                                .map_or(true, |t| t.path.res.def_id() != trait_id);
 +                            if implements_trait(cx, ty, trait_id, &[rty.into()]);
 +                            then {
 +                                span_lint_and_then(
 +                                    cx,
 +                                    ASSIGN_OP_PATTERN,
 +                                    expr.span,
 +                                    "manual implementation of an assign operation",
 +                                    |diag| {
 +                                        if let (Some(snip_a), Some(snip_r)) =
 +                                            (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
 +                                        {
 +                                            diag.span_suggestion(
 +                                                expr.span,
 +                                                "replace it with",
 +                                                format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
 +                                                Applicability::MachineApplicable,
 +                                            );
 +                                        }
 +                                    },
 +                                );
 +                            }
 +                        }
 +                    };
 +
 +                    let mut visitor = ExprVisitor {
 +                        assignee,
 +                        counter: 0,
 +                        cx,
 +                    };
 +
 +                    walk_expr(&mut visitor, e);
 +
 +                    if visitor.counter == 1 {
 +                        // a = a op b
 +                        if eq_expr_value(cx, assignee, l) {
 +                            lint(assignee, r);
 +                        }
 +                        // a = b commutative_op a
 +                        // Limited to primitive type as these ops are know to be commutative
 +                        if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
 +                            match op.node {
 +                                hir::BinOpKind::Add
 +                                | hir::BinOpKind::Mul
 +                                | hir::BinOpKind::And
 +                                | hir::BinOpKind::Or
 +                                | hir::BinOpKind::BitXor
 +                                | hir::BinOpKind::BitAnd
 +                                | hir::BinOpKind::BitOr => {
 +                                    lint(assignee, l);
 +                                },
 +                                _ => {},
 +                            }
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn lint_misrefactored_assign_op(
 +    cx: &LateContext<'_>,
 +    expr: &hir::Expr<'_>,
 +    op: hir::BinOp,
 +    rhs: &hir::Expr<'_>,
 +    assignee: &hir::Expr<'_>,
 +    rhs_other: &hir::Expr<'_>,
 +) {
 +    span_lint_and_then(
 +        cx,
 +        MISREFACTORED_ASSIGN_OP,
 +        expr.span,
 +        "variable appears on both sides of an assignment operation",
 +        |diag| {
 +            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
 +                let a = &sugg::Sugg::hir(cx, assignee, "..");
 +                let r = &sugg::Sugg::hir(cx, rhs, "..");
 +                let long = format!("{} = {}", snip_a, sugg::make_binop(op.node.into(), a, r));
 +                diag.span_suggestion(
 +                    expr.span,
 +                    &format!(
 +                        "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
 +                        snip_a,
 +                        snip_a,
 +                        op.node.as_str(),
 +                        snip_r,
 +                        long
 +                    ),
 +                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
 +                    Applicability::MaybeIncorrect,
 +                );
 +                diag.span_suggestion(
 +                    expr.span,
 +                    "or",
 +                    long,
 +                    Applicability::MaybeIncorrect, // snippet
 +                );
 +            }
 +        },
 +    );
 +}
 +
 +#[must_use]
 +fn is_commutative(op: hir::BinOpKind) -> bool {
 +    use rustc_hir::BinOpKind::{
 +        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
 +    };
 +    match op {
 +        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
 +        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
 +    }
 +}
 +
 +struct ExprVisitor<'a, 'tcx> {
 +    assignee: &'a hir::Expr<'a>,
 +    counter: u8,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        if eq_expr_value(self.cx, self.assignee, expr) {
 +            self.counter += 1;
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 3de91f3d24a9147ef7cbdfc89f05c020cdfac5c3,0000000000000000000000000000000000000000..770cb6a3d7b8baf0dab02cd28b97115387a314d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,713 -1,0 +1,728 @@@
-     /// // Bad
 +//! 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_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +use rustc_span::symbol::Symbol;
 +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 `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
 +    /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
 +    /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
 +    /// `extern crate` items with a `#[macro_use]` attribute.
 +    ///
 +    /// ### Why is this bad?
 +    /// Lint attributes have no effect on crate imports. Most
 +    /// likely a `!` was forgotten.
 +    ///
 +    /// ### Example
 +    /// ```ignore
-     /// // Ok
 +    /// #[deny(dead_code)]
 +    /// extern crate foo;
 +    /// #[forbid(dead_code)]
 +    /// use foo::bar;
++    /// ```
 +    ///
-     /// // Bad
-     /// #[allow(dead_code)]
-     ///
-     /// fn not_quite_good_code() { }
++    /// 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() { }
 +    ///
-     /// Bad:
++    /// // 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
-     /// Good:
 +    /// ```rust
 +    /// #![deny(clippy::restriction)]
 +    /// ```
 +    ///
-     /// Bad:
++    /// 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
-     /// Good:
 +    /// ```rust
 +    /// #[cfg_attr(rustfmt, rustfmt_skip)]
 +    /// fn main() { }
 +    /// ```
 +    ///
-     /// Bad:
++    /// 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
-     /// Good:
 +    /// ```rust
 +    /// #[cfg(linux)]
 +    /// fn conditional() { }
 +    /// ```
 +    ///
-     /// ```
++    /// Use instead:
 +    /// ```rust
++    /// # mod hidden {
 +    /// #[cfg(target_os = "linux")]
 +    /// fn conditional() { }
-     /// Or:
-     /// ```rust
++    /// # }
++    ///
++    /// // or
 +    ///
-     /// Bad:
 +    /// #[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
-     /// Good:
 +    /// ```rust
 +    /// #![feature(lint_reasons)]
 +    ///
 +    /// #![allow(clippy::some_lint)]
 +    /// ```
 +    ///
-     for attr in &item.attrs {
++    /// 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_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",
 +                                                )
 +                                            })
 +                                        {
 +                                            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(),
 +                    "restriction lints are not meant to be all enabled",
 +                    None,
 +                    "try enabling only the lints you really need",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +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;
 +    }
 +
 +    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 `{}`. This is usually a bad idea",
 +                        name
 +                    ),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +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 end_of_attr_to_item = Span::new(attr.span.hi(), item.span.lo(), item.span.ctxt(), item.span.parent());
++    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());
-             if let Some(snippet) = snippet_opt(cx, end_of_attr_to_item) {
++            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 skip_item.has_name(sym!(rustfmt_skip)) ||
-             skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
++            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 4c4dd85d518a62ae9db0938d05f56f8fdfed47dc,0000000000000000000000000000000000000000..5bd7a342389fe50216553f483b4e3c236b35e969
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,155 @@@
-     /// // Bad
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 +use clippy_utils::get_parent_expr;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_block_with_applicability;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BlockCheckMode, Expr, ExprKind};
 +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::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `if` conditions that use blocks containing an
 +    /// expression, statements or conditions that use closures with blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// Style, using blocks in the condition makes it hard to read.
 +    ///
 +    /// ### Examples
 +    /// ```rust
-     /// // Good
-     /// if true { /* ... */ }
++    /// # fn somefunc() -> bool { true };
 +    /// if { true } { /* ... */ }
 +    ///
-     /// // or
-     ///
++    /// if { let x = somefunc(); x } { /* ... */ }
 +    /// ```
 +    ///
-     /// // Bad
-     /// if { let x = somefunc(); x } { /* ... */ }
++    /// Use instead:
 +    /// ```rust
 +    /// # fn somefunc() -> bool { true };
-     /// // Good
++    /// if true { /* ... */ }
 +    ///
 +    /// let res = { let x = somefunc(); x };
 +    /// if res { /* ... */ }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub BLOCKS_IN_IF_CONDITIONS,
 +    style,
 +    "useless or complex blocks that can be eliminated in conditions"
 +}
 +
 +declare_lint_pass!(BlocksInIfConditions => [BLOCKS_IN_IF_CONDITIONS]);
 +
 +struct ExVisitor<'a, 'tcx> {
 +    found_block: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for ExVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if let ExprKind::Closure(_, _, eid, _, _) = expr.kind {
 +            // do not lint if the closure is called using an iterator (see #1141)
 +            if_chain! {
 +                if let Some(parent) = get_parent_expr(self.cx, expr);
 +                if let ExprKind::MethodCall(_, [self_arg, ..], _) = &parent.kind;
 +                let caller = self.cx.typeck_results().expr_ty(self_arg);
 +                if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
 +                if implements_trait(self.cx, caller, iter_id, &[]);
 +                then {
 +                    return;
 +                }
 +            }
 +
 +            let body = self.cx.tcx.hir().body(eid);
 +            let ex = &body.value;
 +            if let ExprKind::Block(block, _) = ex.kind {
 +                if !body.value.span.from_expansion() && !block.stmts.is_empty() {
 +                    self.found_block = Some(ex);
 +                    return;
 +                }
 +            }
 +        }
 +        walk_expr(self, expr);
 +    }
 +}
 +
 +const BRACED_EXPR_MESSAGE: &str = "omit braces around single expression condition";
 +const COMPLEX_BLOCK_MESSAGE: &str = "in an `if` condition, avoid complex blocks or closures with blocks; \
 +                                    instead, move the block or closure higher and bind it with a `let`";
 +
 +impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +        if let Some(higher::If { cond, .. }) = higher::If::hir(expr) {
 +            if let ExprKind::Block(block, _) = &cond.kind {
 +                if block.rules == BlockCheckMode::DefaultBlock {
 +                    if block.stmts.is_empty() {
 +                        if let Some(ex) = &block.expr {
 +                            // don't dig into the expression here, just suggest that they remove
 +                            // the block
 +                            if expr.span.from_expansion() || ex.span.from_expansion() {
 +                                return;
 +                            }
 +                            let mut applicability = Applicability::MachineApplicable;
 +                            span_lint_and_sugg(
 +                                cx,
 +                                BLOCKS_IN_IF_CONDITIONS,
 +                                cond.span,
 +                                BRACED_EXPR_MESSAGE,
 +                                "try",
 +                                format!(
 +                                    "{}",
 +                                    snippet_block_with_applicability(
 +                                        cx,
 +                                        ex.span,
 +                                        "..",
 +                                        Some(expr.span),
 +                                        &mut applicability
 +                                    )
 +                                ),
 +                                applicability,
 +                            );
 +                        }
 +                    } else {
 +                        let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
 +                        if span.from_expansion() || expr.span.from_expansion() {
 +                            return;
 +                        }
 +                        // move block higher
 +                        let mut applicability = Applicability::MachineApplicable;
 +                        span_lint_and_sugg(
 +                            cx,
 +                            BLOCKS_IN_IF_CONDITIONS,
 +                            expr.span.with_hi(cond.span.hi()),
 +                            COMPLEX_BLOCK_MESSAGE,
 +                            "try",
 +                            format!(
 +                                "let res = {}; if res",
 +                                snippet_block_with_applicability(
 +                                    cx,
 +                                    block.span,
 +                                    "..",
 +                                    Some(expr.span),
 +                                    &mut applicability
 +                                ),
 +                            ),
 +                            applicability,
 +                        );
 +                    }
 +                }
 +            } else {
 +                let mut visitor = ExVisitor { found_block: None, cx };
 +                walk_expr(&mut visitor, cond);
 +                if let Some(block) = visitor.found_block {
 +                    span_lint(cx, BLOCKS_IN_IF_CONDITIONS, block.span, COMPLEX_BLOCK_MESSAGE);
 +                }
 +            }
 +        }
 +    }
 +}
index 0adb6327164e77f642b391dcad553ea0066fa5d6,0000000000000000000000000000000000000000..e4e122ba6eb59af48afd672470aad7d31fcab867
mode 100644,000000..100644
--- /dev/null
@@@ -1,499 -1,0 +1,510 @@@
-     /// if a && true  // should be: if a
-     /// if !(a == b)  // should be: if a != b
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
 +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
-     /// ```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
-     /// The `b` is unnecessary, the expression is equivalent to `if a`.
++    /// ```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 LOGIC_BUG,
 +    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, LOGIC_BUG]);
 +
 +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)?;
 +                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!(
 +                    "{}{}{}",
 +                    snippet_opt(cx, lhs.span)?,
 +                    op,
 +                    snippet_opt(cx, rhs.span)?
 +                ))
 +            })
 +        },
 +        ExprKind::MethodCall(path, args, _) if args.len() == 1 => {
 +            let type_of_receiver = cx.typeck_results().expr_ty(&args[0]);
 +            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!("{}.{}()", snippet_opt(cx, args[0].span)?, neg_method)))
 +        },
 +        _ => 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_and_then(
 +                            self.cx,
 +                            LOGIC_BUG,
 +                            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_and_then(
 +                    self.cx,
 +                    NONMINIMAL_BOOL,
 +                    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<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    get_trait_def_id(cx, &paths::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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ec2f31cf6737415184740ec6be5cc6fdac9aaab7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++use crate::reference::DEREF_ADDROF;
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::source::snippet_opt;
++use clippy_utils::ty::implements_trait;
++use clippy_utils::{get_parent_expr, is_lint_allowed};
++use rustc_errors::Applicability;
++use rustc_hir::{ExprKind, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::mir::Mutability;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `&*(&T)`.
++    ///
++    /// ### Why is this bad?
++    /// Dereferencing and then borrowing a reference value has no effect in most cases.
++    ///
++    /// ### Known problems
++    /// false negative on such code:
++    /// ```
++    /// let x = &12;
++    /// let addr_x = &x as *const _ as usize;
++    /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd.
++    ///                                         // But if we fix it, assert will fail.
++    /// assert_ne!(addr_x, addr_y);
++    /// ```
++    ///
++    /// ### Example
++    /// ```rust
++    /// let s = &String::new();
++    ///
++    /// // Bad
++    /// let a: &String = &* s;
++    /// foo(&*s);
++    ///
++    /// // Good
++    /// let a: &String = s;
++    /// foo(&**s);
++    ///
++    /// fn foo(_: &str){ }
++    /// ```
++    #[clippy::version = "1.59.0"]
++    pub BORROW_DEREF_REF,
++    complexity,
++    "deref on an immutable reference returns the same type as itself"
++}
++
++declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
++
++impl LateLintPass<'_> for BorrowDerefRef {
++    fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) {
++        if_chain! {
++            if !e.span.from_expansion();
++            if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
++            if !addrof_target.span.from_expansion();
++            if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
++            if !deref_target.span.from_expansion();
++            if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
++            let ref_ty = cx.typeck_results().expr_ty(deref_target);
++            if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
++            then{
++
++                if let Some(parent_expr) = get_parent_expr(cx, e){
++                    if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
++                       !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
++                        return;
++                    }
++
++                    // modification to `&mut &*x` is different from `&mut x`
++                    if matches!(deref_target.kind, ExprKind::Path(..)
++                                             | ExprKind::Field(..)
++                                             | ExprKind::Index(..)
++                                             | ExprKind::Unary(UnOp::Deref, ..))
++                     && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
++                       return;
++                    }
++                }
++
++                span_lint_and_then(
++                    cx,
++                    BORROW_DEREF_REF,
++                    e.span,
++                    "deref on an immutable reference",
++                    |diag| {
++                        diag.span_suggestion(
++                            e.span,
++                            "if you would like to reborrow, try removing `&*`",
++                            snippet_opt(cx, deref_target.span).unwrap(),
++                            Applicability::MachineApplicable
++                        );
++
++                        // has deref trait -> give 2 help
++                        // doesn't have deref trait -> give 1 help
++                        if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
++                            if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
++                                return;
++                            }
++                        }
++
++                        diag.span_suggestion(
++                            e.span,
++                            "if you would like to deref, try using `&**`",
++                            format!(
++                                "&**{}",
++                                &snippet_opt(cx, deref_target.span).unwrap(),
++                             ),
++                            Applicability::MaybeIncorrect
++                        );
++
++                    }
++                );
++
++            }
++        }
++    }
++}
index 02d97bf43df83d50587d50b9669ee205230766a4,0000000000000000000000000000000000000000..bfdbaf2413a2502030f6357a1b9c97f7ff3ae8b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,97 -1,0 +1,103 @@@
-     /// &vec.iter().filter(|x| **x == 0u8).count(); // use bytecount::count instead
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, UintTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +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_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ByteCount {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
 +            if count.ident.name == sym::count;
 +            if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
 +            if filter.ident.name == sym!(filter);
 +            if let ExprKind::Closure(_, _, body_id, _, _) = filter_arg.kind;
 +            let body = cx.tcx.hir().body(body_id);
 +            if let [param] = body.params;
 +            if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
 +            if let ExprKind::Binary(ref op, l, r) = body.value.kind;
 +            if op.node == BinOpKind::Eq;
 +            if match_type(cx,
 +                       cx.typeck_results().expr_ty(filter_recv).peel_refs(),
 +                       &paths::SLICE_ITER);
 +            let operand_is_arg = |expr| {
 +                let expr = peel_ref_operators(cx, peel_blocks(expr));
 +                path_to_local_id(expr, arg_id)
 +            };
 +            let needle = if operand_is_arg(l) {
 +                r
 +            } else if operand_is_arg(r) {
 +                l
 +            } else {
 +                return;
 +            };
 +            if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
 +            if !is_local_used(cx, needle, arg_id);
 +            then {
 +                let haystack = if let ExprKind::MethodCall(path, args, _) =
 +                        filter_recv.kind {
 +                    let p = path.ident.name;
 +                    if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
 +                        &args[0]
 +                    } else {
 +                        filter_recv
 +                    }
 +                } else {
 +                    filter_recv
 +                };
 +                let mut applicability = Applicability::MaybeIncorrect;
 +                span_lint_and_sugg(
 +                    cx,
 +                    NAIVE_BYTECOUNT,
 +                    expr.span,
 +                    "you appear to be counting bytes the naive way",
 +                    "consider using the bytecount crate",
 +                    format!("bytecount::count({}, {})",
 +                            snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
 +                            snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
 +                    applicability,
 +                );
 +            }
 +        };
 +    }
 +}
index 6bac6bf83f8e5480e735854a695c5c0dbbafc98d,0000000000000000000000000000000000000000..64ea326b75a0d3335f7407a9f3e17e9176457190
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,44 @@@
- use if_chain::if_chain;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{meets_msrv, msrvs};
- use rustc_middle::ty::Ty;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::LateContext;
-     if_chain! {
-         if meets_msrv(msrv, msrvs::UNSIGNED_ABS);
-         if cast_from.is_integral();
-         if cast_to.is_integral();
-         if cast_from.is_signed();
-         if !cast_to.is_signed();
-         if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind;
-         if let method_name = method_path.ident.name.as_str();
-         if method_name == "abs";
-         then {
-             span_lint_and_sugg(
-                 cx,
-                 CAST_ABS_TO_UNSIGNED,
-                 expr.span,
-                 &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to),
-                 "replace with",
-                 format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
-                 Applicability::MachineApplicable,
-             );
-         }
++use rustc_middle::ty::{self, Ty};
 +use rustc_semver::RustcVersion;
 +
 +use super::CAST_ABS_TO_UNSIGNED;
 +
 +pub(super) fn check(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    cast_expr: &Expr<'_>,
 +    cast_from: Ty<'_>,
 +    cast_to: Ty<'_>,
 +    msrv: Option<RustcVersion>,
 +) {
++    if meets_msrv(msrv, msrvs::UNSIGNED_ABS)
++        && let ty::Int(from) = cast_from.kind()
++        && let ty::Uint(to) = cast_to.kind()
++        && let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind
++        && method_path.ident.name.as_str() == "abs"
++    {
++        let span = if from.bit_width() == to.bit_width() {
++            expr.span
++        } else {
++            // if the result of `.unsigned_abs` would be a different type, keep the cast
++            // e.g. `i64 -> usize`, `i16 -> u8`
++            cast_expr.span
++        };
++
++        span_lint_and_sugg(
++            cx,
++            CAST_ABS_TO_UNSIGNED,
++            span,
++            &format!("casting the result of `{cast_from}::abs()` to {cast_to}"),
++            "replace with",
++            format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
++            Applicability::MachineApplicable,
++        );
 +    }
 +}
index 7eeaaa0192147d822ab50be86d1238b044adce0a,0000000000000000000000000000000000000000..1010340c712139356cbe5475e2c4d7859a402a26
mode 100644,000000..100644
--- /dev/null
@@@ -1,357 -1,0 +1,357 @@@
-         if let [int] = &*tp.segments;
 +//! lint on manually implemented checked conversions that could be transformed into `try_from`
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{meets_msrv, msrvs, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit bounds checking when casting.
 +    ///
 +    /// ### Why is this bad?
 +    /// Reduces the readability of statements & is error prone.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo: u32 = 5;
 +    /// # let _ =
 +    /// foo <= i32::MAX as u32
 +    /// # ;
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// # let foo = 1;
 +    /// # let _ =
 +    /// i32::try_from(foo).is_ok()
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub CHECKED_CONVERSIONS,
 +    pedantic,
 +    "`try_from` could replace manual bounds checking when casting"
 +}
 +
 +pub struct CheckedConversions {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl CheckedConversions {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
 +        if !meets_msrv(self.msrv, msrvs::TRY_FROM) {
 +            return;
 +        }
 +
 +        let result = if_chain! {
 +            if !in_external_macro(cx.sess(), item.span);
 +            if let ExprKind::Binary(op, left, right) = &item.kind;
 +
 +            then {
 +                match op.node {
 +                    BinOpKind::Ge | BinOpKind::Le => single_check(item),
 +                    BinOpKind::And => double_check(cx, left, right),
 +                    _ => None,
 +                }
 +            } else {
 +                None
 +            }
 +        };
 +
 +        if let Some(cv) = result {
 +            if let Some(to_type) = cv.to_type {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let snippet = snippet_with_applicability(cx, cv.expr_to_cast.span, "_", &mut applicability);
 +                span_lint_and_sugg(
 +                    cx,
 +                    CHECKED_CONVERSIONS,
 +                    item.span,
 +                    "checked cast can be simplified",
 +                    "try",
 +                    format!("{}::try_from({}).is_ok()", to_type, snippet),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Searches for a single check from unsigned to _ is done
 +/// todo: check for case signed -> larger unsigned == only x >= 0
 +fn single_check<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
 +    check_upper_bound(expr).filter(|cv| cv.cvt == ConversionType::FromUnsigned)
 +}
 +
 +/// Searches for a combination of upper & lower bound checks
 +fn double_check<'a>(cx: &LateContext<'_>, left: &'a Expr<'_>, right: &'a Expr<'_>) -> Option<Conversion<'a>> {
 +    let upper_lower = |l, r| {
 +        let upper = check_upper_bound(l);
 +        let lower = check_lower_bound(r);
 +
 +        upper.zip(lower).and_then(|(l, r)| l.combine(r, cx))
 +    };
 +
 +    upper_lower(left, right).or_else(|| upper_lower(right, left))
 +}
 +
 +/// Contains the result of a tried conversion check
 +#[derive(Clone, Debug)]
 +struct Conversion<'a> {
 +    cvt: ConversionType,
 +    expr_to_cast: &'a Expr<'a>,
 +    to_type: Option<&'a str>,
 +}
 +
 +/// The kind of conversion that is checked
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +enum ConversionType {
 +    SignedToUnsigned,
 +    SignedToSigned,
 +    FromUnsigned,
 +}
 +
 +impl<'a> Conversion<'a> {
 +    /// Combine multiple conversions if the are compatible
 +    pub fn combine(self, other: Self, cx: &LateContext<'_>) -> Option<Conversion<'a>> {
 +        if self.is_compatible(&other, cx) {
 +            // Prefer a Conversion that contains a type-constraint
 +            Some(if self.to_type.is_some() { self } else { other })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    /// Checks if two conversions are compatible
 +    /// same type of conversion, same 'castee' and same 'to type'
 +    pub fn is_compatible(&self, other: &Self, cx: &LateContext<'_>) -> bool {
 +        (self.cvt == other.cvt)
 +            && (SpanlessEq::new(cx).eq_expr(self.expr_to_cast, other.expr_to_cast))
 +            && (self.has_compatible_to_type(other))
 +    }
 +
 +    /// Checks if the to-type is the same (if there is a type constraint)
 +    fn has_compatible_to_type(&self, other: &Self) -> bool {
 +        match (self.to_type, other.to_type) {
 +            (Some(l), Some(r)) => l == r,
 +            _ => true,
 +        }
 +    }
 +
 +    /// Try to construct a new conversion if the conversion type is valid
 +    fn try_new(expr_to_cast: &'a Expr<'_>, from_type: &str, to_type: &'a str) -> Option<Conversion<'a>> {
 +        ConversionType::try_new(from_type, to_type).map(|cvt| Conversion {
 +            cvt,
 +            expr_to_cast,
 +            to_type: Some(to_type),
 +        })
 +    }
 +
 +    /// Construct a new conversion without type constraint
 +    fn new_any(expr_to_cast: &'a Expr<'_>) -> Conversion<'a> {
 +        Conversion {
 +            cvt: ConversionType::SignedToUnsigned,
 +            expr_to_cast,
 +            to_type: None,
 +        }
 +    }
 +}
 +
 +impl ConversionType {
 +    /// Creates a conversion type if the type is allowed & conversion is valid
 +    #[must_use]
 +    fn try_new(from: &str, to: &str) -> Option<Self> {
 +        if UINTS.contains(&from) {
 +            Some(Self::FromUnsigned)
 +        } else if SINTS.contains(&from) {
 +            if UINTS.contains(&to) {
 +                Some(Self::SignedToUnsigned)
 +            } else if SINTS.contains(&to) {
 +                Some(Self::SignedToSigned)
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Check for `expr <= (to_type::MAX as from_type)`
 +fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
 +    if_chain! {
 +         if let ExprKind::Binary(ref op, left, right) = &expr.kind;
 +         if let Some((candidate, check)) = normalize_le_ge(op, left, right);
 +         if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX");
 +
 +         then {
 +             Conversion::try_new(candidate, from, to)
 +         } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Check for `expr >= 0|(to_type::MIN as from_type)`
 +fn check_lower_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
 +    fn check_function<'a>(candidate: &'a Expr<'a>, check: &'a Expr<'a>) -> Option<Conversion<'a>> {
 +        (check_lower_bound_zero(candidate, check)).or_else(|| (check_lower_bound_min(candidate, check)))
 +    }
 +
 +    // First of we need a binary containing the expression & the cast
 +    if let ExprKind::Binary(ref op, left, right) = &expr.kind {
 +        normalize_le_ge(op, right, left).and_then(|(l, r)| check_function(l, r))
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Check for `expr >= 0`
 +fn check_lower_bound_zero<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
 +    if_chain! {
 +        if let ExprKind::Lit(ref lit) = &check.kind;
 +        if let LitKind::Int(0, _) = &lit.node;
 +
 +        then {
 +            Some(Conversion::new_any(candidate))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Check for `expr >= (to_type::MIN as from_type)`
 +fn check_lower_bound_min<'a>(candidate: &'a Expr<'_>, check: &'a Expr<'_>) -> Option<Conversion<'a>> {
 +    if let Some((from, to)) = get_types_from_cast(check, SINTS, "min_value", "MIN") {
 +        Conversion::try_new(candidate, from, to)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Tries to extract the from- and to-type from a cast expression
 +fn get_types_from_cast<'a>(
 +    expr: &'a Expr<'_>,
 +    types: &'a [&str],
 +    func: &'a str,
 +    assoc_const: &'a str,
 +) -> Option<(&'a str, &'a str)> {
 +    // `to_type::max_value() as from_type`
 +    // or `to_type::MAX as from_type`
 +    let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! {
 +        // to_type::max_value(), from_type
 +        if let ExprKind::Cast(limit, from_type) = &expr.kind;
 +        if let TyKind::Path(ref from_type_path) = &from_type.kind;
 +        if let Some(from_sym) = int_ty_to_sym(from_type_path);
 +
 +        then {
 +            Some((limit, from_sym))
 +        } else {
 +            None
 +        }
 +    };
 +
 +    // `from_type::from(to_type::max_value())`
 +    let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
 +        if_chain! {
 +            // `from_type::from, to_type::max_value()`
 +            if let ExprKind::Call(from_func, args) = &expr.kind;
 +            // `to_type::max_value()`
 +            if args.len() == 1;
 +            if let limit = &args[0];
 +            // `from_type::from`
 +            if let ExprKind::Path(ref path) = &from_func.kind;
 +            if let Some(from_sym) = get_implementing_type(path, INTS, "from");
 +
 +            then {
 +                Some((limit, from_sym))
 +            } else {
 +                None
 +            }
 +        }
 +    });
 +
 +    if let Some((limit, from_type)) = limit_from {
 +        match limit.kind {
 +            // `from_type::from(_)`
 +            ExprKind::Call(path, _) => {
 +                if let ExprKind::Path(ref path) = path.kind {
 +                    // `to_type`
 +                    if let Some(to_type) = get_implementing_type(path, types, func) {
 +                        return Some((from_type, to_type));
 +                    }
 +                }
 +            },
 +            // `to_type::MAX`
 +            ExprKind::Path(ref path) => {
 +                if let Some(to_type) = get_implementing_type(path, types, assoc_const) {
 +                    return Some((from_type, to_type));
 +                }
 +            },
 +            _ => {},
 +        }
 +    };
 +    None
 +}
 +
 +/// Gets the type which implements the called function
 +fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
 +    if_chain! {
 +        if let QPath::TypeRelative(ty, path) = &path;
 +        if path.ident.name.as_str() == function;
 +        if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind;
-         if let [ty] = &*path.segments;
++        if let [int] = tp.segments;
 +        then {
 +            let name = int.ident.name.as_str();
 +            candidates.iter().find(|c| &name == *c).copied()
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Gets the type as a string, if it is a supported integer
 +fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
 +    if_chain! {
 +        if let QPath::Resolved(_, path) = *path;
++        if let [ty] = path.segments;
 +        then {
 +            let name = ty.ident.name.as_str();
 +            INTS.iter().find(|c| &name == *c).copied()
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Will return the expressions as if they were expr1 <= expr2
 +fn normalize_le_ge<'a>(op: &BinOp, left: &'a Expr<'a>, right: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
 +    match op.node {
 +        BinOpKind::Le => Some((left, right)),
 +        BinOpKind::Ge => Some((right, left)),
 +        _ => None,
 +    }
 +}
 +
 +// Constants
 +const UINTS: &[&str] = &["u8", "u16", "u32", "u64", "usize"];
 +const SINTS: &[&str] = &["i8", "i16", "i32", "i64", "isize"];
 +const INTS: &[&str] = &["u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "isize"];
index 317c4bfb3226ef1f5209a23ff6ee1b56d2b540f9,0000000000000000000000000000000000000000..33c44f8b2dba9199a40e7c460f95aa9e570157f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,167 -1,0 +1,167 @@@
-     /// No. You'll see it when you get the warning.
 +//! 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::LimitStack;
 +use rustc_ast::ast::Attribute;
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +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<'_>,
 +        body: &'tcx Body<'_>,
 +        body_span: Span,
 +    ) {
 +        if body_span.from_expansion() {
 +            return;
 +        }
 +
 +        let expr = &body.value;
 +
 +        let mut helper = CcHelper { cc: 1, returns: 0 };
 +        helper.visit_expr(expr);
 +        let CcHelper { cc, returns } = helper;
 +        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)
 +        };
 +
 +        let mut rust_cc = cc;
 +        // prevent degenerate cases where unreachable code contains `return` statements
 +        if rust_cc >= ret_adjust {
 +            rust_cc -= ret_adjust;
 +        }
 +
 +        if rust_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 ({}/{})",
 +                    rust_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) {
 +            self.check(cx, kind, decl, body, 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");
 +    }
 +}
 +
 +struct CcHelper {
 +    cc: u64,
 +    returns: u64,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for CcHelper {
 +    fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +        walk_expr(self, e);
 +        match e.kind {
 +            ExprKind::If(_, _, _) => {
 +                self.cc += 1;
 +            },
 +            ExprKind::Match(_, arms, _) => {
 +                if arms.len() > 1 {
 +                    self.cc += 1;
 +                }
 +                self.cc += arms.iter().filter(|arm| arm.guard.is_some()).count() as u64;
 +            },
 +            ExprKind::Ret(_) => self.returns += 1,
 +            _ => {},
 +        }
 +    }
 +}
index 3227e6e86af4e3021c3b97c28655437536f12f2c,0000000000000000000000000000000000000000..3eceb848822e95bfca48bbd2f30d269757e767a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,195 -1,0 +1,195 @@@
-     /// Should be written:
 +//! Checks for if expressions that contain only an if expression.
 +//!
 +//! For example, the lint would catch:
 +//!
 +//! ```rust,ignore
 +//! if x {
 +//!     if y {
 +//!         println!("Hello world");
 +//!     }
 +//! }
 +//! ```
 +//!
 +//! This lint is **warn** by default
 +
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for nested `if` statements which can be collapsed
 +    /// by `&&`-combining their conditions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Each `if`-statement adds one level of nesting, which
 +    /// makes code look more complex than it really is.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if x {
 +    ///     if y {
 +    ///         …
 +    ///     }
 +    /// }
 +    ///
 +    /// ```
 +    ///
++    /// Use instead:
 +    ///
 +    /// ```rust,ignore
 +    /// if x && y {
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub COLLAPSIBLE_IF,
 +    style,
 +    "nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for collapsible `else { if ... }` expressions
 +    /// that can be collapsed to `else if ...`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Each `if`-statement adds one level of nesting, which
 +    /// makes code look more complex than it really is.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    ///
 +    /// if x {
 +    ///     …
 +    /// } else {
 +    ///     if y {
 +    ///         …
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Should be written:
 +    ///
 +    /// ```rust,ignore
 +    /// if x {
 +    ///     …
 +    /// } else if y {
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub COLLAPSIBLE_ELSE_IF,
 +    style,
 +    "nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
 +}
 +
 +declare_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
 +
 +impl EarlyLintPass for CollapsibleIf {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +        if !expr.span.from_expansion() {
 +            check_if(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_if(cx: &EarlyContext<'_>, expr: &ast::Expr) {
 +    if let ast::ExprKind::If(check, then, else_) = &expr.kind {
 +        if let Some(else_) = else_ {
 +            check_collapsible_maybe_if_let(cx, then.span, else_);
 +        } else if let ast::ExprKind::Let(..) = check.kind {
 +            // Prevent triggering on `if let a = b { if c { .. } }`.
 +        } else {
 +            check_collapsible_no_if_let(cx, expr, check, then);
 +        }
 +    }
 +}
 +
 +fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
 +    // We trim all opening braces and whitespaces and then check if the next string is a comment.
 +    let trimmed_block_text = snippet_block(cx, expr.span, "..", None)
 +        .trim_start_matches(|c: char| c.is_whitespace() || c == '{')
 +        .to_owned();
 +    trimmed_block_text.starts_with("//") || trimmed_block_text.starts_with("/*")
 +}
 +
 +fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
 +    if_chain! {
 +        if let ast::ExprKind::Block(ref block, _) = else_.kind;
 +        if !block_starts_with_comment(cx, block);
 +        if let Some(else_) = expr_block(block);
 +        if else_.attrs.is_empty();
 +        if !else_.span.from_expansion();
 +        if let ast::ExprKind::If(..) = else_.kind;
 +        then {
 +            // Prevent "elseif"
 +            // Check that the "else" is followed by whitespace
 +            let up_to_else = then_span.between(block.span);
 +            let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false };
 +
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                COLLAPSIBLE_ELSE_IF,
 +                block.span,
 +                "this `else { if .. }` block can be collapsed",
 +                "collapse nested if block",
 +                format!(
 +                    "{}{}",
 +                    if requires_space { " " } else { "" },
 +                    snippet_block_with_applicability(cx, else_.span, "..", Some(block.span), &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
 +    if_chain! {
 +        if !block_starts_with_comment(cx, then);
 +        if let Some(inner) = expr_block(then);
 +        if inner.attrs.is_empty();
 +        if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
 +        // Prevent triggering on `if c { if let a = b { .. } }`.
 +        if !matches!(check_inner.kind, ast::ExprKind::Let(..));
 +        if expr.span.ctxt() == inner.span.ctxt();
 +        then {
 +            span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| {
 +                let lhs = Sugg::ast(cx, check, "..");
 +                let rhs = Sugg::ast(cx, check_inner, "..");
 +                diag.span_suggestion(
 +                    expr.span,
 +                    "collapse nested if block",
 +                    format!(
 +                        "if {} {}",
 +                        lhs.and(&rhs),
 +                        snippet_block(cx, content.span, "..", Some(expr.span)),
 +                    ),
 +                    Applicability::MachineApplicable, // snippet
 +                );
 +            });
 +        }
 +    }
 +}
 +
 +/// If the block contains only one expression, return it.
 +fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
 +    let mut it = block.stmts.iter();
 +
 +    if let (Some(stmt), None) = (it.next(), it.next()) {
 +        match stmt.kind {
 +            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
index 399d11472b09b2a6f49b8346c65a09cdb8368267,0000000000000000000000000000000000000000..913e081af3bdae6bb8ec557f0e11a66cd586fcb6
mode 100644,000000..100644
--- /dev/null
@@@ -1,133 -1,0 +1,133 @@@
-     /// Could be written:
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::{get_trait_def_id, if_sequence, in_constant, is_else_clause, paths, SpanlessEq};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks comparison chains written with `if` that can be
 +    /// rewritten with `match` and `cmp`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `if` is not guaranteed to be exhaustive and conditionals can get
 +    /// repetitive
 +    ///
 +    /// ### Known problems
 +    /// The match statement may be slower due to the compiler
 +    /// not inlining the call to cmp. See issue [#5354](https://github.com/rust-lang/rust-clippy/issues/5354)
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # fn c() {}
 +    /// fn f(x: u8, y: u8) {
 +    ///     if x > y {
 +    ///         a()
 +    ///     } else if x < y {
 +    ///         b()
 +    ///     } else {
 +    ///         c()
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
++    /// Use instead:
 +    ///
 +    /// ```rust,ignore
 +    /// use std::cmp::Ordering;
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # fn c() {}
 +    /// fn f(x: u8, y: u8) {
 +    ///      match x.cmp(&y) {
 +    ///          Ordering::Greater => a(),
 +    ///          Ordering::Less => b(),
 +    ///          Ordering::Equal => c()
 +    ///      }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub COMPARISON_CHAIN,
 +    style,
 +    "`if`s that can be rewritten with `match` and `cmp`"
 +}
 +
 +declare_lint_pass!(ComparisonChain => [COMPARISON_CHAIN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        // We only care about the top-most `if` in the chain
 +        if is_else_clause(cx.tcx, expr) {
 +            return;
 +        }
 +
 +        if in_constant(cx, expr.hir_id) {
 +            return;
 +        }
 +
 +        // Check that there exists at least one explicit else condition
 +        let (conds, _) = if_sequence(expr);
 +        if conds.len() < 2 {
 +            return;
 +        }
 +
 +        for cond in conds.windows(2) {
 +            if let (&ExprKind::Binary(ref kind1, lhs1, rhs1), &ExprKind::Binary(ref kind2, lhs2, rhs2)) =
 +                (&cond[0].kind, &cond[1].kind)
 +            {
 +                if !kind_is_cmp(kind1.node) || !kind_is_cmp(kind2.node) {
 +                    return;
 +                }
 +
 +                // Check that both sets of operands are equal
 +                let mut spanless_eq = SpanlessEq::new(cx);
 +                let same_fixed_operands = spanless_eq.eq_expr(lhs1, lhs2) && spanless_eq.eq_expr(rhs1, rhs2);
 +                let same_transposed_operands = spanless_eq.eq_expr(lhs1, rhs2) && spanless_eq.eq_expr(rhs1, lhs2);
 +
 +                if !same_fixed_operands && !same_transposed_operands {
 +                    return;
 +                }
 +
 +                // Check that if the operation is the same, either it's not `==` or the operands are transposed
 +                if kind1.node == kind2.node {
 +                    if kind1.node == BinOpKind::Eq {
 +                        return;
 +                    }
 +                    if !same_transposed_operands {
 +                        return;
 +                    }
 +                }
 +
 +                // Check that the type being compared implements `core::cmp::Ord`
 +                let ty = cx.typeck_results().expr_ty(lhs1);
 +                let is_ord = get_trait_def_id(cx, &paths::ORD).map_or(false, |id| implements_trait(cx, ty, id, &[]));
 +
 +                if !is_ord {
 +                    return;
 +                }
 +            } else {
 +                // We only care about comparison chains
 +                return;
 +            }
 +        }
 +        span_lint_and_help(
 +            cx,
 +            COMPARISON_CHAIN,
 +            expr.span,
 +            "`if` chain can be rewritten with `match`",
 +            None,
 +            "consider rewriting the `if` chain to use `cmp` and `match`",
 +        );
 +    }
 +}
 +
 +fn kind_is_cmp(kind: BinOpKind) -> bool {
 +    matches!(kind, BinOpKind::Lt | BinOpKind::Gt | BinOpKind::Eq)
 +}
index e6a0162fd02728d5c205996b2a34c0500756dba6,0000000000000000000000000000000000000000..1e9a1153011000397b0b8fdf731935c4fd131973
mode 100644,000000..100644
--- /dev/null
@@@ -1,651 -1,0 +1,651 @@@
-     /// Could be written as:
 +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 +use clippy_utils::{
 +    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, is_else_clause, is_lint_allowed,
 +    search_same, ContainsName, SpanlessEq, SpanlessHash,
 +};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::{Applicability, Diagnostic};
 +use rustc_hir::intravisit::{self, Visitor};
 +use rustc_hir::{Block, Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{source_map::Span, symbol::Symbol, BytePos};
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same condition.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if a == b {
 +    ///     …
 +    /// } else if a == b {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// Note that this lint ignores all conditions with a function call as it could
 +    /// have side effects:
 +    ///
 +    /// ```ignore
 +    /// if foo() {
 +    ///     …
 +    /// } else if foo() { // not linted
 +    ///     …
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IFS_SAME_COND,
 +    correctness,
 +    "consecutive `if`s with the same condition"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same function call.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    /// Despite the fact that function can have side effects and `if` works as
 +    /// intended, such an approach is implicit and can be considered a "code smell".
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == bar {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// This probably should be:
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == baz {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo and called function mutates a state,
 +    /// consider move the mutation out of the `if` condition to avoid similarity to
 +    /// a copy & paste error:
 +    ///
 +    /// ```ignore
 +    /// let first = foo();
 +    /// if first == bar {
 +    ///     …
 +    /// } else {
 +    ///     let second = foo();
 +    ///     if second == bar {
 +    ///     …
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub SAME_FUNCTIONS_IN_IF_CONDITION,
 +    pedantic,
 +    "consecutive `if`s with the same function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `if/else` with the same body as the *then* part
 +    /// and the *else* part.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     42
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IF_SAME_THEN_ELSE,
 +    correctness,
 +    "`if` with the same `then` and `else` blocks"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks if the `if` and `else` block contain shared code that can be
 +    /// moved out of the blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate code is less maintainable.
 +    ///
 +    /// ### Known problems
 +    /// * The lint doesn't check if the moved expressions modify values that are being used in
 +    ///   the if condition. The suggestion can in that case modify the behavior of the program.
 +    ///   See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     println!("Hello World");
 +    ///     13
 +    /// } else {
 +    ///     println!("Hello World");
 +    ///     42
 +    /// };
 +    /// ```
 +    ///
++    /// Use instead:
 +    /// ```ignore
 +    /// println!("Hello World");
 +    /// let foo = if … {
 +    ///     13
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub BRANCHES_SHARING_CODE,
 +    nursery,
 +    "`if` statement with shared code in all blocks"
 +}
 +
 +declare_lint_pass!(CopyAndPaste => [
 +    IFS_SAME_COND,
 +    SAME_FUNCTIONS_IN_IF_CONDITION,
 +    IF_SAME_THEN_ELSE,
 +    BRANCHES_SHARING_CODE
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !expr.span.from_expansion() {
 +            if let ExprKind::If(_, _, _) = expr.kind {
 +                // skip ifs directly in else, it will be checked in the parent if
 +                if let Some(&Expr {
 +                    kind: ExprKind::If(_, _, Some(else_expr)),
 +                    ..
 +                }) = get_parent_expr(cx, expr)
 +                {
 +                    if else_expr.hir_id == expr.hir_id {
 +                        return;
 +                    }
 +                }
 +
 +                let (conds, blocks) = if_sequence(expr);
 +                // Conditions
 +                lint_same_cond(cx, &conds);
 +                lint_same_fns_in_if_cond(cx, &conds);
 +                // Block duplication
 +                lint_same_then_else(cx, &conds, &blocks, conds.len() == blocks.len(), expr);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal.
 +fn lint_same_then_else<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    conds: &[&'tcx Expr<'_>],
 +    blocks: &[&Block<'tcx>],
 +    has_conditional_else: bool,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    // We only lint ifs with multiple blocks
 +    if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
 +        return;
 +    }
 +
 +    // Check if each block has shared code
 +    let has_expr = blocks[0].expr.is_some();
 +
 +    let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, conds, blocks) {
 +        (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
 +    } else {
 +        return;
 +    };
 +
 +    // BRANCHES_SHARING_CODE prerequisites
 +    if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
 +        return;
 +    }
 +
 +    // Only the start is the same
 +    if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) {
 +        let block = blocks[0];
 +        let start_stmts = block.stmts.split_at(start_eq).0;
 +
 +        let mut start_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in start_stmts {
 +            intravisit::walk_stmt(&mut start_walker, stmt);
 +        }
 +
 +        emit_branches_sharing_code_lint(
 +            cx,
 +            start_eq,
 +            0,
 +            false,
 +            check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr),
 +            blocks,
 +            expr,
 +        );
 +    } else if end_eq != 0 || (has_expr && expr_eq) {
 +        let block = blocks[blocks.len() - 1];
 +        let (start_stmts, block_stmts) = block.stmts.split_at(start_eq);
 +        let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq);
 +
 +        // Scan start
 +        let mut start_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in start_stmts {
 +            intravisit::walk_stmt(&mut start_walker, stmt);
 +        }
 +        let mut moved_syms = start_walker.def_symbols;
 +
 +        // Scan block
 +        let mut block_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in block_stmts {
 +            intravisit::walk_stmt(&mut block_walker, stmt);
 +        }
 +        let mut block_defs = block_walker.defs;
 +
 +        // Scan moved stmts
 +        let mut moved_start: Option<usize> = None;
 +        let mut end_walker = UsedValueFinderVisitor::new(cx);
 +        for (index, stmt) in end_stmts.iter().enumerate() {
 +            intravisit::walk_stmt(&mut end_walker, stmt);
 +
 +            for value in &end_walker.uses {
 +                // Well we can't move this and all prev statements. So reset
 +                if block_defs.contains(value) {
 +                    moved_start = Some(index + 1);
 +                    end_walker.defs.drain().for_each(|x| {
 +                        block_defs.insert(x);
 +                    });
 +
 +                    end_walker.def_symbols.clear();
 +                }
 +            }
 +
 +            end_walker.uses.clear();
 +        }
 +
 +        if let Some(moved_start) = moved_start {
 +            end_eq -= moved_start;
 +        }
 +
 +        let end_linable = block.expr.map_or_else(
 +            || end_eq != 0,
 +            |expr| {
 +                intravisit::walk_expr(&mut end_walker, expr);
 +                end_walker.uses.iter().any(|x| !block_defs.contains(x))
 +            },
 +        );
 +
 +        if end_linable {
 +            end_walker.def_symbols.drain().for_each(|x| {
 +                moved_syms.insert(x);
 +            });
 +        }
 +
 +        emit_branches_sharing_code_lint(
 +            cx,
 +            start_eq,
 +            end_eq,
 +            end_linable,
 +            check_for_warn_of_moved_symbol(cx, &moved_syms, expr),
 +            blocks,
 +            expr,
 +        );
 +    }
 +}
 +
 +struct BlockEqual {
 +    /// The amount statements that are equal from the start
 +    start_eq: usize,
 +    /// The amount statements that are equal from the end
 +    end_eq: usize,
 +    ///  An indication if the block expressions are the same. This will also be true if both are
 +    /// `None`
 +    expr_eq: bool,
 +}
 +
 +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
 +/// abort any further processing and avoid duplicate lint triggers.
 +fn scan_block_for_eq(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&Block<'_>]) -> Option<BlockEqual> {
 +    let mut start_eq = usize::MAX;
 +    let mut end_eq = usize::MAX;
 +    let mut expr_eq = true;
 +    let mut iter = blocks.windows(2).enumerate();
 +    while let Some((i, &[block0, block1])) = iter.next() {
 +        let l_stmts = block0.stmts;
 +        let r_stmts = block1.stmts;
 +
 +        // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
 +        // The comparison therefore needs to be done in a way that builds the correct context.
 +        let mut evaluator = SpanlessEq::new(cx);
 +        let mut evaluator = evaluator.inter_expr();
 +
 +        let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r));
 +
 +        let current_end_eq = {
 +            // We skip the middle statements which can't be equal
 +            let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq;
 +            let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count);
 +            let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count);
 +            it1.zip(it2)
 +                .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
 +        };
 +        let block_expr_eq = both(&block0.expr, &block1.expr, |l, r| evaluator.eq_expr(l, r));
 +
 +        // IF_SAME_THEN_ELSE
 +        if_chain! {
 +            if block_expr_eq;
 +            if l_stmts.len() == r_stmts.len();
 +            if l_stmts.len() == current_start_eq;
 +            // `conds` may have one last item than `blocks`.
 +            // Any `i` from `blocks.windows(2)` will exist in `conds`, but `i+1` may not exist on the last iteration.
 +            if !matches!(conds[i].kind, ExprKind::Let(..));
 +            if !matches!(conds.get(i + 1).map(|e| &e.kind), Some(ExprKind::Let(..)));
 +            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block0.hir_id);
 +            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, block1.hir_id);
 +            then {
 +                span_lint_and_note(
 +                    cx,
 +                    IF_SAME_THEN_ELSE,
 +                    block0.span,
 +                    "this `if` has identical blocks",
 +                    Some(block1.span),
 +                    "same as this",
 +                );
 +
 +                return None;
 +            }
 +        }
 +
 +        start_eq = start_eq.min(current_start_eq);
 +        end_eq = end_eq.min(current_end_eq);
 +        expr_eq &= block_expr_eq;
 +    }
 +
 +    if !expr_eq {
 +        end_eq = 0;
 +    }
 +
 +    // Check if the regions are overlapping. Set `end_eq` to prevent the overlap
 +    let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap();
 +    if (start_eq + end_eq) > min_block_size {
 +        end_eq = min_block_size - start_eq;
 +    }
 +
 +    Some(BlockEqual {
 +        start_eq,
 +        end_eq,
 +        expr_eq,
 +    })
 +}
 +
 +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool {
 +    get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
 +        let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
 +
 +        symbols
 +            .iter()
 +            .filter(|sym| !sym.as_str().starts_with('_'))
 +            .any(move |sym| {
 +                let mut walker = ContainsName {
 +                    name: *sym,
 +                    result: false,
 +                };
 +
 +                // Scan block
 +                block
 +                    .stmts
 +                    .iter()
 +                    .filter(|stmt| !ignore_span.overlaps(stmt.span))
 +                    .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
 +
 +                if let Some(expr) = block.expr {
 +                    intravisit::walk_expr(&mut walker, expr);
 +                }
 +
 +                walker.result
 +            })
 +    })
 +}
 +
 +fn emit_branches_sharing_code_lint(
 +    cx: &LateContext<'_>,
 +    start_stmts: usize,
 +    end_stmts: usize,
 +    lint_end: bool,
 +    warn_about_moved_symbol: bool,
 +    blocks: &[&Block<'_>],
 +    if_expr: &Expr<'_>,
 +) {
 +    if start_stmts == 0 && !lint_end {
 +        return;
 +    }
 +
 +    // (help, span, suggestion)
 +    let mut suggestions: Vec<(&str, Span, String)> = vec![];
 +    let mut add_expr_note = false;
 +
 +    // Construct suggestions
 +    let sm = cx.sess().source_map();
 +    if start_stmts > 0 {
 +        let block = blocks[0];
 +        let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo();
 +        let span_end = sm.stmt_span(block.stmts[start_stmts - 1].span, block.span);
 +
 +        let cond_span = first_line_of_span(cx, if_expr.span).until(block.span);
 +        let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None);
 +        let cond_indent = indent_of(cx, cond_span);
 +        let moved_span = block.stmts[0].span.source_callsite().to(span_end);
 +        let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
 +        let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
 +
 +        let span = span_start.to(span_end);
 +        suggestions.push(("start", span, suggestion.to_string()));
 +    }
 +
 +    if lint_end {
 +        let block = blocks[blocks.len() - 1];
 +        let span_end = block.span.shrink_to_hi();
 +
 +        let moved_start = if end_stmts == 0 && block.expr.is_some() {
 +            block.expr.unwrap().span.source_callsite()
 +        } else {
 +            sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span)
 +        };
 +        let moved_end = block.expr.map_or_else(
 +            || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span),
 +            |expr| expr.span.source_callsite(),
 +        );
 +
 +        let moved_span = moved_start.to(moved_end);
 +        let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
 +        let indent = indent_of(cx, if_expr.span.shrink_to_hi());
 +        let suggestion = "}\n".to_string() + &moved_snipped;
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
 +
 +        let mut span = moved_start.to(span_end);
 +        // Improve formatting if the inner block has indention (i.e. normal Rust formatting)
 +        let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent());
 +        if snippet_opt(cx, test_span)
 +            .map(|snip| snip == "    ")
 +            .unwrap_or_default()
 +        {
 +            span = span.with_lo(test_span.lo());
 +        }
 +
 +        suggestions.push(("end", span, suggestion.to_string()));
 +        add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit();
 +    }
 +
 +    let add_optional_msgs = |diag: &mut Diagnostic| {
 +        if add_expr_note {
 +            diag.note("The end suggestion probably needs some adjustments to use the expression result correctly");
 +        }
 +
 +        if warn_about_moved_symbol {
 +            diag.warn("Some moved values might need to be renamed to avoid wrong references");
 +        }
 +    };
 +
 +    // Emit lint
 +    if suggestions.len() == 1 {
 +        let (place_str, span, sugg) = suggestions.pop().unwrap();
 +        let msg = format!("all if blocks contain the same code at the {}", place_str);
 +        let help = format!("consider moving the {} statements out like this", place_str);
 +        span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| {
 +            diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified);
 +
 +            add_optional_msgs(diag);
 +        });
 +    } else if suggestions.len() == 2 {
 +        let (_, end_span, end_sugg) = suggestions.pop().unwrap();
 +        let (_, start_span, start_sugg) = suggestions.pop().unwrap();
 +        span_lint_and_then(
 +            cx,
 +            BRANCHES_SHARING_CODE,
 +            start_span,
 +            "all if blocks contain the same code at the start and the end. Here at the start",
 +            move |diag| {
 +                diag.span_note(end_span, "and here at the end");
 +
 +                diag.span_suggestion(
 +                    start_span,
 +                    "consider moving the start statements out like this",
 +                    start_sugg,
 +                    Applicability::Unspecified,
 +                );
 +
 +                diag.span_suggestion(
 +                    end_span,
 +                    "and consider moving the end statements out like this",
 +                    end_sugg,
 +                    Applicability::Unspecified,
 +                );
 +
 +                add_optional_msgs(diag);
 +            },
 +        );
 +    }
 +}
 +
 +/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values.
 +struct UsedValueFinderVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +
 +    /// The `HirId`s of defined values in the scanned statements
 +    defs: FxHashSet<HirId>,
 +
 +    /// The Symbols of the defined symbols in the scanned statements
 +    def_symbols: FxHashSet<Symbol>,
 +
 +    /// The `HirId`s of the used values
 +    uses: FxHashSet<HirId>,
 +}
 +
 +impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        UsedValueFinderVisitor {
 +            cx,
 +            defs: FxHashSet::default(),
 +            def_symbols: FxHashSet::default(),
 +            uses: FxHashSet::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) {
 +        let local_id = l.pat.hir_id;
 +        self.defs.insert(local_id);
 +
 +        if let Some(sym) = l.pat.simple_ident() {
 +            self.def_symbols.insert(sym.name);
 +        }
 +
 +        if let Some(expr) = l.init {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
 +        if let rustc_hir::QPath::Resolved(_, path) = *qpath {
 +            if path.segments.len() == 1 {
 +                if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) {
 +                    self.uses.insert(var);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of `IFS_SAME_COND`.
 +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(expr);
 +        h.finish()
 +    };
 +
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) };
 +
 +    for (i, j) in search_same(conds, hash, eq) {
 +        span_lint_and_note(
 +            cx,
 +            IFS_SAME_COND,
 +            j.span,
 +            "this `if` has the same condition as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
 +
 +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
 +fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(expr);
 +        h.finish()
 +    };
 +
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
 +        // Do not lint if any expr originates from a macro
 +        if lhs.span.from_expansion() || rhs.span.from_expansion() {
 +            return false;
 +        }
 +        // Do not spawn warning if `IFS_SAME_COND` already produced it.
 +        if eq_expr_value(cx, lhs, rhs) {
 +            return false;
 +        }
 +        SpanlessEq::new(cx).eq_expr(lhs, rhs)
 +    };
 +
 +    for (i, j) in search_same(conds, hash, eq) {
 +        span_lint_and_note(
 +            cx,
 +            SAME_FUNCTIONS_IN_IF_CONDITION,
 +            j.span,
 +            "this `if` has the same function call as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
index fc141b4a6e3afcc3874b5c776be3cfb88303bdfc,0000000000000000000000000000000000000000..9b8a481b6eab796157268e671ca3ebcfabbd0cd6
mode 100644,000000..100644
--- /dev/null
@@@ -1,125 -1,0 +1,125 @@@
-             if let Some(span) = is_crate_keyword(&curr);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
 +use rustc_ast::token::{Token, TokenKind};
 +use rustc_ast::tokenstream::{TokenStream, TokenTree};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{symbol::sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
 +    ///
 +    /// ### Why is this bad?
 +    /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
 +    /// crate. Rarely is the former intended. See:
 +    /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[macro_export]
 +    /// macro_rules! print_message {
 +    ///     () => {
 +    ///         println!("{}", crate::MESSAGE);
 +    ///     };
 +    /// }
 +    /// pub const MESSAGE: &str = "Hello!";
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[macro_export]
 +    /// macro_rules! print_message {
 +    ///     () => {
 +    ///         println!("{}", $crate::MESSAGE);
 +    ///     };
 +    /// }
 +    /// pub const MESSAGE: &str = "Hello!";
 +    /// ```
 +    ///
 +    /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
 +    /// macro definition, e.g.:
 +    /// ```rust,ignore
 +    /// #[allow(clippy::crate_in_macro_def)]
 +    /// macro_rules! ok { ... crate::foo ... }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CRATE_IN_MACRO_DEF,
 +    suspicious,
 +    "using `crate` in a macro definition"
 +}
 +declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
 +
 +impl EarlyLintPass for CrateInMacroDef {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if_chain! {
 +            if item.attrs.iter().any(is_macro_export);
 +            if let ItemKind::MacroDef(macro_def) = &item.kind;
 +            let tts = macro_def.body.inner_tokens();
 +            if let Some(span) = contains_unhygienic_crate_reference(&tts);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    CRATE_IN_MACRO_DEF,
 +                    span,
 +                    "`crate` references the macro call's crate",
 +                    "to reference the macro definition's crate, use",
 +                    String::from("$crate"),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_macro_export(attr: &Attribute) -> bool {
 +    if_chain! {
 +        if let AttrKind::Normal(attr_item, _) = &attr.kind;
 +        if let [segment] = attr_item.path.segments.as_slice();
 +        then {
 +            segment.ident.name == sym::macro_export
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
 +    let mut prev_is_dollar = false;
 +    let mut cursor = tts.trees();
 +    while let Some(curr) = cursor.next() {
 +        if_chain! {
 +            if !prev_is_dollar;
-         prev_is_dollar = is_token(&curr, &TokenKind::Dollar);
++            if let Some(span) = is_crate_keyword(curr);
 +            if let Some(next) = cursor.look_ahead(0);
 +            if is_token(next, &TokenKind::ModSep);
 +            then {
 +                return Some(span);
 +            }
 +        }
 +        if let TokenTree::Delimited(_, _, tts) = &curr {
 +            let span = contains_unhygienic_crate_reference(tts);
 +            if span.is_some() {
 +                return span;
 +            }
 +        }
++        prev_is_dollar = is_token(curr, &TokenKind::Dollar);
 +    }
 +    None
 +}
 +
 +fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
 +    if_chain! {
 +        if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt;
 +        if symbol.as_str() == "crate";
 +        then { Some(*span) } else { None }
 +    }
 +}
 +
 +fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
 +    if let TokenTree::Token(Token { kind: other, .. }) = tt {
 +        kind == other
 +    } else {
 +        false
 +    }
 +}
index f99d793c201d76c4c20584c48ce98e8af0d48868,0000000000000000000000000000000000000000..17deccf8c3930b4fbc6f4ffc7e1077181345a010
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,100 @@@
- use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{is_in_cfg_test, is_in_test_function};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
- declare_lint_pass!(DbgMacro => [DBG_MACRO]);
++use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of dbg!() macro.
 +    ///
 +    /// ### Why is this bad?
 +    /// `dbg!` macro is intended as a debugging tool. It
 +    /// should not be in version control.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// dbg!(true)
 +    ///
 +    /// // Good
 +    /// true
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub DBG_MACRO,
 +    restriction,
 +    "`dbg!` macro is intended as a debugging tool"
 +}
 +
-             // we make an exception for test code
-             if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
++#[derive(Copy, Clone)]
++pub struct DbgMacro {
++    allow_dbg_in_tests: bool,
++}
++
++impl_lint_pass!(DbgMacro => [DBG_MACRO]);
++
++impl DbgMacro {
++    pub fn new(allow_dbg_in_tests: bool) -> Self {
++        DbgMacro { allow_dbg_in_tests }
++    }
++}
 +
 +impl LateLintPass<'_> for DbgMacro {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
 +        if cx.tcx.is_diagnostic_item(sym::dbg_macro, macro_call.def_id) {
++            // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
++            if self.allow_dbg_in_tests
++                && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id))
++            {
 +                return;
 +            }
 +            let mut applicability = Applicability::MachineApplicable;
 +            let suggestion = match expr.peel_drop_temps().kind {
 +                // dbg!()
 +                ExprKind::Block(_, _) => String::new(),
 +                // dbg!(1)
 +                ExprKind::Match(val, ..) => {
 +                    snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string()
 +                },
 +                // dbg!(2, 3)
 +                ExprKind::Tup(
 +                    [
 +                        Expr {
 +                            kind: ExprKind::Match(first, ..),
 +                            ..
 +                        },
 +                        ..,
 +                        Expr {
 +                            kind: ExprKind::Match(last, ..),
 +                            ..
 +                        },
 +                    ],
 +                ) => {
 +                    let snippet = snippet_with_applicability(
 +                        cx,
 +                        first.span.source_callsite().to(last.span.source_callsite()),
 +                        "..",
 +                        &mut applicability,
 +                    );
 +                    format!("({snippet})")
 +                },
 +                _ => return,
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                DBG_MACRO,
 +                macro_call.span,
 +                "`dbg!` macro is intended as a debugging tool",
 +                "ensure to avoid having uses of it in version control",
 +                suggestion,
 +                applicability,
 +            );
 +        }
 +    }
 +}
index 34a5f8444dea0af0d58a1d221db4a1ca3998999a,0000000000000000000000000000000000000000..e98691fd5bb0ac0988185898c174c4f6372af61c
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,119 @@@
-     /// impl std::default::Default for Foo {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::{is_default_equivalent, peel_blocks};
 +use rustc_hir::{
 +    def::{DefKind, Res},
 +    Body, Expr, ExprKind, GenericArg, Impl, ImplItemKind, Item, ItemKind, Node, PathSegment, QPath, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects manual `std::default::Default` implementations that are identical to a derived implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is less concise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo {
 +    ///     bar: bool
 +    /// }
 +    ///
++    /// impl Default for Foo {
 +    ///     fn default() -> Self {
 +    ///         Self {
 +    ///             bar: false
 +    ///         }
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    ///
 +    /// ```rust
 +    /// #[derive(Default)]
 +    /// struct Foo {
 +    ///     bar: bool
 +    /// }
 +    /// ```
 +    ///
 +    /// ### Known problems
 +    /// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
 +    /// in generic types and the user defined `impl` maybe is more generalized or
 +    /// specialized than what derive will produce. This lint can't detect the manual `impl`
 +    /// has exactly equal bounds, and therefore this lint is disabled for types with
 +    /// generic parameters.
 +    ///
 +    #[clippy::version = "1.57.0"]
 +    pub DERIVABLE_IMPLS,
 +    complexity,
 +    "manual implementation of the `Default` trait which is equal to a derive"
 +}
 +
 +declare_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
 +
 +fn is_path_self(e: &Expr<'_>) -> bool {
 +    if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
 +        matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
 +    } else {
 +        false
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if_chain! {
 +            if let ItemKind::Impl(Impl {
 +                of_trait: Some(ref trait_ref),
 +                items: [child],
 +                self_ty,
 +                ..
 +            }) = item.kind;
 +            if !cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
 +            if !item.span.from_expansion();
 +            if let Some(def_id) = trait_ref.trait_def_id();
 +            if cx.tcx.is_diagnostic_item(sym::Default, def_id);
 +            if let impl_item_hir = child.id.hir_id();
 +            if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir);
 +            if let ImplItemKind::Fn(_, b) = &impl_item.kind;
 +            if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b);
 +            if let Some(adt_def) = cx.tcx.type_of(item.def_id).ty_adt_def();
 +            if let attrs = cx.tcx.hir().attrs(item.hir_id());
 +            if !attrs.iter().any(|attr| attr.doc_str().is_some());
 +            if let child_attrs = cx.tcx.hir().attrs(impl_item_hir);
 +            if !child_attrs.iter().any(|attr| attr.doc_str().is_some());
 +            if adt_def.is_struct();
 +            then {
 +                if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind {
 +                    if let Some(PathSegment { args: Some(a), .. }) = p.segments.last() {
 +                        for arg in a.args {
 +                            if !matches!(arg, GenericArg::Lifetime(_)) {
 +                                return;
 +                            }
 +                        }
 +                    }
 +                }
 +                let should_emit = match peel_blocks(func_expr).kind {
 +                    ExprKind::Tup(fields) => fields.iter().all(|e| is_default_equivalent(cx, e)),
 +                    ExprKind::Call(callee, args)
 +                        if is_path_self(callee) => args.iter().all(|e| is_default_equivalent(cx, e)),
 +                    ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_equivalent(cx, ef.expr)),
 +                    _ => false,
 +                };
 +                if should_emit {
 +                    let path_string = cx.tcx.def_path_str(adt_def.did());
 +                    span_lint_and_help(
 +                        cx,
 +                        DERIVABLE_IMPLS,
 +                        item.span,
 +                        "this `impl` can be derived",
 +                        None,
 +                        &format!("try annotating `{}` with `#[derive(Default)]`", path_string),
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index fe99f4a8d55d1e359473da38289fb397400cd490,0000000000000000000000000000000000000000..99347ebadc60277e7249796cd1048e1cafa388ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,488 -1,0 +1,517 @@@
- use clippy_utils::ty::{implements_trait, is_copy};
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::paths;
-     BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
++use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 +use clippy_utils::{is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 +use rustc_hir::{
- use rustc_middle::ty::{self, Ty};
++    self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource, Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
-     trait_ref: &TraitRef<'_>,
++use rustc_middle::ty::subst::GenericArg;
++use rustc_middle::ty::{self, BoundConstness, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, 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 deriving `Hash` but implementing `PartialEq`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `HashMap`) so it’s probably a bad idea to use a
 +    /// default-generated `Hash` implementation with an explicitly defined
 +    /// `PartialEq`. In particular, the following must hold for any type:
 +    ///
 +    /// ```text
 +    /// k1 == k2 ⇒ hash(k1) == hash(k2)
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[derive(Hash)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialEq for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DERIVE_HASH_XOR_EQ,
 +    correctness,
 +    "deriving `Hash` but implementing `PartialEq` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Ord` but implementing `PartialOrd`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `sort`) so it’s probably a bad idea to use a
 +    /// default-generated `Ord` implementation with an explicitly defined
 +    /// `PartialOrd`. In particular, the following must hold for any type
 +    /// implementing `Ord`:
 +    ///
 +    /// ```text
 +    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
 +    ///        Some(self.cmp(other))
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Ord for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// or, if you don't need a custom ordering:
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
 +    /// struct Foo;
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub DERIVE_ORD_XOR_PARTIAL_ORD,
 +    correctness,
 +    "deriving `Ord` but implementing `PartialOrd` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `Clone` implementations for `Copy`
 +    /// types.
 +    ///
 +    /// ### Why is this bad?
 +    /// To avoid surprising behavior, these traits should
 +    /// agree and the behavior of `Copy` cannot be overridden. In almost all
 +    /// situations a `Copy` type should have a `Clone` implementation that does
 +    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
 +    /// gets you.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Copy)]
 +    /// struct Foo;
 +    ///
 +    /// impl Clone for Foo {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPL_IMPL_CLONE_ON_COPY,
 +    pedantic,
 +    "implementing `Clone` explicitly on `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `serde::Deserialize` on a type that
 +    /// has methods using `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Deriving `serde::Deserialize` will create a constructor
 +    /// that may violate invariants hold by another constructor.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use serde::Deserialize;
 +    ///
 +    /// #[derive(Deserialize)]
 +    /// pub struct Foo {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         // setup here ..
 +    ///     }
 +    ///
 +    ///     pub unsafe fn parts() -> (&str, &str) {
 +    ///         // assumes invariants hold
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types that derive `PartialEq` and could implement `Eq`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
 +    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
 +    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
 +    /// `Eq` themselves.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[derive(PartialEq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    style,
 +    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE,
 +    DERIVE_PARTIAL_EQ_WITHOUT_EQ
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Derive {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(ref trait_ref),
 +            ..
 +        }) = item.kind
 +        {
 +            let ty = cx.tcx.type_of(item.def_id);
 +            let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
 +
 +            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
 +            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 +
 +            if is_automatically_derived {
 +                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
 +                check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
 +            } else {
 +                check_copy_clone(cx, item, trait_ref, ty);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
 +fn check_hash_peq<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
-     trait_ref: &TraitRef<'_>,
++    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    hash_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
 +        then {
 +            // Look for the PartialEq implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
 +                let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
- fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
++    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
 +        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
 +        if let Some(def_id) = &trait_ref.trait_def_id();
 +        if *def_id == ord_trait_def_id;
 +        then {
 +            // Look for the PartialOrd implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
 +                let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
-     trait_ref: &TraitRef<'_>,
++fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    let clone_id = match cx.tcx.lang_items().clone_trait() {
 +        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
 +        _ => return,
 +    };
 +    let copy_id = match cx.tcx.lang_items().copy_trait() {
 +        Some(id) => id,
 +        None => return,
 +    };
 +    let (ty_adt, ty_subs) = match *ty.kind() {
 +        // Unions can't derive clone.
 +        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
 +        _ => return,
 +    };
 +    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
 +    // there's a Copy impl for any instance of the adt.
 +    if !is_copy(cx, ty) {
 +        if ty_subs.non_erasable_generics().next().is_some() {
 +            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
 +                impls
 +                    .iter()
 +                    .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
 +            });
 +            if !has_copy_impl {
 +                return;
 +            }
 +        } else {
 +            return;
 +        }
 +    }
 +    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
 +    // this impl.
 +    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
 +        return;
 +    }
 +
 +    span_lint_and_note(
 +        cx,
 +        EXPL_IMPL_CLONE_ON_COPY,
 +        item.span,
 +        "you are implementing `Clone` explicitly on a `Copy` type",
 +        Some(item.span),
 +        "consider deriving `Clone` or removing `Copy`",
 +    );
 +}
 +
 +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
 +fn check_unsafe_derive_deserialize<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item: &Item<'_>,
- fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &TraitRef<'_>, ty: Ty<'tcx>) {
++    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
 +        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
 +        walk_item(&mut visitor, item);
 +        visitor.has_unsafe
 +    }
 +
 +    if_chain! {
 +        if let Some(trait_def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if let Some(local_def_id) = def.did().as_local();
 +        let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +        if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
 +        if cx.tcx.inherent_impls(def.did())
 +            .iter()
 +            .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
 +            .any(|imp| has_unsafe(cx, imp));
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                UNSAFE_DERIVE_DESERIALIZE,
 +                item.span,
 +                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
 +                None,
 +                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
 +            );
 +        }
 +    }
 +}
 +
 +struct UnsafeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    has_unsafe: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.unsafety == Unsafety::Unsafe;
 +            then {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_fn(self, kind, decl, body_id, span, id);
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
-         if !implements_trait(cx, ty, eq_trait_def_id, substs);
++fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::Adt(adt, substs) = ty.kind();
 +        if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
++        if let Some(peq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::PartialEq);
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
++        // New `ParamEnv` replacing `T: PartialEq` with `T: Eq`
++        let param_env = ParamEnv::new(
++            cx.tcx.mk_predicates(cx.param_env.caller_bounds().iter().map(|p| {
++                let kind = p.kind();
++                match kind.skip_binder() {
++                    PredicateKind::Trait(p)
++                        if p.trait_ref.def_id == peq_trait_def_id
++                            && p.trait_ref.substs.get(0) == p.trait_ref.substs.get(1)
++                            && matches!(p.trait_ref.self_ty().kind(), ty::Param(_))
++                            && p.constness == BoundConstness::NotConst
++                            && p.polarity == ImplPolarity::Positive =>
++                    {
++                        cx.tcx.mk_predicate(kind.rebind(PredicateKind::Trait(TraitPredicate {
++                            trait_ref: TraitRef::new(
++                                eq_trait_def_id,
++                                cx.tcx.mk_substs([GenericArg::from(p.trait_ref.self_ty())].into_iter()),
++                            ),
++                            constness: BoundConstness::NotConst,
++                            polarity: ImplPolarity::Positive,
++                        })))
++                    },
++                    _ => p,
++                }
++            })),
++            cx.param_env.reveal(),
++            cx.param_env.constness(),
++        );
++        if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, substs);
 +        then {
 +            // If all of our fields implement `Eq`, we can implement `Eq` too
 +            for variant in adt.variants() {
 +                for field in &variant.fields {
 +                    let ty = field.ty(cx.tcx, substs);
 +
 +                    if !implements_trait(cx, ty, eq_trait_def_id, substs) {
 +                        return;
 +                    }
 +                }
 +            }
 +
 +            span_lint_and_sugg(
 +                cx,
 +                DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +                span.ctxt().outer_expn_data().call_site,
 +                "you are deriving `PartialEq` and can implement `Eq`",
 +                "consider deriving `Eq` as well",
 +                "PartialEq, Eq".to_string(),
 +                Applicability::MachineApplicable,
 +            )
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb07f57e87006ff95368b8619b4bb559f983bc8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++use clippy_utils::diagnostics::span_lint;
++use itertools::Itertools;
++use rustc_ast::{AttrKind, Attribute};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++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.60.0"]
++    pub DOC_LINK_WITH_QUOTES,
++    pedantic,
++    "possible typo for an intra-doc link"
++}
++declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);
++
++impl EarlyLintPass for DocLinkWithQuotes {
++    fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
++        if let AttrKind::DocComment(_, symbol) = attr.kind {
++            if contains_quote_link(symbol.as_str()) {
++                span_lint(
++                    ctx,
++                    DOC_LINK_WITH_QUOTES,
++                    attr.span,
++                    "possible intra-doc link using quotes instead of backticks",
++                );
++            }
++        }
++    }
++}
++
++fn contains_quote_link(s: &str) -> bool {
++    let mut in_backticks = false;
++    let mut found_opening = false;
++
++    for c in s.chars().tuple_windows::<(char, char)>() {
++        match c {
++            ('`', _) => in_backticks = !in_backticks,
++            ('[', '\'') if !in_backticks => found_opening = true,
++            ('\'', ']') if !in_backticks && found_opening => return true,
++            _ => {},
++        }
++    }
++
++    false
++}
index be95375789d5b4663ac7ed7212e9e16bf195b9c0,0000000000000000000000000000000000000000..ee0440e52ff85a1ec3bb65d8c88d4801348fa2d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,96 @@@
-     /// Could be written as:
 +//! Lint on unnecessary double comparisons. Some examples:
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::eq_expr_value;
 +use clippy_utils::source::snippet_with_applicability;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for double comparisons that could be simplified to a single expression.
 +    ///
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// # let y = 2;
 +    /// if x == y || x < y {}
 +    /// ```
 +    ///
++    /// Use instead:
 +    ///
 +    /// ```rust
 +    /// # let x = 1;
 +    /// # let y = 2;
 +    /// if x <= y {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOUBLE_COMPARISONS,
 +    complexity,
 +    "unnecessary double comparisons that can be simplified"
 +}
 +
 +declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]);
 +
 +impl<'tcx> DoubleComparisons {
 +    #[expect(clippy::similar_names)]
 +    fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
 +        let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
 +            (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
 +                (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
 +            },
 +            _ => return,
 +        };
 +        if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
 +            return;
 +        }
 +        macro_rules! lint_double_comparison {
 +            ($op:tt) => {{
 +                let mut applicability = Applicability::MachineApplicable;
 +                let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
 +                let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
 +                let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
 +                span_lint_and_sugg(
 +                    cx,
 +                    DOUBLE_COMPARISONS,
 +                    span,
 +                    "this binary expression can be simplified",
 +                    "try",
 +                    sugg,
 +                    applicability,
 +                );
 +            }};
 +        }
 +        #[rustfmt::skip]
 +        match (op, lkind, rkind) {
 +            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
 +                lint_double_comparison!(<=);
 +            },
 +            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
 +                lint_double_comparison!(>=);
 +            },
 +            (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
 +                lint_double_comparison!(!=);
 +            },
 +            (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
 +                lint_double_comparison!(==);
 +            },
 +            _ => (),
 +        };
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for DoubleComparisons {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind {
 +            Self::check_binop(cx, kind.node, lhs, rhs, expr.span);
 +        }
 +    }
 +}
index e10f740d24a4193d4c141b7c1f1e96a12239353f,0000000000000000000000000000000000000000..a33ef5ce6e37c5a30b4c9586ffe5590bfba66235
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,75 @@@
-     /// // Bad
 +use clippy_utils::diagnostics::span_lint;
 +use rustc_ast::ast::{Expr, ExprKind};
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary double parentheses.
 +    ///
 +    /// ### Why is this bad?
 +    /// This makes code harder to read and might indicate a
 +    /// mistake.
 +    ///
 +    /// ### Example
 +    /// ```rust
-     /// // Good
 +    /// fn simple_double_parens() -> i32 {
 +    ///     ((0))
 +    /// }
 +    ///
-     /// // or
-     ///
++    /// # fn foo(bar: usize) {}
++    /// foo((0));
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
 +    /// fn simple_no_parens() -> i32 {
 +    ///     0
 +    /// }
 +    ///
-     /// // Bad
-     /// foo((0));
-     ///
-     /// // Good
 +    /// # fn foo(bar: usize) {}
 +    /// foo(0);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DOUBLE_PARENS,
 +    complexity,
 +    "Warn on unnecessary double parentheses"
 +}
 +
 +declare_lint_pass!(DoubleParens => [DOUBLE_PARENS]);
 +
 +impl EarlyLintPass for DoubleParens {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        let msg: &str = "consider removing unnecessary double parentheses";
 +
 +        match expr.kind {
 +            ExprKind::Paren(ref in_paren) => match in_paren.kind {
 +                ExprKind::Paren(_) | ExprKind::Tup(_) => {
 +                    span_lint(cx, DOUBLE_PARENS, expr.span, msg);
 +                },
 +                _ => {},
 +            },
 +            ExprKind::Call(_, ref params) => {
 +                if params.len() == 1 {
 +                    let param = &params[0];
 +                    if let ExprKind::Paren(_) = param.kind {
 +                        span_lint(cx, DOUBLE_PARENS, param.span, msg);
 +                    }
 +                }
 +            },
 +            ExprKind::MethodCall(_, ref params, _) => {
 +                if params.len() == 2 {
 +                    let param = &params[1];
 +                    if let ExprKind::Paren(_) = param.kind {
 +                        span_lint(cx, DOUBLE_PARENS, param.span, msg);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
index 09318f74527c2bf16485add7c10f1cf70981f30e,0000000000000000000000000000000000000000..d85ace3a279b3b8e4b9d586301c06c75222f5246
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,75 @@@
-     /// let dur = Duration::new(5, 0);
-     ///
-     /// // Bad
-     /// let _micros = dur.subsec_nanos() / 1_000;
-     /// let _millis = dur.subsec_nanos() / 1_000_000;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calculation of subsecond microseconds or milliseconds
 +    /// from other `Duration` methods.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more concise to call `Duration::subsec_micros()` or
 +    /// `Duration::subsec_millis()` than to calculate them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::time::Duration;
-     /// // Good
-     /// let _micros = dur.subsec_micros();
-     /// let _millis = dur.subsec_millis();
++    /// # let duration = Duration::new(5, 0);
++    /// let micros = duration.subsec_nanos() / 1_000;
++    /// let millis = duration.subsec_nanos() / 1_000_000;
++    /// ```
 +    ///
++    /// Use instead:
++    /// ```rust
++    /// # use std::time::Duration;
++    /// # let duration = Duration::new(5, 0);
++    /// let micros = duration.subsec_micros();
++    /// let millis = duration.subsec_millis();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DURATION_SUBSEC,
 +    complexity,
 +    "checks for calculation of subsecond microseconds or milliseconds"
 +}
 +
 +declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind;
 +            if let ExprKind::MethodCall(method_path, args, _) = left.kind;
 +            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::Duration);
 +            if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
 +            then {
 +                let suggested_fn = match (method_path.ident.as_str(), divisor) {
 +                    ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
 +                    ("subsec_nanos", 1_000) => "subsec_micros",
 +                    _ => return,
 +                };
 +                let mut applicability = Applicability::MachineApplicable;
 +                span_lint_and_sugg(
 +                    cx,
 +                    DURATION_SUBSEC,
 +                    expr.span,
 +                    &format!("calling `{}()` is more concise than this calculation", suggested_fn),
 +                    "try",
 +                    format!(
 +                        "{}.{}()",
 +                        snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
 +                        suggested_fn
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index 0b9f54231c59bc8566e06b8599ec04e9242893ac,0000000000000000000000000000000000000000..bf4488570eaf21d190f3c2d252098103070bfaaa
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -1,0 +1,72 @@@
-     /// Could be written:
 +//! Lint on if expressions with an else if, but without a final else branch.
 +
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_ast::ast::{Expr, ExprKind};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of if expressions with an `else if` branch,
 +    /// but without a final `else` branch.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # let x: i32 = 1;
 +    /// if x.is_positive() {
 +    ///     a();
 +    /// } else if x.is_negative() {
 +    ///     b();
 +    /// }
 +    /// ```
 +    ///
++    /// Use instead:
 +    ///
 +    /// ```rust
 +    /// # fn a() {}
 +    /// # fn b() {}
 +    /// # let x: i32 = 1;
 +    /// if x.is_positive() {
 +    ///     a();
 +    /// } else if x.is_negative() {
 +    ///     b();
 +    /// } else {
 +    ///     // We don't care about zero.
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ELSE_IF_WITHOUT_ELSE,
 +    restriction,
 +    "`if` expression with an `else if`, but without a final `else` branch"
 +}
 +
 +declare_lint_pass!(ElseIfWithoutElse => [ELSE_IF_WITHOUT_ELSE]);
 +
 +impl EarlyLintPass for ElseIfWithoutElse {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, mut item: &Expr) {
 +        if in_external_macro(cx.sess(), item.span) {
 +            return;
 +        }
 +
 +        while let ExprKind::If(_, _, Some(ref els)) = item.kind {
 +            if let ExprKind::If(_, _, None) = els.kind {
 +                span_lint_and_help(
 +                    cx,
 +                    ELSE_IF_WITHOUT_ELSE,
 +                    els.span,
 +                    "`if` expression with an `else if`, but without a final `else`",
 +                    None,
 +                    "add an `else` block here",
 +                );
 +            }
 +
 +            item = els;
 +        }
 +    }
 +}
index b5d6b3c7524baad8f64bea9069054624c990707e,0000000000000000000000000000000000000000..bbebc02441412aa5ab36a72d8a9574ccea8c58fc
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,67 @@@
-     /// Bad:
 +//! lint when there is an enum with no variants
 +
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `enum`s with no variants.
 +    ///
 +    /// As of this writing, the `never_type` is still a
 +    /// nightly-only experimental API. Therefore, this lint is only triggered
 +    /// if the `never_type` is enabled.
 +    ///
 +    /// ### Why is this bad?
 +    /// If you want to introduce a type which
 +    /// can't be instantiated, you should use `!` (the primitive type "never"),
 +    /// or a wrapper around it, because `!` has more extensive
 +    /// compiler support (type inference, etc...) and wrappers
 +    /// around it are the conventional way to define an uninhabited type.
 +    /// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
 +    ///
 +    ///
 +    /// ### Example
-     /// Good:
 +    /// ```rust
 +    /// enum Test {}
 +    /// ```
 +    ///
++    /// Use instead:
 +    /// ```rust
 +    /// #![feature(never_type)]
 +    ///
 +    /// struct Test(!);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EMPTY_ENUM,
 +    pedantic,
 +    "enum with no variants"
 +}
 +
 +declare_lint_pass!(EmptyEnum => [EMPTY_ENUM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EmptyEnum {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        // Only suggest the `never_type` if the feature is enabled
 +        if !cx.tcx.features().never_type {
 +            return;
 +        }
 +
 +        if let ItemKind::Enum(..) = item.kind {
 +            let ty = cx.tcx.type_of(item.def_id);
 +            let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
 +            if adt.variants().is_empty() {
 +                span_lint_and_help(
 +                    cx,
 +                    EMPTY_ENUM,
 +                    item.span,
 +                    "enum with no variants",
 +                    None,
 +                    "consider using the uninhabited type `!` (never type) or a wrapper \
 +                    around it to introduce a type which can't be instantiated",
 +                );
 +            }
 +        }
 +    }
 +}
index c5a987842c3f186cfc4119f658a890f1d60846ce,0000000000000000000000000000000000000000..27743a0ebec7e59dc688de5547043a4b2b60e95b
mode 100644,000000..100644
--- /dev/null
@@@ -1,658 -1,0 +1,658 @@@
-     /// can both be rewritten as:
 +use clippy_utils::higher;
 +use clippy_utils::{
 +    can_move_expr_to_closure_no_visit,
 +    diagnostics::span_lint_and_sugg,
 +    is_expr_final_block_expr, is_expr_used_or_unified, match_def_path, paths, peel_hir_expr_while,
 +    source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
 +    SpanlessEq,
 +};
 +use core::fmt::Write;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    hir_id::HirIdSet,
 +    intravisit::{walk_expr, Visitor},
 +    Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{Span, SyntaxContext, DUMMY_SP};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of `contains_key` + `insert` on `HashMap`
 +    /// or `BTreeMap`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `entry` is more efficient.
 +    ///
 +    /// ### Known problems
 +    /// The suggestion may have type inference errors in some cases. e.g.
 +    /// ```rust
 +    /// let mut map = std::collections::HashMap::new();
 +    /// let _ = if !map.contains_key(&0) {
 +    ///     map.insert(0, 0)
 +    /// } else {
 +    ///     None
 +    /// };
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # let mut map = HashMap::new();
 +    /// # let k = 1;
 +    /// # let v = 1;
 +    /// if !map.contains_key(&k) {
 +    ///     map.insert(k, v);
 +    /// }
 +    /// ```
++    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashMap;
 +    /// # let mut map = HashMap::new();
 +    /// # let k = 1;
 +    /// # let v = 1;
 +    /// map.entry(k).or_insert(v);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MAP_ENTRY,
 +    perf,
 +    "use of `contains_key` followed by `insert` on a `HashMap` or `BTreeMap`"
 +}
 +
 +declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for HashMapPass {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
 +            Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
 +            _ => return,
 +        };
 +
 +        let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
 +            Some(x) => x,
 +            None => return,
 +        };
 +
 +        let then_search = match find_insert_calls(cx, &contains_expr, then_expr) {
 +            Some(x) => x,
 +            None => return,
 +        };
 +
 +        let mut app = Applicability::MachineApplicable;
 +        let map_str = snippet_with_context(cx, contains_expr.map.span, contains_expr.call_ctxt, "..", &mut app).0;
 +        let key_str = snippet_with_context(cx, contains_expr.key.span, contains_expr.call_ctxt, "..", &mut app).0;
 +        let sugg = if let Some(else_expr) = else_expr {
 +            let else_search = match find_insert_calls(cx, &contains_expr, else_expr) {
 +                Some(search) => search,
 +                None => return,
 +            };
 +
 +            if then_search.edits.is_empty() && else_search.edits.is_empty() {
 +                // No insertions
 +                return;
 +            } else if then_search.edits.is_empty() || else_search.edits.is_empty() {
 +                // if .. { insert } else { .. } or if .. { .. } else { insert }
 +                let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
 +                    (true, true) => (
 +                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
 +                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
 +                    ),
 +                    (true, false) => (
 +                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
 +                        snippet_with_applicability(cx, else_expr.span, "{ .. }", &mut app),
 +                    ),
 +                    (false, true) => (
 +                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
 +                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
 +                    ),
 +                    (false, false) => (
 +                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
 +                        snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
 +                    ),
 +                };
 +                format!(
 +                    "if let {}::{} = {}.entry({}) {} else {}",
 +                    map_ty.entry_path(),
 +                    entry_kind,
 +                    map_str,
 +                    key_str,
 +                    then_str,
 +                    else_str,
 +                )
 +            } else {
 +                // if .. { insert } else { insert }
 +                let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
 +                    (
 +                        then_search.snippet_vacant(cx, then_expr.span, &mut app),
 +                        else_search.snippet_occupied(cx, else_expr.span, &mut app),
 +                    )
 +                } else {
 +                    (
 +                        then_search.snippet_occupied(cx, then_expr.span, &mut app),
 +                        else_search.snippet_vacant(cx, else_expr.span, &mut app),
 +                    )
 +                };
 +                let indent_str = snippet_indent(cx, expr.span);
 +                let indent_str = indent_str.as_deref().unwrap_or("");
 +                format!(
 +                    "match {}.entry({}) {{\n{indent}    {entry}::{} => {}\n\
 +                        {indent}    {entry}::{} => {}\n{indent}}}",
 +                    map_str,
 +                    key_str,
 +                    then_entry,
 +                    reindent_multiline(then_str.into(), true, Some(4 + indent_str.len())),
 +                    else_entry,
 +                    reindent_multiline(else_str.into(), true, Some(4 + indent_str.len())),
 +                    entry = map_ty.entry_path(),
 +                    indent = indent_str,
 +                )
 +            }
 +        } else {
 +            if then_search.edits.is_empty() {
 +                // no insertions
 +                return;
 +            }
 +
 +            // if .. { insert }
 +            if !then_search.allow_insert_closure {
 +                let (body_str, entry_kind) = if contains_expr.negated {
 +                    then_search.snippet_vacant(cx, then_expr.span, &mut app)
 +                } else {
 +                    then_search.snippet_occupied(cx, then_expr.span, &mut app)
 +                };
 +                format!(
 +                    "if let {}::{} = {}.entry({}) {}",
 +                    map_ty.entry_path(),
 +                    entry_kind,
 +                    map_str,
 +                    key_str,
 +                    body_str,
 +                )
 +            } else if let Some(insertion) = then_search.as_single_insertion() {
 +                let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
 +                if contains_expr.negated {
 +                    if insertion.value.can_have_side_effects() {
 +                        format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, value_str)
 +                    } else {
 +                        format!("{}.entry({}).or_insert({});", map_str, key_str, value_str)
 +                    }
 +                } else {
 +                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
 +                    // This would need to be a different lint.
 +                    return;
 +                }
 +            } else {
 +                let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
 +                if contains_expr.negated {
 +                    format!("{}.entry({}).or_insert_with(|| {});", map_str, key_str, block_str)
 +                } else {
 +                    // TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
 +                    // This would need to be a different lint.
 +                    return;
 +                }
 +            }
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            MAP_ENTRY,
 +            expr.span,
 +            &format!("usage of `contains_key` followed by `insert` on a `{}`", map_ty.name()),
 +            "try this",
 +            sugg,
 +            app,
 +        );
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum MapType {
 +    Hash,
 +    BTree,
 +}
 +impl MapType {
 +    fn name(self) -> &'static str {
 +        match self {
 +            Self::Hash => "HashMap",
 +            Self::BTree => "BTreeMap",
 +        }
 +    }
 +    fn entry_path(self) -> &'static str {
 +        match self {
 +            Self::Hash => "std::collections::hash_map::Entry",
 +            Self::BTree => "std::collections::btree_map::Entry",
 +        }
 +    }
 +}
 +
 +struct ContainsExpr<'tcx> {
 +    negated: bool,
 +    map: &'tcx Expr<'tcx>,
 +    key: &'tcx Expr<'tcx>,
 +    call_ctxt: SyntaxContext,
 +}
 +fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> {
 +    let mut negated = false;
 +    let expr = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::Unary(UnOp::Not, e) => {
 +            negated = !negated;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    match expr.kind {
 +        ExprKind::MethodCall(
 +            _,
 +            [
 +                map,
 +                Expr {
 +                    kind: ExprKind::AddrOf(_, _, key),
 +                    span: key_span,
 +                    ..
 +                },
 +            ],
 +            _,
 +        ) if key_span.ctxt() == expr.span.ctxt() => {
 +            let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
 +            let expr = ContainsExpr {
 +                negated,
 +                map,
 +                key,
 +                call_ctxt: expr.span.ctxt(),
 +            };
 +            if match_def_path(cx, id, &paths::BTREEMAP_CONTAINS_KEY) {
 +                Some((MapType::BTree, expr))
 +            } else if match_def_path(cx, id, &paths::HASHMAP_CONTAINS_KEY) {
 +                Some((MapType::Hash, expr))
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +struct InsertExpr<'tcx> {
 +    map: &'tcx Expr<'tcx>,
 +    key: &'tcx Expr<'tcx>,
 +    value: &'tcx Expr<'tcx>,
 +}
 +fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
 +    if let ExprKind::MethodCall(_, [map, key, value], _) = expr.kind {
 +        let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
 +        if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) {
 +            Some(InsertExpr { map, key, value })
 +        } else {
 +            None
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// An edit that will need to be made to move the expression to use the entry api
 +#[derive(Clone, Copy)]
 +enum Edit<'tcx> {
 +    /// A semicolon that needs to be removed. Used to create a closure for `insert_with`.
 +    RemoveSemi(Span),
 +    /// An insertion into the map.
 +    Insertion(Insertion<'tcx>),
 +}
 +impl<'tcx> Edit<'tcx> {
 +    fn as_insertion(self) -> Option<Insertion<'tcx>> {
 +        if let Self::Insertion(i) = self { Some(i) } else { None }
 +    }
 +}
 +#[derive(Clone, Copy)]
 +struct Insertion<'tcx> {
 +    call: &'tcx Expr<'tcx>,
 +    value: &'tcx Expr<'tcx>,
 +}
 +
 +/// This visitor needs to do a multiple things:
 +/// * Find all usages of the map. An insertion can only be made before any other usages of the map.
 +/// * Determine if there's an insertion using the same key. There's no need for the entry api
 +///   otherwise.
 +/// * Determine if the final statement executed is an insertion. This is needed to use
 +///   `or_insert_with`.
 +/// * Determine if there's any sub-expression that can't be placed in a closure.
 +/// * Determine if there's only a single insert statement. `or_insert` can be used in this case.
 +#[expect(clippy::struct_excessive_bools)]
 +struct InsertSearcher<'cx, 'tcx> {
 +    cx: &'cx LateContext<'tcx>,
 +    /// The map expression used in the contains call.
 +    map: &'tcx Expr<'tcx>,
 +    /// The key expression used in the contains call.
 +    key: &'tcx Expr<'tcx>,
 +    /// The context of the top level block. All insert calls must be in the same context.
 +    ctxt: SyntaxContext,
 +    /// Whether this expression can be safely moved into a closure.
 +    allow_insert_closure: bool,
 +    /// Whether this expression can use the entry api.
 +    can_use_entry: bool,
 +    /// Whether this expression is the final expression in this code path. This may be a statement.
 +    in_tail_pos: bool,
 +    // Is this expression a single insert. A slightly better suggestion can be made in this case.
 +    is_single_insert: bool,
 +    /// If the visitor has seen the map being used.
 +    is_map_used: bool,
 +    /// The locations where changes need to be made for the suggestion.
 +    edits: Vec<Edit<'tcx>>,
 +    /// A stack of loops the visitor is currently in.
 +    loops: Vec<HirId>,
 +    /// Local variables created in the expression. These don't need to be captured.
 +    locals: HirIdSet,
 +}
 +impl<'tcx> InsertSearcher<'_, 'tcx> {
 +    /// Visit the expression as a branch in control flow. Multiple insert calls can be used, but
 +    /// only if they are on separate code paths. This will return whether the map was used in the
 +    /// given expression.
 +    fn visit_cond_arm(&mut self, e: &'tcx Expr<'_>) -> bool {
 +        let is_map_used = self.is_map_used;
 +        let in_tail_pos = self.in_tail_pos;
 +        self.visit_expr(e);
 +        let res = self.is_map_used;
 +        self.is_map_used = is_map_used;
 +        self.in_tail_pos = in_tail_pos;
 +        res
 +    }
 +
 +    /// Visits an expression which is not itself in a tail position, but other sibling expressions
 +    /// may be. e.g. if conditions
 +    fn visit_non_tail_expr(&mut self, e: &'tcx Expr<'_>) {
 +        let in_tail_pos = self.in_tail_pos;
 +        self.in_tail_pos = false;
 +        self.visit_expr(e);
 +        self.in_tail_pos = in_tail_pos;
 +    }
 +}
 +impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
 +    fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
 +        match stmt.kind {
 +            StmtKind::Semi(e) => {
 +                self.visit_expr(e);
 +
 +                if self.in_tail_pos && self.allow_insert_closure {
 +                    // The spans are used to slice the top level expression into multiple parts. This requires that
 +                    // they all come from the same part of the source code.
 +                    if stmt.span.ctxt() == self.ctxt && e.span.ctxt() == self.ctxt {
 +                        self.edits
 +                            .push(Edit::RemoveSemi(stmt.span.trim_start(e.span).unwrap_or(DUMMY_SP)));
 +                    } else {
 +                        self.allow_insert_closure = false;
 +                    }
 +                }
 +            },
 +            StmtKind::Expr(e) => self.visit_expr(e),
 +            StmtKind::Local(l) => {
 +                self.visit_pat(l.pat);
 +                if let Some(e) = l.init {
 +                    self.allow_insert_closure &= !self.in_tail_pos;
 +                    self.in_tail_pos = false;
 +                    self.is_single_insert = false;
 +                    self.visit_expr(e);
 +                }
 +            },
 +            StmtKind::Item(_) => {
 +                self.allow_insert_closure &= !self.in_tail_pos;
 +                self.is_single_insert = false;
 +            },
 +        }
 +    }
 +
 +    fn visit_block(&mut self, block: &'tcx Block<'_>) {
 +        // If the block is in a tail position, then the last expression (possibly a statement) is in the
 +        // tail position. The rest, however, are not.
 +        match (block.stmts, block.expr) {
 +            ([], None) => {
 +                self.allow_insert_closure &= !self.in_tail_pos;
 +            },
 +            ([], Some(expr)) => self.visit_expr(expr),
 +            (stmts, Some(expr)) => {
 +                let in_tail_pos = self.in_tail_pos;
 +                self.in_tail_pos = false;
 +                for stmt in stmts {
 +                    self.visit_stmt(stmt);
 +                }
 +                self.in_tail_pos = in_tail_pos;
 +                self.visit_expr(expr);
 +            },
 +            ([stmts @ .., stmt], None) => {
 +                let in_tail_pos = self.in_tail_pos;
 +                self.in_tail_pos = false;
 +                for stmt in stmts {
 +                    self.visit_stmt(stmt);
 +                }
 +                self.in_tail_pos = in_tail_pos;
 +                self.visit_stmt(stmt);
 +            },
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if !self.can_use_entry {
 +            return;
 +        }
 +
 +        match try_parse_insert(self.cx, expr) {
 +            Some(insert_expr) if SpanlessEq::new(self.cx).eq_expr(self.map, insert_expr.map) => {
 +                // Multiple inserts, inserts with a different key, and inserts from a macro can't use the entry api.
 +                if self.is_map_used
 +                    || !SpanlessEq::new(self.cx).eq_expr(self.key, insert_expr.key)
 +                    || expr.span.ctxt() != self.ctxt
 +                {
 +                    self.can_use_entry = false;
 +                    return;
 +                }
 +
 +                self.edits.push(Edit::Insertion(Insertion {
 +                    call: expr,
 +                    value: insert_expr.value,
 +                }));
 +                self.is_map_used = true;
 +                self.allow_insert_closure &= self.in_tail_pos;
 +
 +                // The value doesn't affect whether there is only a single insert expression.
 +                let is_single_insert = self.is_single_insert;
 +                self.visit_non_tail_expr(insert_expr.value);
 +                self.is_single_insert = is_single_insert;
 +            },
 +            _ if SpanlessEq::new(self.cx).eq_expr(self.map, expr) => {
 +                self.is_map_used = true;
 +            },
 +            _ => match expr.kind {
 +                ExprKind::If(cond_expr, then_expr, Some(else_expr)) => {
 +                    self.is_single_insert = false;
 +                    self.visit_non_tail_expr(cond_expr);
 +                    // Each branch may contain it's own insert expression.
 +                    let mut is_map_used = self.visit_cond_arm(then_expr);
 +                    is_map_used |= self.visit_cond_arm(else_expr);
 +                    self.is_map_used = is_map_used;
 +                },
 +                ExprKind::Match(scrutinee_expr, arms, _) => {
 +                    self.is_single_insert = false;
 +                    self.visit_non_tail_expr(scrutinee_expr);
 +                    // Each branch may contain it's own insert expression.
 +                    let mut is_map_used = self.is_map_used;
 +                    for arm in arms {
 +                        self.visit_pat(arm.pat);
 +                        if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard {
 +                            self.visit_non_tail_expr(guard);
 +                        }
 +                        is_map_used |= self.visit_cond_arm(arm.body);
 +                    }
 +                    self.is_map_used = is_map_used;
 +                },
 +                ExprKind::Loop(block, ..) => {
 +                    self.loops.push(expr.hir_id);
 +                    self.is_single_insert = false;
 +                    self.allow_insert_closure &= !self.in_tail_pos;
 +                    // Don't allow insertions inside of a loop.
 +                    let edit_len = self.edits.len();
 +                    self.visit_block(block);
 +                    if self.edits.len() != edit_len {
 +                        self.can_use_entry = false;
 +                    }
 +                    self.loops.pop();
 +                },
 +                ExprKind::Block(block, _) => self.visit_block(block),
 +                ExprKind::InlineAsm(_) => {
 +                    self.can_use_entry = false;
 +                },
 +                _ => {
 +                    self.allow_insert_closure &= !self.in_tail_pos;
 +                    self.allow_insert_closure &=
 +                        can_move_expr_to_closure_no_visit(self.cx, expr, &self.loops, &self.locals);
 +                    // Sub expressions are no longer in the tail position.
 +                    self.is_single_insert = false;
 +                    self.in_tail_pos = false;
 +                    walk_expr(self, expr);
 +                },
 +            },
 +        }
 +    }
 +
 +    fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +        p.each_binding_or_first(&mut |_, id, _, _| {
 +            self.locals.insert(id);
 +        });
 +    }
 +}
 +
 +struct InsertSearchResults<'tcx> {
 +    edits: Vec<Edit<'tcx>>,
 +    allow_insert_closure: bool,
 +    is_single_insert: bool,
 +}
 +impl<'tcx> InsertSearchResults<'tcx> {
 +    fn as_single_insertion(&self) -> Option<Insertion<'tcx>> {
 +        self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap())
 +    }
 +
 +    fn snippet(
 +        &self,
 +        cx: &LateContext<'_>,
 +        mut span: Span,
 +        app: &mut Applicability,
 +        write_wrapped: impl Fn(&mut String, Insertion<'_>, SyntaxContext, &mut Applicability),
 +    ) -> String {
 +        let ctxt = span.ctxt();
 +        let mut res = String::new();
 +        for insertion in self.edits.iter().filter_map(|e| e.as_insertion()) {
 +            res.push_str(&snippet_with_applicability(
 +                cx,
 +                span.until(insertion.call.span),
 +                "..",
 +                app,
 +            ));
 +            if is_expr_used_or_unified(cx.tcx, insertion.call) {
 +                write_wrapped(&mut res, insertion, ctxt, app);
 +            } else {
 +                let _ = write!(
 +                    res,
 +                    "e.insert({})",
 +                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
 +                );
 +            }
 +            span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
 +        }
 +        res.push_str(&snippet_with_applicability(cx, span, "..", app));
 +        res
 +    }
 +
 +    fn snippet_occupied(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
 +        (
 +            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
 +                // Insertion into a map would return `Some(&mut value)`, but the entry returns `&mut value`
 +                let _ = write!(
 +                    res,
 +                    "Some(e.insert({}))",
 +                    snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0
 +                );
 +            }),
 +            "Occupied(mut e)",
 +        )
 +    }
 +
 +    fn snippet_vacant(&self, cx: &LateContext<'_>, span: Span, app: &mut Applicability) -> (String, &'static str) {
 +        (
 +            self.snippet(cx, span, app, |res, insertion, ctxt, app| {
 +                // Insertion into a map would return `None`, but the entry returns a mutable reference.
 +                let _ = if is_expr_final_block_expr(cx.tcx, insertion.call) {
 +                    write!(
 +                        res,
 +                        "e.insert({});\n{}None",
 +                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
 +                        snippet_indent(cx, insertion.call.span).as_deref().unwrap_or(""),
 +                    )
 +                } else {
 +                    write!(
 +                        res,
 +                        "{{ e.insert({}); None }}",
 +                        snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0,
 +                    )
 +                };
 +            }),
 +            "Vacant(e)",
 +        )
 +    }
 +
 +    fn snippet_closure(&self, cx: &LateContext<'_>, mut span: Span, app: &mut Applicability) -> String {
 +        let ctxt = span.ctxt();
 +        let mut res = String::new();
 +        for edit in &self.edits {
 +            match *edit {
 +                Edit::Insertion(insertion) => {
 +                    // Cut out the value from `map.insert(key, value)`
 +                    res.push_str(&snippet_with_applicability(
 +                        cx,
 +                        span.until(insertion.call.span),
 +                        "..",
 +                        app,
 +                    ));
 +                    res.push_str(&snippet_with_context(cx, insertion.value.span, ctxt, "..", app).0);
 +                    span = span.trim_start(insertion.call.span).unwrap_or(DUMMY_SP);
 +                },
 +                Edit::RemoveSemi(semi_span) => {
 +                    // Cut out the semicolon. This allows the value to be returned from the closure.
 +                    res.push_str(&snippet_with_applicability(cx, span.until(semi_span), "..", app));
 +                    span = span.trim_start(semi_span).unwrap_or(DUMMY_SP);
 +                },
 +            }
 +        }
 +        res.push_str(&snippet_with_applicability(cx, span, "..", app));
 +        res
 +    }
 +}
 +
 +fn find_insert_calls<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    contains_expr: &ContainsExpr<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<InsertSearchResults<'tcx>> {
 +    let mut s = InsertSearcher {
 +        cx,
 +        map: contains_expr.map,
 +        key: contains_expr.key,
 +        ctxt: expr.span.ctxt(),
 +        edits: Vec::new(),
 +        is_map_used: false,
 +        allow_insert_closure: true,
 +        can_use_entry: true,
 +        in_tail_pos: true,
 +        is_single_insert: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +    };
 +    s.visit_expr(expr);
 +    let allow_insert_closure = s.allow_insert_closure;
 +    let is_single_insert = s.is_single_insert;
 +    let edits = s.edits;
 +    s.can_use_entry.then(|| InsertSearchResults {
 +        edits,
 +        allow_insert_closure,
 +        is_single_insert,
 +    })
 +}
index e029b8e85379f1a6dda254a307fa2ec6cfd12d5b,0000000000000000000000000000000000000000..263a5b573c9cf92420e066ff99d7c8183ebbeedf
mode 100644,000000..100644
--- /dev/null
@@@ -1,300 -1,0 +1,300 @@@
-     /// Could be written as:
 +//! 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;
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```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 (pre.is_empty(), 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 {}fix: `{}`", what, value),
 +        None,
 +        &format!(
 +            "remove the {}fixes and use full paths to \
 +             the variants instead of glob imports",
 +            what
 +        ),
 +    );
 +}
 +
 +#[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(&(ref mod_name, ref 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.def_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.access_levels.is_exported(item.def_id)) {
 +                check_variant(cx, self.threshold, def, item_name, item.span);
 +            }
 +        }
 +        self.modules.push((item.ident.name, item_camel));
 +    }
 +}
index afb5d32f95334b31792d440185798047f2425a86,0000000000000000000000000000000000000000..c3176d987c6371ef7f631dc4f2b409bcc68099ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,321 -1,0 +1,321 @@@
-     /// ```
-     /// or
-     /// ```rust
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
 +use clippy_utils::get_enclosing_block;
 +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::{implements_trait, is_copy};
 +use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
 +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, LateLintPass};
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for equal operands to comparison, logical and
 +    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
 +    /// `||`, `&`, `|`, `^`, `-` and `/`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This is usually just a typo or a copy and paste error.
 +    ///
 +    /// ### Known problems
 +    /// False negatives: We had some false positives regarding
 +    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
 +    /// of `x.pop() && x.pop()`), so we removed matching any function or method
 +    /// calls. We may introduce a list of known pure functions in the future.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// if x + 1 == x + 1 {}
++    ///
++    /// // or
++    ///
 +    /// # let a = 3;
 +    /// # let b = 4;
 +    /// assert_eq!(a, a);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EQ_OP,
 +    correctness,
 +    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arguments to `==` which have their address
 +    /// taken to satisfy a bound
 +    /// and suggests to dereference the other argument instead
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more idiomatic to dereference the other argument.
 +    ///
 +    /// ### Known problems
 +    /// None
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad
 +    /// &x == y
 +    ///
 +    /// // Good
 +    /// x == *y
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OP_REF,
 +    style,
 +    "taking a reference to satisfy the type constraints on `==`"
 +}
 +
 +declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EqOp {
 +    #[expect(clippy::similar_names, clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
 +                let name = cx.tcx.item_name(macro_call.def_id);
 +                matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
 +                    .then(|| (macro_call, name))
 +            });
 +            if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
 +            if eq_expr_value(cx, lhs, rhs);
 +            if macro_call.is_local();
 +            if !is_in_test_function(cx.tcx, e.hir_id);
 +            then {
 +                span_lint(
 +                    cx,
 +                    EQ_OP,
 +                    lhs.span.to(rhs.span),
 +                    &format!("identical args used in this `{}!` macro call", macro_name),
 +                );
 +            }
 +        }
 +        if let ExprKind::Binary(op, left, right) = e.kind {
 +            if e.span.from_expansion() {
 +                return;
 +            }
 +            let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
 +                if let ExprKind::Unary(_, expr) = *expr_kind {
 +                    expr.span.from_expansion()
 +                } else {
 +                    false
 +                }
 +            };
 +            if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
 +                return;
 +            }
 +            if is_useless_with_eq_exprs(op.node.into())
 +                && eq_expr_value(cx, left, right)
 +                && !is_in_test_function(cx.tcx, e.hir_id)
 +            {
 +                span_lint(
 +                    cx,
 +                    EQ_OP,
 +                    e.span,
 +                    &format!("equal expressions as operands to `{}`", op.node.as_str()),
 +                );
 +                return;
 +            }
 +            let (trait_id, requires_ref) = match op.node {
 +                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 Some(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<'tcx>(cx: &LateContext<'tcx>, 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.def_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 cf47e581ccb48f26166502236527f0004983f6b2,0000000000000000000000000000000000000000..ef1216358dd97eb8eaa6dd00ddfbf3983c056e85
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,101 @@@
-     /// Should be written
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Pat, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +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.is_some() && 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_chain! {
 +            if let ExprKind::Let(let_expr) = expr.kind;
 +            if 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);
 +            if is_structural_partial_eq(cx, exp_ty, pat_ty);
 +            then {
 +
 +                let mut applicability = Applicability::MachineApplicable;
 +                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!(
 +                        "{} == {}",
 +                        snippet_with_context(cx, let_expr.init.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                        pat_str,
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index 807ecd2ddd16e0d2d9d8cdb6e838146c9cfe290f,0000000000000000000000000000000000000000..9d21dd71e0e8dd7c8edd8da762e8fd30df18baa6
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,204 @@@
-     /// // Bad
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::contains_ty;
 +use rustc_hir::intravisit;
 +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
 +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;
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +
 +#[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(bar: usize) {}
-     /// // Good
 +    /// let x = Box::new(1);
 +    /// foo(*x);
 +    /// println!("{}", *x);
++    /// ```
 +    ///
++    /// Use instead:
++    /// ```rust
++    /// # fn foo(bar: usize) {}
 +    /// let x = 1;
 +    /// foo(x);
 +    /// println!("{}", x);
 +    /// ```
 +    #[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);
 +        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.def_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);
 +        cx.tcx.infer_ctxt().enter(|infcx| {
 +            ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
 +        });
 +
 +        for node in v.set {
 +            span_lint(
 +                cx,
 +                BOXED_LOCAL,
 +                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::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);
 +                let map = &self.cx.tcx.hir();
 +                if let Some(Node::Binding(_)) = map.find(cmt.hir_id) {
 +                    if self.set.contains(&lid) {
 +                        // let y = x where x is known
 +                        // remove x, insert y
 +                        self.set.insert(cmt.hir_id);
 +                        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 && contains_ty(cmt.place.ty(), 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_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 7a81fb37e841cf7f218f23bbbddb9ecaf7ae7bea,0000000000000000000000000000000000000000..a2af10e2ba5eabf5dd9ada51a7df0a22b8a2be81
mode 100644,000000..100644
--- /dev/null
@@@ -1,178 -1,0 +1,177 @@@
-     /// Bad:
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_ast::ast::{AssocItemKind, Extern, Fn, FnSig, Impl, Item, ItemKind, Trait, Ty, TyKind};
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +
 +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
-     /// Good:
 +    /// ```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
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// fn f(is_round: bool, is_hot: bool) { ... }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```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,
 +}
 +
 +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,
 +        }
 +    }
 +
 +    fn check_fn_sig(&self, cx: &EarlyContext<'_>, fn_sig: &FnSig, span: Span) {
 +        match fn_sig.header.ext {
 +            Extern::Implicit | Extern::Explicit(_) => return,
 +            Extern::None => (),
 +        }
 +
 +        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 {
 +            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]);
 +
 +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;
 +        }
 +    }
 +    false
 +}
 +
 +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;
 +                }
 +
 +                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),
 +            _ => (),
 +        }
 +    }
 +}
index d8f765b288a6c20a624ee4a980b4615e2ddad85b,0000000000000000000000000000000000000000..12d636cf41014853a919632681531c216423b327
mode 100644,000000..100644
--- /dev/null
@@@ -1,134 -1,0 +1,142 @@@
-     /// // this would be clearer as `eprintln!("foo: {:?}", bar);`
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::macros::FormatArgsExpn;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{is_expn_of, match_function_call, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
 +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 usage of `write!()` / `writeln()!` which can be
 +    /// replaced with `(e)print!()` / `(e)println!()`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `(e)println! is clearer and more concise
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::io::Write;
 +    /// # let bar = "furchtbar";
 +    /// writeln!(&mut std::io::stderr(), "foo: {:?}", bar).unwrap();
++    /// writeln!(&mut std::io::stdout(), "foo: {:?}", bar).unwrap();
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// # use std::io::Write;
++    /// # let bar = "furchtbar";
++    /// eprintln!("foo: {:?}", bar);
++    /// println!("foo: {:?}", bar);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPLICIT_WRITE,
 +    complexity,
 +    "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
 +}
 +
 +declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // match call to unwrap
 +            if let ExprKind::MethodCall(unwrap_fun, [write_call], _) = expr.kind;
 +            if unwrap_fun.ident.name == sym::unwrap;
 +            // match call to write_fmt
 +            if let ExprKind::MethodCall(write_fun, [write_recv, write_arg], _) = look_in_block(cx, &write_call.kind);
 +            if write_fun.ident.name == sym!(write_fmt);
 +            // match calls to std::io::stdout() / std::io::stderr ()
 +            if let Some(dest_name) = if match_function_call(cx, write_recv, &paths::STDOUT).is_some() {
 +                Some("stdout")
 +            } else if match_function_call(cx, write_recv, &paths::STDERR).is_some() {
 +                Some("stderr")
 +            } else {
 +                None
 +            };
 +            if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg);
 +            then {
 +                let calling_macro =
 +                    // ordering is important here, since `writeln!` uses `write!` internally
 +                    if is_expn_of(write_call.span, "writeln").is_some() {
 +                        Some("writeln")
 +                    } else if is_expn_of(write_call.span, "write").is_some() {
 +                        Some("write")
 +                    } else {
 +                        None
 +                    };
 +                let prefix = if dest_name == "stderr" {
 +                    "e"
 +                } else {
 +                    ""
 +                };
 +
 +                // We need to remove the last trailing newline from the string because the
 +                // underlying `fmt::write` function doesn't know whether `println!` or `print!` was
 +                // used.
 +                let (used, sugg_mac) = if let Some(macro_name) = calling_macro {
 +                    (
 +                        format!("{}!({}(), ...)", macro_name, dest_name),
 +                        macro_name.replace("write", "print"),
 +                    )
 +                } else {
 +                    (
 +                        format!("{}().write_fmt(...)", dest_name),
 +                        "print".into(),
 +                    )
 +                };
 +                let mut applicability = Applicability::MachineApplicable;
 +                let inputs_snippet = snippet_with_applicability(
 +                    cx,
 +                    format_args.inputs_span(),
 +                    "..",
 +                    &mut applicability,
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    EXPLICIT_WRITE,
 +                    expr.span,
 +                    &format!("use of `{}.unwrap()`", used),
 +                    "try this",
 +                    format!("{}{}!({})", prefix, sugg_mac, inputs_snippet),
 +                    applicability,
 +                )
 +            }
 +        }
 +    }
 +}
 +
 +/// If `kind` is a block that looks like `{ let result = $expr; result }` then
 +/// returns $expr. Otherwise returns `kind`.
 +fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> {
 +    if_chain! {
 +        if let ExprKind::Block(block, _label @ None) = kind;
 +        if let Block {
 +            stmts: [Stmt { kind: StmtKind::Local(local), .. }],
 +            expr: Some(expr_end_of_block),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        } = block;
 +
 +        // Find id of the local that expr_end_of_block resolves to
 +        if let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind;
 +        if let Res::Local(expr_res) = expr_path.res;
 +        if let Some(Node::Binding(res_pat)) = cx.tcx.hir().find(expr_res);
 +
 +        // Find id of the local we found in the block
 +        if let PatKind::Binding(BindingAnnotation::Unannotated, local_hir_id, _ident, None) = local.pat.kind;
 +
 +        // If those two are the same hir id
 +        if res_pat.hir_id == local_hir_id;
 +
 +        if let Some(init) = local.init;
 +        then {
 +            return &init.kind;
 +        }
 +    }
 +    kind
 +}
index 9f868df3ad063452f3dd45ebe960d2430fb1efc0,0000000000000000000000000000000000000000..b88e53aeca693d42f40d3d69f82a6ef17ac4b276
mode 100644,000000..100644
--- /dev/null
@@@ -1,133 -1,0 +1,132 @@@
-     /// // Bad
 +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);
 +    ///
-     /// // Good
 +    /// 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.def_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<'tcx>(cx: &LateContext<'tcx>, 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.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 7a4397a7b746714ac8cd70d26d4a797e76976f13,0000000000000000000000000000000000000000..f850ea31f4d6e54b249587e0390df2685d4ed5dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,180 @@@
-     /// // Bad
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::numeric_literal;
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitFloatType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, FloatTy};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::fmt;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for float literals with a precision greater
 +    /// than that supported by the underlying type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust will truncate the literal silently.
 +    ///
 +    /// ### Example
 +    /// ```rust
-     /// // Good
 +    /// let v: f32 = 0.123_456_789_9;
 +    /// println!("{}", v); //  0.123_456_789
++    /// ```
 +    ///
++    /// Use instead:
++    /// ```rust
 +    /// let v: f64 = 0.123_456_789_9;
 +    /// println!("{}", v); //  0.123_456_789_9
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXCESSIVE_PRECISION,
 +    style,
 +    "excessive precision for float literal"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for whole number float literals that
 +    /// cannot be represented as the underlying type without loss.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust will silently lose precision during
 +    /// conversion to a float.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _: f32 = 16_777_217.0; // 16_777_216.0
 +    ///
 +    /// // Good
 +    /// let _: f32 = 16_777_216.0;
 +    /// let _: f64 = 16_777_217.0;
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub LOSSY_FLOAT_LITERAL,
 +    restriction,
 +    "lossy whole number float literals"
 +}
 +
 +declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        if_chain! {
 +            if let ty::Float(fty) = *ty.kind();
 +            if let hir::ExprKind::Lit(ref lit) = expr.kind;
 +            if let LitKind::Float(sym, lit_float_ty) = lit.node;
 +            then {
 +                let sym_str = sym.as_str();
 +                let formatter = FloatFormat::new(sym_str);
 +                // Try to bail out if the float is for sure fine.
 +                // If its within the 2 decimal digits of being out of precision we
 +                // check if the parsed representation is the same as the string
 +                // since we'll need the truncated string anyway.
 +                let digits = count_digits(sym_str);
 +                let max = max_digits(fty);
 +                let type_suffix = match lit_float_ty {
 +                    LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"),
 +                    LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"),
 +                    LitFloatType::Unsuffixed => None
 +                };
 +                let (is_whole, mut float_str) = match fty {
 +                    FloatTy::F32 => {
 +                        let value = sym_str.parse::<f32>().unwrap();
 +
 +                        (value.fract() == 0.0, formatter.format(value))
 +                    },
 +                    FloatTy::F64 => {
 +                        let value = sym_str.parse::<f64>().unwrap();
 +
 +                        (value.fract() == 0.0, formatter.format(value))
 +                    },
 +                };
 +
 +                if is_whole && !sym_str.contains(|c| c == 'e' || c == 'E') {
 +                    // Normalize the literal by stripping the fractional portion
 +                    if sym_str.split('.').next().unwrap() != float_str {
 +                        // If the type suffix is missing the suggestion would be
 +                        // incorrectly interpreted as an integer so adding a `.0`
 +                        // suffix to prevent that.
 +                        if type_suffix.is_none() {
 +                            float_str.push_str(".0");
 +                        }
 +
 +                        span_lint_and_sugg(
 +                            cx,
 +                            LOSSY_FLOAT_LITERAL,
 +                            expr.span,
 +                            "literal cannot be represented as the underlying type without loss of precision",
 +                            "consider changing the type or replacing it with",
 +                            numeric_literal::format(&float_str, type_suffix, true),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                } else if digits > max as usize && float_str.len() < sym_str.len() {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        EXCESSIVE_PRECISION,
 +                        expr.span,
 +                        "float has excessive precision",
 +                        "consider changing the type or truncating it to",
 +                        numeric_literal::format(&float_str, type_suffix, true),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[must_use]
 +fn max_digits(fty: FloatTy) -> u32 {
 +    match fty {
 +        FloatTy::F32 => f32::DIGITS,
 +        FloatTy::F64 => f64::DIGITS,
 +    }
 +}
 +
 +/// Counts the digits excluding leading zeros
 +#[must_use]
 +fn count_digits(s: &str) -> usize {
 +    // Note that s does not contain the f32/64 suffix, and underscores have been stripped
 +    s.chars()
 +        .filter(|c| *c != '-' && *c != '.')
 +        .take_while(|c| *c != 'e' && *c != 'E')
 +        .fold(0, |count, c| {
 +            // leading zeros
 +            if c == '0' && count == 0 { count } else { count + 1 }
 +        })
 +}
 +
 +enum FloatFormat {
 +    LowerExp,
 +    UpperExp,
 +    Normal,
 +}
 +impl FloatFormat {
 +    #[must_use]
 +    fn new(s: &str) -> Self {
 +        s.chars()
 +            .find_map(|x| match x {
 +                'e' => Some(Self::LowerExp),
 +                'E' => Some(Self::UpperExp),
 +                _ => None,
 +            })
 +            .unwrap_or(Self::Normal)
 +    }
 +    fn format<T>(&self, f: T) -> String
 +    where
 +        T: fmt::UpperExp + fmt::LowerExp + fmt::Display,
 +    {
 +        match self {
 +            Self::LowerExp => format!("{:e}", f),
 +            Self::UpperExp => format!("{:E}", f),
 +            Self::Normal => format!("{}", f),
 +        }
 +    }
 +}
index 42503c26de1d1dcb1112fd1cf3a888a0acbec037,0000000000000000000000000000000000000000..df9b41d2c98bef1fe10d2f1eca1fe10e41477aab
mode 100644,000000..100644
--- /dev/null
@@@ -1,736 -1,0 +1,735 @@@
-     /// is better expressed as
-     ///
 +use clippy_utils::consts::{
 +    constant, constant_simple, Constant,
 +    Constant::{Int, F32, F64},
 +};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 +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_ast::ast;
 +use std::f32::consts as f32_consts;
 +use std::f64::consts as f64_consts;
 +use sugg::Sugg;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve accuracy
 +    /// at the cost of performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.powf(1.0 / 3.0);
 +    /// let _ = (1.0 + a).ln();
 +    /// let _ = a.exp() - 1.0;
 +    /// ```
 +    ///
++    /// Use instead:
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.cbrt();
 +    /// let _ = a.ln_1p();
 +    /// let _ = a.exp_m1();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub IMPRECISE_FLOPS,
 +    nursery,
 +    "usage of imprecise floating point operations"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve both
 +    /// accuracy and performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy and performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = (2f32).powf(a);
 +    /// let _ = E.powf(a);
 +    /// let _ = a.powf(1.0 / 2.0);
 +    /// let _ = a.log(2.0);
 +    /// let _ = a.log(10.0);
 +    /// let _ = a.log(E);
 +    /// let _ = a.powf(2.0);
 +    /// let _ = a * 2.0 + 4.0;
 +    /// let _ = if a < 0.0 {
 +    ///     -a
 +    /// } else {
 +    ///     a
 +    /// };
 +    /// let _ = if a < 0.0 {
 +    ///     a
 +    /// } else {
 +    ///     -a
 +    /// };
 +    /// ```
 +    ///
 +    /// is better expressed as
 +    ///
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = a.exp2();
 +    /// let _ = a.exp();
 +    /// let _ = a.sqrt();
 +    /// let _ = a.log2();
 +    /// let _ = a.log10();
 +    /// let _ = a.ln();
 +    /// let _ = a.powi(2);
 +    /// let _ = a.mul_add(2.0, 4.0);
 +    /// let _ = a.abs();
 +    /// let _ = -a.abs();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub SUBOPTIMAL_FLOPS,
 +    nursery,
 +    "usage of sub-optimal floating point operations"
 +}
 +
 +declare_lint_pass!(FloatingPointArithmetic => [
 +    IMPRECISE_FLOPS,
 +    SUBOPTIMAL_FLOPS
 +]);
 +
 +// Returns the specialized log method for a given base if base is constant
 +// and is one of 2, 10 and e
 +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), base) {
 +        if F32(2.0) == value || F64(2.0) == value {
 +            return Some("log2");
 +        } else if F32(10.0) == value || F64(10.0) == value {
 +            return Some("log10");
 +        } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            return Some("ln");
 +        }
 +    }
 +
 +    None
 +}
 +
 +// Adds type suffixes and parenthesis to method receivers if necessary
 +fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
 +    let mut suggestion = Sugg::hir(cx, expr, "..");
 +
 +    if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
 +        expr = inner_expr;
 +    }
 +
 +    if_chain! {
 +        // if the expression is a float literal and it is unsuffixed then
 +        // add a suffix so the suggestion is valid and unambiguous
 +        if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
 +        if let ExprKind::Lit(lit) = &expr.kind;
 +        if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
 +        then {
 +            let op = format!(
 +                "{}{}{}",
 +                suggestion,
 +                // Check for float literals without numbers following the decimal
 +                // separator such as `2.` and adds a trailing zero
 +                if sym.as_str().ends_with('.') {
 +                    "0"
 +                } else {
 +                    ""
 +                },
 +                float_ty.name_str()
 +            ).into();
 +
 +            suggestion = match suggestion {
 +                Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
 +                _ => Sugg::NonParen(op)
 +            };
 +        }
 +    }
 +
 +    suggestion.maybe_par()
 +}
 +
 +fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(method) = get_specialized_log_method(cx, &args[1]) {
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "logarithm for bases 2, 10 and e can be computed more accurately",
 +            "consider using",
 +            format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
 +// suggest usage of `(x + (y - 1)).ln_1p()` instead
 +fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &args[0].kind
 +    {
 +        let recv = match (
 +            constant(cx, cx.typeck_results(), lhs),
 +            constant(cx, cx.typeck_results(), rhs),
 +        ) {
 +            (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
 +            (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
 +            _ => return,
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "ln(1 + x) can be computed more accurately",
 +            "consider using",
 +            format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// Returns an integer if the float constant is a whole number and it can be
 +// converted to an integer without loss of precision. For now we only check
 +// ranges [-16777215, 16777216) for type f32 as whole number floats outside
 +// this range are lossy and ambiguous.
 +#[expect(clippy::cast_possible_truncation)]
 +fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
 +    match value {
 +        F32(num) if num.fract() == 0.0 => {
 +            if (-16_777_215.0..16_777_216.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        F64(num) if num.fract() == 0.0 => {
 +            if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    // Check receiver
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
 +        let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            "exp"
 +        } else if F32(2.0) == value || F64(2.0) == value {
 +            "exp2"
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "exponent for bases 2 and e can be computed more accurately",
 +            "consider using",
 +            format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +
 +    // Check argument
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
 +        let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "square-root of a number can be computed more efficiently and accurately",
 +                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
 +            )
 +        } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
 +            (
 +                IMPRECISE_FLOPS,
 +                "cube-root of a number can be computed more accurately",
 +                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
 +            )
 +        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "exponentiation with integer powers can be computed more efficiently",
 +                format!(
 +                    "{}.powi({})",
 +                    Sugg::hir(cx, &args[0], ".."),
 +                    numeric_literal::format(&exponent.to_string(), None, false)
 +                ),
 +            )
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            lint,
 +            expr.span,
 +            help,
 +            "consider using",
 +            suggestion,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
 +        if value == Int(2) {
 +            if let Some(parent) = get_parent_expr(cx, expr) {
 +                if let Some(grandparent) = get_parent_expr(cx, parent) {
 +                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, args, _) = grandparent.kind {
 +                        if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
 +                            return;
 +                        }
 +                    }
 +                }
 +
 +                if let ExprKind::Binary(
 +                    Spanned {
 +                        node: BinOpKind::Add, ..
 +                    },
 +                    lhs,
 +                    rhs,
 +                ) = parent.kind
 +                {
 +                    let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        SUBOPTIMAL_FLOPS,
 +                        parent.span,
 +                        "multiply and add expressions can be calculated more efficiently and accurately",
 +                        "consider using",
 +                        format!(
 +                            "{}.mul_add({}, {})",
 +                            Sugg::hir(cx, &args[0], ".."),
 +                            Sugg::hir(cx, &args[0], ".."),
 +                            Sugg::hir(cx, other_addend, ".."),
 +                        ),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        add_lhs,
 +        add_rhs,
 +    ) = args[0].kind
 +    {
 +        // check if expression of the form x * x + y * y
 +        if_chain! {
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind;
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind;
 +            if eq_expr_value(cx, lmul_lhs, lmul_rhs);
 +            if eq_expr_value(cx, rmul_lhs, rmul_rhs);
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, "..")));
 +            }
 +        }
 +
 +        // check if expression of the form x.powi(2) + y.powi(2)
 +        if_chain! {
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: lmethod_name, .. },
 +                [largs_0, largs_1, ..],
 +                _
 +            ) = &add_lhs.kind;
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: rmethod_name, .. },
 +                [rargs_0, rargs_1, ..],
 +                _
 +            ) = &add_rhs.kind;
 +            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
 +            if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1);
 +            if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
 +            if Int(2) == lvalue && Int(2) == rvalue;
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, "..")));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(message) = detect_hypot(cx, args) {
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "hypotenuse can be computed more accurately",
 +            "consider using",
 +            message,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `x.exp() - y` where y > 1
 +// and suggest usage of `x.exp_m1() - (y - 1)` instead
 +fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
 +        if F32(1.0) == value || F64(1.0) == value;
 +        if let ExprKind::MethodCall(path, [self_arg, ..], _) = &lhs.kind;
 +        if cx.typeck_results().expr_ty(self_arg).is_floating_point();
 +        if path.ident.name.as_str() == "exp";
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                IMPRECISE_FLOPS,
 +                expr.span,
 +                "(e.pow(x) - 1) can be computed more accurately",
 +                "consider using",
 +                format!(
 +                    "{}.exp_m1()",
 +                    Sugg::hir(cx, self_arg, "..")
 +                ),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if cx.typeck_results().expr_ty(rhs).is_floating_point();
 +        then {
 +            return Some((lhs, rhs));
 +        }
 +    }
 +
 +    None
 +}
 +
 +// TODO: Fix rust-lang/rust-clippy#4735
 +fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &expr.kind
 +    {
 +        if let Some(parent) = get_parent_expr(cx, expr) {
 +            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, args, _) = parent.kind {
 +                if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
 +                    return;
 +                }
 +            }
 +        }
 +
 +        let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
 +            (inner_lhs, inner_rhs, rhs)
 +        } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
 +            (inner_lhs, inner_rhs, lhs)
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "multiply and add expressions can be calculated more efficiently and accurately",
 +            "consider using",
 +            format!(
 +                "{}.mul_add({}, {})",
 +                prepare_receiver_sugg(cx, recv),
 +                Sugg::hir(cx, arg1, ".."),
 +                Sugg::hir(cx, arg2, ".."),
 +            ),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +/// Returns true iff expr is an expression which tests whether or not
 +/// test is positive or an expression which tests whether or not test
 +/// is nonnegative.
 +/// Used for check-custom-abs function below
 +fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// See [`is_testing_positive`]
 +fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Returns true iff expr is some zero literal
 +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    match constant_simple(cx, cx.typeck_results(), expr) {
 +        Some(Constant::Int(i)) => i == 0,
 +        Some(Constant::F32(f)) => f == 0.0,
 +        Some(Constant::F64(f)) => f == 0.0,
 +        _ => false,
 +    }
 +}
 +
 +/// If the two expressions are negations of each other, then it returns
 +/// a tuple, in which the first element is true iff expr1 is the
 +/// positive expressions, and the second element is the positive
 +/// one of the two expressions
 +/// If the two expressions are not negations of each other, then it
 +/// returns None.
 +fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
 +    if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
 +        if eq_expr_value(cx, expr1_negated, expr2) {
 +            return Some((false, expr2));
 +        }
 +    }
 +    if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
 +        if eq_expr_value(cx, expr1, expr2_negated) {
 +            return Some((true, expr1));
 +        }
 +    }
 +    None
 +}
 +
 +fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr);
 +        let if_body_expr = peel_blocks(then);
 +        let else_body_expr = peel_blocks(r#else);
 +        if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
 +        then {
 +            let positive_abs_sugg = (
 +                "manual implementation of `abs` method",
 +                format!("{}.abs()", Sugg::hir(cx, body, "..")),
 +            );
 +            let negative_abs_sugg = (
 +                "manual implementation of negation of `abs` method",
 +                format!("-{}.abs()", Sugg::hir(cx, body, "..")),
 +            );
 +            let sugg = if is_testing_positive(cx, cond, body) {
 +                if if_expr_positive {
 +                    positive_abs_sugg
 +                } else {
 +                    negative_abs_sugg
 +                }
 +            } else if is_testing_negative(cx, cond, body) {
 +                if if_expr_positive {
 +                    negative_abs_sugg
 +                } else {
 +                    positive_abs_sugg
 +                }
 +            } else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                sugg.0,
 +                "try",
 +                sugg.1,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, args_a, _) = expr_a.kind;
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, args_b, _) = expr_b.kind;
 +        then {
 +            return method_name_a.as_str() == method_name_b.as_str() &&
 +                args_a.len() == args_b.len() &&
 +                (
 +                    ["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
 +                    method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
 +                );
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    // check if expression of the form x.logN() / y.logN()
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) = &expr.kind;
 +        if are_same_base_logs(cx, lhs, rhs);
 +        if let ExprKind::MethodCall(_, [largs_self, ..], _) = &lhs.kind;
 +        if let ExprKind::MethodCall(_, [rargs_self, ..], _) = &rhs.kind;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                "log base can be expressed more clearly",
 +                "consider using",
 +                format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            div_lhs,
 +            div_rhs,
 +        ) = &expr.kind;
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Mul, ..
 +            },
 +            mul_lhs,
 +            mul_rhs,
 +        ) = &div_lhs.kind;
 +        if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
 +        if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
 +        then {
 +            // TODO: also check for constant values near PI/180 or 180/PI
 +            if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
 +               (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
 +            {
 +                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                if_chain! {
 +                    if let ExprKind::Lit(ref literal) = mul_lhs.kind;
 +                    if let ast::LitKind::Float(ref value, float_type) = literal.node;
 +                    if float_type == ast::LitFloatType::Unsuffixed;
 +                    then {
 +                        if value.as_str().ends_with('.') {
 +                            proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                        } else {
 +                            proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                        }
 +                    }
 +                }
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to degrees can be done more accurately",
 +                    "consider using",
 +                    proposal,
 +                    Applicability::MachineApplicable,
 +                );
 +            } else if
 +                (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
 +                (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
 +            {
 +                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                if_chain! {
 +                    if let ExprKind::Lit(ref literal) = mul_lhs.kind;
 +                    if let ast::LitKind::Float(ref value, float_type) = literal.node;
 +                    if float_type == ast::LitFloatType::Unsuffixed;
 +                    then {
 +                        if value.as_str().ends_with('.') {
 +                            proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                        } else {
 +                            proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                        }
 +                    }
 +                }
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to radians can be done more accurately",
 +                    "consider using",
 +                    proposal,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // All of these operations are currently not const.
 +        if in_constant(cx, expr.hir_id) {
 +            return;
 +        }
 +
 +        if let ExprKind::MethodCall(path, args, _) = &expr.kind {
 +            let recv_ty = cx.typeck_results().expr_ty(&args[0]);
 +
 +            if recv_ty.is_floating_point() {
 +                match path.ident.name.as_str() {
 +                    "ln" => check_ln1p(cx, expr, args),
 +                    "log" => check_log_base(cx, expr, args),
 +                    "powf" => check_powf(cx, expr, args),
 +                    "powi" => check_powi(cx, expr, args),
 +                    "sqrt" => check_hypot(cx, expr, args),
 +                    _ => {},
 +                }
 +            }
 +        } else {
 +            check_expm1(cx, expr);
 +            check_mul_add(cx, expr);
 +            check_custom_abs(cx, expr);
 +            check_log_division(cx, expr);
 +            check_radians(cx, expr);
 +        }
 +    }
 +}
index 64c41b565878b36772fe2d2e5d17882c2f867314,0000000000000000000000000000000000000000..3084c70589fa331fe89206432622d0ead7fe36fc
mode 100644,000000..100644
--- /dev/null
@@@ -1,137 -1,0 +1,138 @@@
-     ///
-     /// // Bad
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
 +use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::kw;
 +use rustc_span::{sym, BytePos, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `format!("string literal with no
 +    /// argument")` and `format!("{}", foo)` where `foo` is a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// There is no point of doing that. `format!("foo")` can
 +    /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
 +    /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
 +    /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
 +    /// if `foo: &str`.
 +    ///
 +    /// ### Examples
 +    /// ```rust
-     /// // Good
 +    /// let foo = "foo";
 +    /// format!("{}", foo);
++    /// ```
 +    ///
++    /// Use instead:
++    /// ```rust
++    /// let foo = "foo";
 +    /// foo.to_owned();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_FORMAT,
 +    complexity,
 +    "useless use of `format!`"
 +}
 +
 +declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UselessFormat {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (format_args, call_site) = if_chain! {
 +            if let Some(macro_call) = root_macro_call_first_node(cx, expr);
 +            if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
 +            if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
 +            then {
 +                (format_args, macro_call.span)
 +            } else {
 +                return
 +            }
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +        if format_args.value_args.is_empty() {
 +            match *format_args.format_string_parts {
 +                [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
 +                [_] => {
 +                    if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
 +                        // Simulate macro expansion, converting {{ and }} to { and }.
 +                        let s_expand = s_src.replace("{{", "{").replace("}}", "}");
 +                        let sugg = format!("{}.to_string()", s_expand);
 +                        span_useless_format(cx, call_site, sugg, applicability);
 +                    }
 +                },
 +                [..] => {},
 +            }
 +        } else if let [value] = *format_args.value_args {
 +            if_chain! {
 +                if format_args.format_string_parts == [kw::Empty];
 +                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
 +                    ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
 +                    ty::Str => true,
 +                    _ => false,
 +                };
 +                if let Some(args) = format_args.args();
 +                if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
 +                then {
 +                    let is_new_string = match value.kind {
 +                        ExprKind::Binary(..) => true,
 +                        ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
 +                        _ => false,
 +                    };
 +                    let sugg = if format_args.format_string_span.contains(value.span) {
 +                        // Implicit argument. e.g. `format!("{x}")` span points to `{x}`
 +                        let spdata = value.span.data();
 +                        let span = Span::new(
 +                            spdata.lo + BytePos(1),
 +                            spdata.hi - BytePos(1),
 +                            spdata.ctxt,
 +                            spdata.parent
 +                        );
 +                        let snip = snippet_with_applicability(cx, span, "..", &mut applicability);
 +                        if is_new_string {
 +                            snip.into()
 +                        } else {
 +                            format!("{snip}.to_string()")
 +                        }
 +                    } else if is_new_string {
 +                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
 +                    } else {
 +                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
 +                        format!("{}.to_string()", sugg.maybe_par())
 +                    };
 +                    span_useless_format(cx, call_site, sugg, applicability);
 +                }
 +            }
 +        };
 +    }
 +}
 +
 +fn span_useless_format_empty(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        USELESS_FORMAT,
 +        span,
 +        "useless use of `format!`",
 +        "consider using `String::new()`",
 +        sugg,
 +        applicability,
 +    );
 +}
 +
 +fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        USELESS_FORMAT,
 +        span,
 +        "useless use of `format!`",
 +        "consider using `.to_string()`",
 +        sugg,
 +        applicability,
 +    );
 +}
index 57964b8d48ea9c4904282e191e097b044b140534,0000000000000000000000000000000000000000..db0166da57f0e343fb65e3898b8320b3b42bd1f3
mode 100644,000000..100644
--- /dev/null
@@@ -1,335 -1,0 +1,341 @@@
-     /// ```rust,ignore
-     /// if foo <- 30 { // this should be `foo < -30` but looks like a different operator
-     /// }
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
 +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::source_map::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of the non-existent `=*`, `=!` and `=-`
 +    /// operators.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is either a typo of `*=`, `!=` or `-=` or
 +    /// confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    suspicious,
 +    "suspicious formatting of `*=`, `-=` or `!=`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the formatting of a unary operator on the right hand side
 +    /// of a binary operator. It lints if there is no space between the binary and unary operators,
 +    /// but there is a space between the unary and its operand.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is either a typo in the binary operator or confusing.
 +    ///
 +    /// ### Example
-     /// if foo &&! bar { // this should be `foo && !bar` but looks like a different operator
-     /// }
++    /// ```rust
++    /// # let foo = true;
++    /// # let bar = false;
++    /// // &&! looks like a different operator
++    /// if foo &&! bar {}
++    /// ```
 +    ///
++    /// Use instead:
++    /// ```rust
++    /// # let foo = true;
++    /// # let bar = false;
++    /// if foo && !bar {}
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub SUSPICIOUS_UNARY_OP_FORMATTING,
 +    suspicious,
 +    "suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for formatting of `else`. It lints if the `else`
 +    /// is followed immediately by a newline or the `else` seems to be missing.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably some refactoring remnant, even if the
 +    /// code is correct, it might look confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if foo {
 +    /// } { // looks like an `else` is missing here
 +    /// }
 +    ///
 +    /// if foo {
 +    /// } if bar { // looks like an `else` is missing here
 +    /// }
 +    ///
 +    /// if foo {
 +    /// } else
 +    ///
 +    /// { // this is the `else` block of the previous `if`, but should it be?
 +    /// }
 +    ///
 +    /// if foo {
 +    /// } else
 +    ///
 +    /// if bar { // this is the `else` block of the previous `if`, but should it be?
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SUSPICIOUS_ELSE_FORMATTING,
 +    suspicious,
 +    "suspicious formatting of `else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for possible missing comma in an array. It lints if
 +    /// an array element is a binary operator expression and it lies on two lines.
 +    ///
 +    /// ### Why is this bad?
 +    /// This could lead to unexpected results.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let a = &[
 +    ///     -1, -2, -3 // <= no comma here
 +    ///     -4, -5, -6
 +    /// ];
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub POSSIBLE_MISSING_COMMA,
 +    correctness,
 +    "possible missing comma in array"
 +}
 +
 +declare_lint_pass!(Formatting => [
 +    SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    SUSPICIOUS_UNARY_OP_FORMATTING,
 +    SUSPICIOUS_ELSE_FORMATTING,
 +    POSSIBLE_MISSING_COMMA
 +]);
 +
 +impl EarlyLintPass for Formatting {
 +    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
 +        for w in block.stmts.windows(2) {
 +            if let (StmtKind::Expr(first), StmtKind::Expr(second) | StmtKind::Semi(second)) = (&w[0].kind, &w[1].kind) {
 +                check_missing_else(cx, first, second);
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        check_assign(cx, expr);
 +        check_unop(cx, expr);
 +        check_else(cx, expr);
 +        check_array(cx, expr);
 +    }
 +}
 +
 +/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
 +fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
 +        if !lhs.span.from_expansion() && !rhs.span.from_expansion() {
 +            let eq_span = lhs.span.between(rhs.span);
 +            if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
 +                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
 +                    let op = UnOp::to_string(op);
 +                    let eqop_span = lhs.span.between(sub_rhs.span);
 +                    if eq_snippet.ends_with('=') {
 +                        span_lint_and_note(
 +                            cx,
 +                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +                            eqop_span,
 +                            &format!(
 +                                "this looks like you are trying to use `.. {op}= ..`, but you \
 +                                 really are doing `.. = ({op} ..)`",
 +                                op = op
 +                            ),
 +                            None,
 +                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
 +fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if_chain! {
 +        if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
 +        if !lhs.span.from_expansion() && !rhs.span.from_expansion();
 +        // span between BinOp LHS and RHS
 +        let binop_span = lhs.span.between(rhs.span);
 +        // if RHS is an UnOp
 +        if let ExprKind::Unary(op, ref un_rhs) = rhs.kind;
 +        // from UnOp operator to UnOp operand
 +        let unop_operand_span = rhs.span.until(un_rhs.span);
 +        if let Some(binop_snippet) = snippet_opt(cx, binop_span);
 +        if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span);
 +        let binop_str = BinOpKind::to_string(&binop.node);
 +        // no space after BinOp operator and space after UnOp operator
 +        if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ');
 +        then {
 +            let unop_str = UnOp::to_string(op);
 +            let eqop_span = lhs.span.between(un_rhs.span);
 +            span_lint_and_help(
 +                cx,
 +                SUSPICIOUS_UNARY_OP_FORMATTING,
 +                eqop_span,
 +                &format!(
 +                    "by not having a space between `{binop}` and `{unop}` it looks like \
 +                     `{binop}{unop}` is a single operator",
 +                    binop = binop_str,
 +                    unop = unop_str
 +                ),
 +                None,
 +                &format!(
 +                    "put a space between `{binop}` and `{unop}` and remove the space after `{unop}`",
 +                    binop = binop_str,
 +                    unop = unop_str
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
 +fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if_chain! {
 +        if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
 +        if is_block(else_) || is_if(else_);
 +        if !then.span.from_expansion() && !else_.span.from_expansion();
 +        if !in_external_macro(cx.sess(), expr.span);
 +
 +        // workaround for rust-lang/rust#43081
 +        if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
 +
 +        // this will be a span from the closing ‘}’ of the “then” block (excluding) to
 +        // the “if” of the “else if” block (excluding)
 +        let else_span = then.span.between(else_.span);
 +
 +        // the snippet should look like " else \n    " with maybe comments anywhere
 +        // it’s bad when there is a ‘\n’ after the “else”
 +        if let Some(else_snippet) = snippet_opt(cx, else_span);
 +        if let Some((pre_else, post_else)) = else_snippet.split_once("else");
 +        if let Some((_, post_else_post_eol)) = post_else.split_once('\n');
 +
 +        then {
 +            // Allow allman style braces `} \n else \n {`
 +            if_chain! {
 +                if is_block(else_);
 +                if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
 +                // Exactly one eol before and after the else
 +                if !pre_else_post_eol.contains('\n');
 +                if !post_else_post_eol.contains('\n');
 +                then {
 +                    return;
 +                }
 +            }
 +
 +            let else_desc = if is_if(else_) { "if" } else { "{..}" };
 +            span_lint_and_note(
 +                cx,
 +                SUSPICIOUS_ELSE_FORMATTING,
 +                else_span,
 +                &format!("this is an `else {}` but the formatting might hide it", else_desc),
 +                None,
 +                &format!(
 +                    "to remove this lint, remove the `else` or remove the new line between \
 +                     `else` and `{}`",
 +                    else_desc,
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +#[must_use]
 +fn has_unary_equivalent(bin_op: BinOpKind) -> bool {
 +    // &, *, -
 +    bin_op == BinOpKind::And || bin_op == BinOpKind::Mul || bin_op == BinOpKind::Sub
 +}
 +
 +fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
 +    cx.sess().source_map().lookup_char_pos(span.lo()).col.0
 +}
 +
 +/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
 +fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
 +    if let ExprKind::Array(ref array) = expr.kind {
 +        for element in array {
 +            if_chain! {
 +                if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
 +                if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
 +                let space_span = lhs.span.between(op.span);
 +                if let Some(space_snippet) = snippet_opt(cx, space_span);
 +                let lint_span = lhs.span.with_lo(lhs.span.hi());
 +                if space_snippet.contains('\n');
 +                if indentation(cx, op.span) <= indentation(cx, lhs.span);
 +                then {
 +                    span_lint_and_note(
 +                        cx,
 +                        POSSIBLE_MISSING_COMMA,
 +                        lint_span,
 +                        "possibly missing a comma here",
 +                        None,
 +                        "to remove this lint, add a comma or write the expr in a single line",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
 +    if_chain! {
 +        if !first.span.from_expansion() && !second.span.from_expansion();
 +        if let ExprKind::If(cond_expr, ..) = &first.kind;
 +        if is_block(second) || is_if(second);
 +
 +        // Proc-macros can give weird spans. Make sure this is actually an `if`.
 +        if let Some(if_snip) = snippet_opt(cx, first.span.until(cond_expr.span));
 +        if if_snip.starts_with("if");
 +
 +        // If there is a line break between the two expressions, don't lint.
 +        // If there is a non-whitespace character, this span came from a proc-macro.
 +        let else_span = first.span.between(second.span);
 +        if let Some(else_snippet) = snippet_opt(cx, else_span);
 +        if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace());
 +        then {
 +            let (looks_like, next_thing) = if is_if(second) {
 +                ("an `else if`", "the second `if`")
 +            } else {
 +                ("an `else {..}`", "the next block")
 +            };
 +
 +            span_lint_and_note(
 +                cx,
 +                SUSPICIOUS_ELSE_FORMATTING,
 +                else_span,
 +                &format!("this looks like {} but the `else` is missing", looks_like),
 +                None,
 +                &format!(
 +                    "to remove this lint, add the missing `else` or add a new line before {}",
 +                    next_thing,
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +fn is_block(expr: &Expr) -> bool {
 +    matches!(expr.kind, ExprKind::Block(..))
 +}
 +
 +/// Check if the expression is an `if` or `if let`
 +fn is_if(expr: &Expr) -> bool {
 +    matches!(expr.kind, ExprKind::If(..))
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0748ab45252ad3d8decd4ad9b8e80a99afc0f29c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
++use if_chain::if_chain;
++use rustc_ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::source_map::Spanned;
++
++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
++    /// // Bad
++    /// let x = vec![2, 3, 5];
++    /// let first_element = x.get(0);
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// // Good
++    /// 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_lint_pass!(GetFirst => [GET_FIRST]);
++
++impl<'tcx> LateLintPass<'tcx> for GetFirst {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if_chain! {
++            if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
++            if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++            if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
++
++            if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
++            if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
++
++            then {
++                let mut applicability = Applicability::MachineApplicable;
++                let slice_name = snippet_with_applicability(
++                    cx,
++                    struct_calling_on.span, "..",
++                    &mut applicability,
++                );
++                span_lint_and_sugg(
++                    cx,
++                    GET_FIRST,
++                    expr.span,
++                    &format!("accessing first element with `{0}.get(0)`", slice_name),
++                    "try",
++                    format!("{}.first()", slice_name),
++                    applicability,
++                );
++            }
++        }
++    }
++}
index 40cc5cd4bcf9df4111d202591fde32e8fb4339e6,0000000000000000000000000000000000000000..419ea5a6811b87884acd5822d0e3fb979b7803ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,150 -1,0 +1,169 @@@
- use clippy_utils::get_parent_expr;
- use clippy_utils::source::snippet;
- use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
++use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{clip, unsext};
++use rustc_errors::Applicability;
++use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
- use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
- use clippy_utils::diagnostics::span_lint;
- use clippy_utils::{clip, unsext};
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for identity operations, e.g., `x + 0`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This code can be removed without changing the
 +    /// meaning. So it just obscures what's going on. Delete it mercilessly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// x / 1 + 0 * 1 - 0 | 0;
 +    /// ```
-     ///
-     /// ### Known problems
-     /// False negatives: `f(0 + if b { 1 } else { 2 } + 3);` is reducible to
-     /// `f(if b { 1 } else { 2 } + 3);`. But the lint doesn't trigger for the code.
-     /// See [#8724](https://github.com/rust-lang/rust-clippy/issues/8724)
 +    #[clippy::version = "pre 1.29.0"]
 +    pub IDENTITY_OP,
 +    complexity,
 +    "using identity operations, e.g., `x + 0` or `y / 1`"
 +}
 +
 +declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IdentityOp {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +        if let ExprKind::Binary(cmp, left, right) = &expr.kind {
 +            if !is_allowed(cx, *cmp, left, right) {
 +                match cmp.node {
 +                    BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-                         if reducible_to_right(cx, expr, right) {
-                             check(cx, left, 0, expr.span, right.span);
-                         }
-                         check(cx, right, 0, expr.span, left.span);
++                        check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
++                        check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
 +                    },
 +                    BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
-                         check(cx, right, 0, expr.span, left.span);
++                        check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
 +                    },
 +                    BinOpKind::Mul => {
-                         if reducible_to_right(cx, expr, right) {
-                             check(cx, left, 1, expr.span, right.span);
-                         }
-                         check(cx, right, 1, expr.span, left.span);
++                        check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
++                        check(cx, right, 1, expr.span, left.span, Parens::Unneeded);
 +                    },
-                     BinOpKind::Div => check(cx, right, 1, expr.span, left.span),
++                    BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded),
 +                    BinOpKind::BitAnd => {
-                         if reducible_to_right(cx, expr, right) {
-                             check(cx, left, -1, expr.span, right.span);
-                         }
-                         check(cx, right, -1, expr.span, left.span);
-                     },
-                     BinOpKind::Rem => {
-                         // Don't call reducible_to_right because N % N is always reducible to 1
-                         check_remainder(cx, left, right, expr.span, left.span);
++                        check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
++                        check(cx, right, -1, expr.span, left.span, Parens::Unneeded);
 +                    },
++                    BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
 +                    _ => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
- /// Checks if `left op ..right` can be actually reduced to `right`
- /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
- /// cannot be reduced to `if b { 1 } else { 2 } +  if b { 3 } else { 4 }`
++#[derive(Copy, Clone)]
++enum Parens {
++    Needed,
++    Unneeded,
++}
++
++/// Checks if `left op right` needs parenthesis when reduced to `right`
++/// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
++/// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
++/// interpreted as a statement
++///
 +/// See #8724
- fn reducible_to_right(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> bool {
-     if let ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) = right.kind {
-         is_toplevel_binary(cx, binary)
-     } else {
-         true
++fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens {
++    match right.kind {
++        ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => {
++            // ensure we're checking against the leftmost expression of `right`
++            //
++            //     ~~~ `lhs`
++            // 0 + {4} * 2
++            //     ~~~~~~~ `right`
++            return needs_parenthesis(cx, binary, lhs);
++        },
++        ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {},
++        _ => return Parens::Unneeded,
 +    }
- }
 +
- fn is_toplevel_binary(cx: &LateContext<'_>, must_be_binary: &Expr<'_>) -> bool {
-     if let Some(parent) = get_parent_expr(cx, must_be_binary) && let ExprKind::Binary(..) = &parent.kind {
-         false
-     } else {
-         true
++    let mut prev_id = binary.hir_id;
++    for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) {
++        if let Node::Expr(expr) = node
++            && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind
++            && lhs.hir_id == prev_id
++        {
++            // keep going until we find a node that encompasses left of `binary`
++            prev_id = expr.hir_id;
++            continue;
++        }
++
++        match node {
++            Node::Block(_) | Node::Stmt(_) => break,
++            _ => return Parens::Unneeded,
++        };
 +    }
++
++    Parens::Needed
 +}
 +
 +fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    // This lint applies to integers
 +    !cx.typeck_results().expr_ty(left).peel_refs().is_integral()
 +        || !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
 +        // `1 << 0` is a common pattern in bit manipulation code
 +        || (cmp.node == BinOpKind::Shl
 +            && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
 +            && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
 +}
 +
 +fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
 +    let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
 +    let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
 +    if match (lhs_const, rhs_const) {
 +        (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
 +        (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
 +        _ => return,
 +    } {
-         span_ineffective_operation(cx, span, arg);
++        span_ineffective_operation(cx, span, arg, Parens::Unneeded);
 +    }
 +}
 +
- fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
++fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
 +    if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
 +        let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
 +            ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
 +            ty::Uint(uty) => clip(cx.tcx, !0, uty),
 +            _ => return,
 +        };
 +        if match m {
 +            0 => v == 0,
 +            -1 => v == check,
 +            1 => v == 1,
 +            _ => unreachable!(),
 +        } {
-             span_ineffective_operation(cx, span, arg);
++            span_ineffective_operation(cx, span, arg, parens);
 +        }
 +    }
 +}
 +
- fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) {
-     span_lint(
++fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span, parens: Parens) {
++    let mut applicability = Applicability::MachineApplicable;
++    let expr_snippet = snippet_with_applicability(cx, arg, "..", &mut applicability);
++
++    let suggestion = match parens {
++        Parens::Needed => format!("({expr_snippet})"),
++        Parens::Unneeded => expr_snippet.into_owned(),
++    };
++
++    span_lint_and_sugg(
 +        cx,
 +        IDENTITY_OP,
 +        span,
-         &format!(
-             "the operation is ineffective. Consider reducing it to `{}`",
-             snippet(cx, arg, "..")
-         ),
++        "this operation has no effect",
++        "consider reducing it to",
++        suggestion,
++        applicability,
 +    );
 +}
index 0f3889a2936188a8ffdb83aea483ce7504727785,0000000000000000000000000000000000000000..63ac092dfaf1230b0239da8f8dbdd89270673b2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,198 @@@
- use clippy_utils::diagnostics::span_lint_and_then;
 +//! lint when there is a large size difference between variants on an enum
 +
-     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Item, ItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::layout::LayoutOf;
++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 a
 +    /// 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 auxillary
++    /// structure (e.g. Arena or ECS).
++    ///
++    /// The lint will ignore generic types if the layout depends on the
++    /// generics, even if the size difference will be large anyway.
++    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// enum Test {
 +    ///     A(i32),
 +    ///     B([i32; 8000]),
 +    /// }
 +    ///
 +    /// // 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,
 +        }
 +    }
 +}
 +
 +struct FieldInfo {
 +    ind: usize,
 +    size: u64,
 +}
 +
 +struct VariantInfo {
 +    ind: usize,
 +    size: u64,
 +    fields_size: Vec<FieldInfo>,
 +}
 +
 +impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
-                         let sugg: Vec<(Span, String)> = variants_size[0]
-                             .fields_size
-                             .iter()
-                             .rev()
-                             .map_while(|val| {
-                                 if difference > self.maximum_size_difference_allowed {
-                                     difference = difference.saturating_sub(val.size);
-                                     Some((
-                                         fields[val.ind].ty.span,
-                                         format!(
-                                             "Box<{}>",
-                                             snippet_with_applicability(
-                                                 cx,
-                                                 fields[val.ind].ty.span,
-                                                 "..",
-                                                 &mut applicability
-                                             )
-                                             .into_owned()
-                                         ),
-                                     ))
-                                 } else {
-                                     None
-                                 }
-                             })
-                             .collect();
++    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.def_id);
 +            let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
 +            if adt.variants().len() <= 1 {
 +                return;
 +            }
 +            let mut variants_size: Vec<VariantInfo> = Vec::new();
 +            for (i, variant) in adt.variants().iter().enumerate() {
 +                let mut fields_size = Vec::new();
 +                for (i, f) in variant.fields.iter().enumerate() {
 +                    let ty = cx.tcx.type_of(f.did);
 +                    // don't lint variants which have a field of generic type.
 +                    match cx.layout_of(ty) {
 +                        Ok(l) => {
 +                            let fsize = l.size.bytes();
 +                            fields_size.push(FieldInfo { ind: i, size: fsize });
 +                        },
 +                        Err(_) => {
 +                            return;
 +                        },
 +                    }
 +                }
 +                let size: u64 = fields_size.iter().map(|info| info.size).sum();
 +
 +                variants_size.push(VariantInfo {
 +                    ind: i,
 +                    size,
 +                    fields_size,
 +                });
 +            }
 +
 +            variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
 +
 +            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,
 +                    def.variants[variants_size[0].ind].span,
 +                    "large size difference between variants",
 +                    |diag| {
 +                        diag.span_label(
 +                            def.variants[variants_size[0].ind].span,
 +                            &format!("this variant is {} bytes", variants_size[0].size),
 +                        );
 +                        diag.span_note(
 +                            def.variants[variants_size[1].ind].span,
 +                            &format!("and the second-largest variant is {} bytes:", variants_size[1].size),
 +                        );
 +
 +                        let fields = def.variants[variants_size[0].ind].data.fields();
 +                        variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
 +                        let mut applicability = Applicability::MaybeIncorrect;
-                         if !sugg.is_empty() {
-                             diag.multipart_suggestion(help_text, sugg, Applicability::MaybeIncorrect);
-                             return;
++                        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()
++                                .map_while(|val| {
++                                    if difference > self.maximum_size_difference_allowed {
++                                        difference = difference.saturating_sub(val.size);
++                                        Some((
++                                            fields[val.ind].ty.span,
++                                            format!(
++                                                "Box<{}>",
++                                                snippet_with_applicability(
++                                                    cx,
++                                                    fields[val.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 db09d00d7303f3e80cdd876be081a7ac664c2b29,0000000000000000000000000000000000000000..56bbbbbc819e5aa58020185a6ddee98dc0be15d0
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,160 @@@
-                 if let Some(value) = check_assign(cx, canonical_id, &*then);
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{path_to_local_id, visitors::is_local_used};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::BindingAnnotation;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for variable declarations immediately followed by a
 +    /// conditional affectation.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not idiomatic Rust.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let foo;
 +    ///
 +    /// if bar() {
 +    ///     foo = 42;
 +    /// } else {
 +    ///     foo = 0;
 +    /// }
 +    ///
 +    /// let mut baz = None;
 +    ///
 +    /// if bar() {
 +    ///     baz = Some(42);
 +    /// }
 +    /// ```
 +    ///
 +    /// should be written
 +    ///
 +    /// ```rust,ignore
 +    /// let foo = if bar() {
 +    ///     42
 +    /// } else {
 +    ///     0
 +    /// };
 +    ///
 +    /// let baz = if bar() {
 +    ///     Some(42)
 +    /// } else {
 +    ///     None
 +    /// };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_LET_IF_SEQ,
 +    nursery,
 +    "unidiomatic `let mut` declaration followed by initialization in `if`"
 +}
 +
 +declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let mut it = block.stmts.iter().peekable();
 +        while let Some(stmt) = it.next() {
 +            if_chain! {
 +                if let Some(expr) = it.peek();
 +                if let hir::StmtKind::Local(local) = stmt.kind;
 +                if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
 +                if let hir::StmtKind::Expr(if_) = expr.kind;
 +                if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
 +                if !is_local_used(cx, *cond, canonical_id);
 +                if let hir::ExprKind::Block(then, _) = then.kind;
++                if let Some(value) = check_assign(cx, canonical_id, then);
 +                if !is_local_used(cx, value, canonical_id);
 +                then {
 +                    let span = stmt.span.to(if_.span);
 +
 +                    let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze(
 +                        cx.tcx.at(span),
 +                        cx.param_env,
 +                    );
 +                    if has_interior_mutability { return; }
 +
 +                    let (default_multi_stmts, default) = if let Some(else_) = else_ {
 +                        if let hir::ExprKind::Block(else_, _) = else_.kind {
 +                            if let Some(default) = check_assign(cx, canonical_id, else_) {
 +                                (else_.stmts.len() > 1, default)
 +                            } else if let Some(default) = local.init {
 +                                (true, default)
 +                            } else {
 +                                continue;
 +                            }
 +                        } else {
 +                            continue;
 +                        }
 +                    } else if let Some(default) = local.init {
 +                        (false, default)
 +                    } else {
 +                        continue;
 +                    };
 +
 +                    let mutability = match mode {
 +                        BindingAnnotation::RefMut | BindingAnnotation::Mutable => "<mut> ",
 +                        _ => "",
 +                    };
 +
 +                    // FIXME: this should not suggest `mut` if we can detect that the variable is not
 +                    // use mutably after the `if`
 +
 +                    let sug = format!(
 +                        "let {mut}{name} = if {cond} {{{then} {value} }} else {{{else} {default} }};",
 +                        mut=mutability,
 +                        name=ident.name,
 +                        cond=snippet(cx, cond.span, "_"),
 +                        then=if then.stmts.len() > 1 { " ..;" } else { "" },
 +                        else=if default_multi_stmts { " ..;" } else { "" },
 +                        value=snippet(cx, value.span, "<value>"),
 +                        default=snippet(cx, default.span, "<default>"),
 +                    );
 +                    span_lint_and_then(cx,
 +                                       USELESS_LET_IF_SEQ,
 +                                       span,
 +                                       "`if _ { .. } else { .. }` is an expression",
 +                                       |diag| {
 +                                           diag.span_suggestion(
 +                                                span,
 +                                                "it is more idiomatic to write",
 +                                                sug,
 +                                                Applicability::HasPlaceholders,
 +                                            );
 +                                           if !mutability.is_empty() {
 +                                               diag.note("you might not need `mut` at all");
 +                                           }
 +                                       });
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_assign<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    decl: hir::HirId,
 +    block: &'tcx hir::Block<'_>,
 +) -> Option<&'tcx hir::Expr<'tcx>> {
 +    if_chain! {
 +        if block.expr.is_none();
 +        if let Some(expr) = block.stmts.iter().last();
 +        if let hir::StmtKind::Semi(expr) = expr.kind;
 +        if let hir::ExprKind::Assign(var, value, _) = expr.kind;
 +        if path_to_local_id(var, decl);
 +        then {
 +            if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) {
 +                None
 +            } else {
 +                Some(value)
 +            }
 +        } else {
 +            None
 +        }
 +    }
 +}
index be5c478900facbb8551c7f4532c28631d25b2bc8,0000000000000000000000000000000000000000..d4ec046d0bb08b2100dc3cc585123b473c2ce2d2
mode 100644,000000..100644
--- /dev/null
@@@ -1,340 -1,0 +1,346 @@@
-     LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
 +// 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.
 +
 +store.register_group(true, "clippy::all", Some("clippy_all"), vec![
 +    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
++    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(bit_mask::BAD_BIT_MASK),
 +    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
 +    LintId::of(blacklisted_name::BLACKLISTED_NAME),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(booleans::LOGIC_BUG),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
++    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
 +    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
-     LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(duration_subsec::DURATION_SUBSEC),
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eq_op::EQ_OP),
 +    LintId::of(eq_op::OP_REF),
 +    LintId::of(erasing_op::ERASING_OP),
 +    LintId::of(escape::BOXED_LOCAL),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
-     LintId::of(manual_map::MANUAL_MAP),
++    LintId::of(get_first::GET_FIRST),
 +    LintId::of(identity_op::IDENTITY_OP),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
-     LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_strip::MANUAL_STRIP),
-     LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
-     LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
++    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
++    LintId::of(matches::MANUAL_MAP),
++    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
++    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
++    LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
++    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
++    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::CMP_NAN),
 +    LintId::of(misc::CMP_OWNED),
 +    LintId::of(misc::MODULO_ONE),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +    LintId::of(ptr::MUT_FROM_REF),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(ptr_eq::PTR_EQ),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(repeat_once::REPEAT_ONCE),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_assignment::SELF_ASSIGNMENT),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    LintId::of(swap::ALMOST_SWAPPED),
 +    LintId::of(swap::MANUAL_SWAP),
++    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
++    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
 +    LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(unwrap::PANICKING_UNWRAP),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(vec::USELESS_VEC),
 +    LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index b15c979d0c70e64ad61f7ed8c752e192fed05d4b,0000000000000000000000000000000000000000..4f1c3673f853cd9cf8fb451c2f3a2c8ac4e01985
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,103 @@@
-     LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +// 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.
 +
 +store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
++    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
 +    LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(duration_subsec::DURATION_SUBSEC),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
-     LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +    LintId::of(identity_op::IDENTITY_OP),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
++    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
++    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OR_THEN_UNWRAP),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(repeat_once::REPEAT_ONCE),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
++    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index 6bf2c4bbaedc024ad45ac7970c6fc030be7c9d67,0000000000000000000000000000000000000000..50cdd0af923051f2453dc36a62f3506f94a6bbac
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-     LintId::of(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
 +// 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.
 +
 +store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
 +    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
 +    LintId::of(bit_mask::BAD_BIT_MASK),
 +    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
 +    LintId::of(booleans::LOGIC_BUG),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(eq_op::EQ_OP),
 +    LintId::of(erasing_op::ERASING_OP),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
++    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::CMP_NAN),
 +    LintId::of(misc::MODULO_ONE),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
 +    LintId::of(ptr::INVALID_NULL_PTR_USAGE),
 +    LintId::of(ptr::MUT_FROM_REF),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(self_assignment::SELF_ASSIGNMENT),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(swap::ALMOST_SWAPPED),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
 +    LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
 +    LintId::of(unwrap::PANICKING_UNWRAP),
 +    LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +])
index 5552ea8aa80ac57655c8cbf8a02ce42d037404f6,0000000000000000000000000000000000000000..b927ba3b17c0ec04ae34fd75ebfa46b9b130ff06
mode 100644,000000..100644
--- /dev/null
@@@ -1,572 -1,0 +1,581 @@@
-     arithmetic::FLOAT_ARITHMETIC,
-     arithmetic::INTEGER_ARITHMETIC,
 +// 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.
 +
 +store.register_lints(&[
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_LINT,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_PATHS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::PRODUCE_ICE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +    absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
++    almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
 +    approx_const::APPROX_CONSTANT,
-     collapsible_match::COLLAPSIBLE_MATCH,
 +    as_conversions::AS_CONVERSIONS,
++    as_underscore::AS_UNDERSCORE,
 +    asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +    asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +    assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +    assign_ops::ASSIGN_OP_PATTERN,
 +    assign_ops::MISREFACTORED_ASSIGN_OP,
 +    async_yields_async::ASYNC_YIELDS_ASYNC,
 +    attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    attrs::DEPRECATED_CFG_ATTR,
 +    attrs::DEPRECATED_SEMVER,
 +    attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
 +    attrs::INLINE_ALWAYS,
 +    attrs::MISMATCHED_TARGET_OS,
 +    attrs::USELESS_ATTRIBUTE,
 +    await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
 +    await_holding_invalid::AWAIT_HOLDING_LOCK,
 +    await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
 +    bit_mask::BAD_BIT_MASK,
 +    bit_mask::INEFFECTIVE_BIT_MASK,
 +    bit_mask::VERBOSE_BIT_MASK,
 +    blacklisted_name::BLACKLISTED_NAME,
 +    blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +    bool_assert_comparison::BOOL_ASSERT_COMPARISON,
 +    booleans::LOGIC_BUG,
 +    booleans::NONMINIMAL_BOOL,
 +    borrow_as_ptr::BORROW_AS_PTR,
++    borrow_deref_ref::BORROW_DEREF_REF,
 +    bytecount::NAIVE_BYTECOUNT,
 +    bytes_count_to_len::BYTES_COUNT_TO_LEN,
 +    cargo::CARGO_COMMON_METADATA,
 +    cargo::MULTIPLE_CRATE_VERSIONS,
 +    cargo::NEGATIVE_FEATURE_NAMES,
 +    cargo::REDUNDANT_FEATURE_NAMES,
 +    cargo::WILDCARD_DEPENDENCIES,
 +    case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    casts::CAST_ABS_TO_UNSIGNED,
 +    casts::CAST_ENUM_CONSTRUCTOR,
 +    casts::CAST_ENUM_TRUNCATION,
 +    casts::CAST_LOSSLESS,
 +    casts::CAST_POSSIBLE_TRUNCATION,
 +    casts::CAST_POSSIBLE_WRAP,
 +    casts::CAST_PRECISION_LOSS,
 +    casts::CAST_PTR_ALIGNMENT,
 +    casts::CAST_REF_TO_MUT,
 +    casts::CAST_SIGN_LOSS,
 +    casts::CAST_SLICE_DIFFERENT_SIZES,
 +    casts::CHAR_LIT_AS_U8,
 +    casts::FN_TO_NUMERIC_CAST,
 +    casts::FN_TO_NUMERIC_CAST_ANY,
 +    casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    casts::PTR_AS_PTR,
 +    casts::UNNECESSARY_CAST,
 +    checked_conversions::CHECKED_CONVERSIONS,
 +    cognitive_complexity::COGNITIVE_COMPLEXITY,
 +    collapsible_if::COLLAPSIBLE_ELSE_IF,
 +    collapsible_if::COLLAPSIBLE_IF,
-     get_last_with_len::GET_LAST_WITH_LEN,
 +    comparison_chain::COMPARISON_CHAIN,
 +    copies::BRANCHES_SHARING_CODE,
 +    copies::IFS_SAME_COND,
 +    copies::IF_SAME_THEN_ELSE,
 +    copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +    copy_iterator::COPY_ITERATOR,
 +    crate_in_macro_def::CRATE_IN_MACRO_DEF,
 +    create_dir::CREATE_DIR,
 +    dbg_macro::DBG_MACRO,
 +    default::DEFAULT_TRAIT_ACCESS,
 +    default::FIELD_REASSIGN_WITH_DEFAULT,
 +    default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +    default_union_representation::DEFAULT_UNION_REPRESENTATION,
 +    dereference::EXPLICIT_DEREF_METHODS,
 +    dereference::NEEDLESS_BORROW,
 +    dereference::REF_BINDING_TO_REFERENCE,
 +    derivable_impls::DERIVABLE_IMPLS,
 +    derive::DERIVE_HASH_XOR_EQ,
 +    derive::DERIVE_ORD_XOR_PARTIAL_ORD,
 +    derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    derive::EXPL_IMPL_CLONE_ON_COPY,
 +    derive::UNSAFE_DERIVE_DESERIALIZE,
 +    disallowed_methods::DISALLOWED_METHODS,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_types::DISALLOWED_TYPES,
 +    doc::DOC_MARKDOWN,
 +    doc::MISSING_ERRORS_DOC,
 +    doc::MISSING_PANICS_DOC,
 +    doc::MISSING_SAFETY_DOC,
 +    doc::NEEDLESS_DOCTEST_MAIN,
++    doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
 +    double_comparison::DOUBLE_COMPARISONS,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_NON_DROP,
 +    drop_forget_ref::DROP_REF,
 +    drop_forget_ref::FORGET_COPY,
 +    drop_forget_ref::FORGET_NON_DROP,
 +    drop_forget_ref::FORGET_REF,
 +    drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
 +    duplicate_mod::DUPLICATE_MOD,
 +    duration_subsec::DURATION_SUBSEC,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +    empty_drop::EMPTY_DROP,
 +    empty_enum::EMPTY_ENUM,
 +    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
 +    entry::MAP_ENTRY,
 +    enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +    enum_variants::ENUM_VARIANT_NAMES,
 +    enum_variants::MODULE_INCEPTION,
 +    enum_variants::MODULE_NAME_REPETITIONS,
 +    eq_op::EQ_OP,
 +    eq_op::OP_REF,
 +    equatable_if_let::EQUATABLE_IF_LET,
 +    erasing_op::ERASING_OP,
 +    escape::BOXED_LOCAL,
 +    eta_reduction::REDUNDANT_CLOSURE,
 +    eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +    excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +    exhaustive_items::EXHAUSTIVE_ENUMS,
 +    exhaustive_items::EXHAUSTIVE_STRUCTS,
 +    exit::EXIT,
 +    explicit_write::EXPLICIT_WRITE,
 +    fallible_impl_from::FALLIBLE_IMPL_FROM,
 +    float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
 +    float_literal::EXCESSIVE_PRECISION,
 +    float_literal::LOSSY_FLOAT_LITERAL,
 +    floating_point_arithmetic::IMPRECISE_FLOPS,
 +    floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +    format::USELESS_FORMAT,
 +    format_args::FORMAT_IN_FORMAT_ARGS,
 +    format_args::TO_STRING_IN_FORMAT_ARGS,
 +    format_impl::PRINT_IN_FORMAT_IMPL,
 +    format_impl::RECURSIVE_FORMAT_IMPL,
 +    format_push_string::FORMAT_PUSH_STRING,
 +    formatting::POSSIBLE_MISSING_COMMA,
 +    formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    formatting::SUSPICIOUS_ELSE_FORMATTING,
 +    formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +    from_over_into::FROM_OVER_INTO,
 +    from_str_radix_10::FROM_STR_RADIX_10,
 +    functions::DOUBLE_MUST_USE,
 +    functions::MUST_USE_CANDIDATE,
 +    functions::MUST_USE_UNIT,
 +    functions::NOT_UNSAFE_PTR_ARG_DEREF,
 +    functions::RESULT_UNIT_ERR,
 +    functions::TOO_MANY_ARGUMENTS,
 +    functions::TOO_MANY_LINES,
 +    future_not_send::FUTURE_NOT_SEND,
-     manual_map::MANUAL_MAP,
++    get_first::GET_FIRST,
 +    identity_op::IDENTITY_OP,
 +    if_let_mutex::IF_LET_MUTEX,
 +    if_not_else::IF_NOT_ELSE,
 +    if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
 +    implicit_hasher::IMPLICIT_HASHER,
 +    implicit_return::IMPLICIT_RETURN,
 +    implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
 +    inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
 +    index_refutable_slice::INDEX_REFUTABLE_SLICE,
 +    indexing_slicing::INDEXING_SLICING,
 +    indexing_slicing::OUT_OF_BOUNDS_INDEXING,
 +    infinite_iter::INFINITE_ITER,
 +    infinite_iter::MAYBE_INFINITE_ITER,
 +    inherent_impl::MULTIPLE_INHERENT_IMPL,
 +    inherent_to_string::INHERENT_TO_STRING,
 +    inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    init_numbered_fields::INIT_NUMBERED_FIELDS,
 +    inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +    int_plus_one::INT_PLUS_ONE,
 +    integer_division::INTEGER_DIVISION,
 +    invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
 +    items_after_statements::ITEMS_AFTER_STATEMENTS,
 +    iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
 +    large_const_arrays::LARGE_CONST_ARRAYS,
 +    large_enum_variant::LARGE_ENUM_VARIANT,
 +    large_include_file::LARGE_INCLUDE_FILE,
 +    large_stack_arrays::LARGE_STACK_ARRAYS,
 +    len_zero::COMPARISON_TO_EMPTY,
 +    len_zero::LEN_WITHOUT_IS_EMPTY,
 +    len_zero::LEN_ZERO,
 +    let_if_seq::USELESS_LET_IF_SEQ,
 +    let_underscore::LET_UNDERSCORE_DROP,
 +    let_underscore::LET_UNDERSCORE_LOCK,
 +    let_underscore::LET_UNDERSCORE_MUST_USE,
 +    lifetimes::EXTRA_UNUSED_LIFETIMES,
 +    lifetimes::NEEDLESS_LIFETIMES,
 +    literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +    literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +    literal_representation::LARGE_DIGIT_GROUPS,
 +    literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +    literal_representation::UNREADABLE_LITERAL,
 +    literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +    loops::EMPTY_LOOP,
 +    loops::EXPLICIT_COUNTER_LOOP,
 +    loops::EXPLICIT_INTO_ITER_LOOP,
 +    loops::EXPLICIT_ITER_LOOP,
 +    loops::FOR_KV_MAP,
 +    loops::FOR_LOOPS_OVER_FALLIBLES,
 +    loops::ITER_NEXT_LOOP,
 +    loops::MANUAL_FLATTEN,
 +    loops::MANUAL_MEMCPY,
 +    loops::MISSING_SPIN_LOOP,
 +    loops::MUT_RANGE_BOUND,
 +    loops::NEEDLESS_COLLECT,
 +    loops::NEEDLESS_RANGE_LOOP,
 +    loops::NEVER_LOOP,
 +    loops::SAME_ITEM_PUSH,
 +    loops::SINGLE_ELEMENT_LOOP,
 +    loops::WHILE_IMMUTABLE_CONDITION,
 +    loops::WHILE_LET_LOOP,
 +    loops::WHILE_LET_ON_ITERATOR,
 +    macro_use::MACRO_USE_IMPORTS,
 +    main_recursion::MAIN_RECURSION,
 +    manual_assert::MANUAL_ASSERT,
 +    manual_async_fn::MANUAL_ASYNC_FN,
 +    manual_bits::MANUAL_BITS,
-     manual_unwrap_or::MANUAL_UNWRAP_OR,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +    manual_ok_or::MANUAL_OK_OR,
 +    manual_strip::MANUAL_STRIP,
-     match_on_vec_items::MATCH_ON_VEC_ITEMS,
 +    map_clone::MAP_CLONE,
 +    map_err_ignore::MAP_ERR_IGNORE,
 +    map_unit_fn::OPTION_MAP_UNIT_FN,
 +    map_unit_fn::RESULT_MAP_UNIT_FN,
-     match_str_case_mismatch::MATCH_STR_CASE_MISMATCH,
 +    match_result_ok::MATCH_RESULT_OK,
-     significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE,
++    matches::COLLAPSIBLE_MATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
++    matches::MANUAL_MAP,
++    matches::MANUAL_UNWRAP_OR,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
++    matches::MATCH_ON_VEC_ITEMS,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
++    matches::MATCH_STR_CASE_MISMATCH,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::NEEDLESS_MATCH,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
++    matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
++    matches::TRY_ERR,
 +    matches::WILDCARD_ENUM_MATCH_ARM,
 +    matches::WILDCARD_IN_OR_PATTERNS,
 +    mem_forget::MEM_FORGET,
 +    mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +    mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +    mem_replace::MEM_REPLACE_WITH_UNINIT,
 +    methods::BIND_INSTEAD_OF_MAP,
 +    methods::BYTES_NTH,
 +    methods::CHARS_LAST_CMP,
 +    methods::CHARS_NEXT_CMP,
 +    methods::CLONED_INSTEAD_OF_COPIED,
 +    methods::CLONE_DOUBLE_REF,
 +    methods::CLONE_ON_COPY,
 +    methods::CLONE_ON_REF_PTR,
 +    methods::ERR_EXPECT,
 +    methods::EXPECT_FUN_CALL,
 +    methods::EXPECT_USED,
 +    methods::EXTEND_WITH_DRAIN,
 +    methods::FILETYPE_IS_FILE,
 +    methods::FILTER_MAP_IDENTITY,
 +    methods::FILTER_MAP_NEXT,
 +    methods::FILTER_NEXT,
 +    methods::FLAT_MAP_IDENTITY,
 +    methods::FLAT_MAP_OPTION,
 +    methods::FROM_ITER_INSTEAD_OF_COLLECT,
++    methods::GET_LAST_WITH_LEN,
 +    methods::GET_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
 +    methods::IS_DIGIT_ASCII_RADIX,
 +    methods::ITERATOR_STEP_BY_ZERO,
 +    methods::ITER_CLONED_COLLECT,
 +    methods::ITER_COUNT,
 +    methods::ITER_NEXT_SLICE,
 +    methods::ITER_NTH,
 +    methods::ITER_NTH_ZERO,
 +    methods::ITER_OVEREAGER_CLONED,
 +    methods::ITER_SKIP_NEXT,
 +    methods::ITER_WITH_DRAIN,
 +    methods::MANUAL_FILTER_MAP,
 +    methods::MANUAL_FIND_MAP,
 +    methods::MANUAL_SATURATING_ARITHMETIC,
 +    methods::MANUAL_SPLIT_ONCE,
 +    methods::MANUAL_STR_REPEAT,
 +    methods::MAP_COLLECT_RESULT_UNIT,
 +    methods::MAP_FLATTEN,
 +    methods::MAP_IDENTITY,
 +    methods::MAP_UNWRAP_OR,
 +    methods::NEEDLESS_OPTION_AS_DEREF,
 +    methods::NEEDLESS_OPTION_TAKE,
 +    methods::NEEDLESS_SPLITN,
 +    methods::NEW_RET_NO_SELF,
++    methods::NO_EFFECT_REPLACE,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    methods::OR_THEN_UNWRAP,
 +    methods::RESULT_MAP_OR_INTO_OPTION,
 +    methods::SEARCH_IS_SOME,
 +    methods::SHOULD_IMPLEMENT_TRAIT,
 +    methods::SINGLE_CHAR_ADD_STR,
 +    methods::SINGLE_CHAR_PATTERN,
 +    methods::SKIP_WHILE_NEXT,
 +    methods::STRING_EXTEND_CHARS,
 +    methods::SUSPICIOUS_MAP,
 +    methods::SUSPICIOUS_SPLITN,
 +    methods::UNINIT_ASSUMED_INIT,
 +    methods::UNNECESSARY_FILTER_MAP,
 +    methods::UNNECESSARY_FIND_MAP,
 +    methods::UNNECESSARY_FOLD,
 +    methods::UNNECESSARY_JOIN,
 +    methods::UNNECESSARY_LAZY_EVALUATIONS,
 +    methods::UNNECESSARY_TO_OWNED,
 +    methods::UNWRAP_OR_ELSE_DEFAULT,
 +    methods::UNWRAP_USED,
 +    methods::USELESS_ASREF,
 +    methods::WRONG_SELF_CONVENTION,
 +    methods::ZST_OFFSET,
 +    minmax::MIN_MAX,
 +    misc::CMP_NAN,
 +    misc::CMP_OWNED,
 +    misc::FLOAT_CMP,
 +    misc::FLOAT_CMP_CONST,
 +    misc::MODULO_ONE,
 +    misc::SHORT_CIRCUIT_STATEMENT,
 +    misc::TOPLEVEL_REF_ARG,
 +    misc::USED_UNDERSCORE_BINDING,
 +    misc::ZERO_PTR,
 +    misc_early::BUILTIN_TYPE_SHADOW,
 +    misc_early::DOUBLE_NEG,
 +    misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +    misc_early::MIXED_CASE_HEX_LITERALS,
 +    misc_early::REDUNDANT_PATTERN,
 +    misc_early::SEPARATED_LITERAL_SUFFIX,
 +    misc_early::UNNEEDED_FIELD_PATTERN,
 +    misc_early::UNNEEDED_WILDCARD_PATTERN,
 +    misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +    misc_early::ZERO_PREFIXED_LITERAL,
++    mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
 +    missing_const_for_fn::MISSING_CONST_FOR_FN,
 +    missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +    missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +    missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
 +    mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
 +    modulo_arithmetic::MODULO_ARITHMETIC,
 +    mut_key::MUTABLE_KEY_TYPE,
 +    mut_mut::MUT_MUT,
 +    mut_mutex_lock::MUT_MUTEX_LOCK,
 +    mut_reference::UNNECESSARY_MUT_PASSED,
 +    mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
 +    mutex_atomic::MUTEX_ATOMIC,
 +    mutex_atomic::MUTEX_INTEGER,
 +    needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
 +    needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
 +    needless_bool::BOOL_COMPARISON,
 +    needless_bool::NEEDLESS_BOOL,
 +    needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +    needless_continue::NEEDLESS_CONTINUE,
 +    needless_for_each::NEEDLESS_FOR_EACH,
 +    needless_late_init::NEEDLESS_LATE_INIT,
 +    needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +    needless_question_mark::NEEDLESS_QUESTION_MARK,
 +    needless_update::NEEDLESS_UPDATE,
 +    neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +    neg_multiply::NEG_MULTIPLY,
 +    new_without_default::NEW_WITHOUT_DEFAULT,
 +    no_effect::NO_EFFECT,
 +    no_effect::NO_EFFECT_UNDERSCORE_BINDING,
 +    no_effect::UNNECESSARY_OPERATION,
 +    non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
 +    non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
 +    non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
 +    non_expressive_names::MANY_SINGLE_CHAR_NAMES,
 +    non_expressive_names::SIMILAR_NAMES,
 +    non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
 +    non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
 +    nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
++    numeric_arithmetic::FLOAT_ARITHMETIC,
++    numeric_arithmetic::INTEGER_ARITHMETIC,
 +    octal_escapes::OCTAL_ESCAPES,
 +    only_used_in_recursion::ONLY_USED_IN_RECURSION,
 +    open_options::NONSENSICAL_OPEN_OPTIONS,
 +    option_env_unwrap::OPTION_ENV_UNWRAP,
 +    option_if_let_else::OPTION_IF_LET_ELSE,
 +    overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
 +    panic_in_result_fn::PANIC_IN_RESULT_FN,
 +    panic_unimplemented::PANIC,
 +    panic_unimplemented::TODO,
 +    panic_unimplemented::UNIMPLEMENTED,
 +    panic_unimplemented::UNREACHABLE,
 +    partialeq_ne_impl::PARTIALEQ_NE_IMPL,
 +    pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +    pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
 +    path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
 +    pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +    precedence::PRECEDENCE,
 +    ptr::CMP_NULL,
 +    ptr::INVALID_NULL_PTR_USAGE,
 +    ptr::MUT_FROM_REF,
 +    ptr::PTR_ARG,
 +    ptr_eq::PTR_EQ,
 +    ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +    pub_use::PUB_USE,
 +    question_mark::QUESTION_MARK,
 +    ranges::MANUAL_RANGE_CONTAINS,
 +    ranges::RANGE_MINUS_ONE,
 +    ranges::RANGE_PLUS_ONE,
 +    ranges::RANGE_ZIP_WITH_LEN,
 +    ranges::REVERSED_EMPTY_RANGES,
 +    rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
 +    redundant_clone::REDUNDANT_CLONE,
 +    redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +    redundant_else::REDUNDANT_ELSE,
 +    redundant_field_names::REDUNDANT_FIELD_NAMES,
 +    redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +    redundant_slicing::DEREF_BY_SLICING,
 +    redundant_slicing::REDUNDANT_SLICING,
 +    redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +    ref_option_ref::REF_OPTION_REF,
 +    reference::DEREF_ADDROF,
 +    regex::INVALID_REGEX,
 +    regex::TRIVIAL_REGEX,
 +    repeat_once::REPEAT_ONCE,
 +    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
 +    returns::LET_AND_RETURN,
 +    returns::NEEDLESS_RETURN,
 +    same_name_method::SAME_NAME_METHOD,
 +    self_assignment::SELF_ASSIGNMENT,
 +    self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +    semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +    serde_api::SERDE_API_MISUSE,
 +    shadow::SHADOW_REUSE,
 +    shadow::SHADOW_SAME,
 +    shadow::SHADOW_UNRELATED,
-     try_err::TRY_ERR,
 +    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
 +    single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +    size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +    slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
 +    stable_sort_primitive::STABLE_SORT_PRIMITIVE,
 +    strings::STRING_ADD,
 +    strings::STRING_ADD_ASSIGN,
 +    strings::STRING_FROM_UTF8_AS_BYTES,
 +    strings::STRING_LIT_AS_BYTES,
 +    strings::STRING_SLICE,
 +    strings::STRING_TO_STRING,
 +    strings::STR_TO_STRING,
 +    strings::TRIM_SPLIT_WHITESPACE,
 +    strlen_on_c_strings::STRLEN_ON_C_STRINGS,
 +    suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
 +    suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
 +    suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
 +    swap::ALMOST_SWAPPED,
 +    swap::MANUAL_SWAP,
++    swap_ptr_to_ref::SWAP_PTR_TO_REF,
 +    tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +    temporary_assignment::TEMPORARY_ASSIGNMENT,
 +    to_digit_is_some::TO_DIGIT_IS_SOME,
 +    trailing_empty_array::TRAILING_EMPTY_ARRAY,
 +    trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
 +    trait_bounds::TYPE_REPETITION_IN_BOUNDS,
 +    transmute::CROSSPOINTER_TRANSMUTE,
 +    transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    transmute::TRANSMUTE_BYTES_TO_STR,
 +    transmute::TRANSMUTE_FLOAT_TO_INT,
 +    transmute::TRANSMUTE_INT_TO_BOOL,
 +    transmute::TRANSMUTE_INT_TO_CHAR,
 +    transmute::TRANSMUTE_INT_TO_FLOAT,
 +    transmute::TRANSMUTE_NUM_TO_BYTES,
 +    transmute::TRANSMUTE_PTR_TO_PTR,
 +    transmute::TRANSMUTE_PTR_TO_REF,
 +    transmute::TRANSMUTE_UNDEFINED_REPR,
 +    transmute::UNSOUND_COLLECTION_TRANSMUTE,
 +    transmute::USELESS_TRANSMUTE,
 +    transmute::WRONG_TRANSMUTE,
 +    transmuting_null::TRANSMUTING_NULL,
 +    types::BORROWED_BOX,
 +    types::BOX_COLLECTION,
 +    types::LINKEDLIST,
 +    types::OPTION_OPTION,
 +    types::RC_BUFFER,
 +    types::RC_MUTEX,
 +    types::REDUNDANT_ALLOCATION,
 +    types::TYPE_COMPLEXITY,
 +    types::VEC_BOX,
 +    undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
 +    unicode::INVISIBLE_CHARACTERS,
 +    unicode::NON_ASCII_LITERAL,
 +    unicode::UNICODE_NOT_NFC,
 +    uninit_vec::UNINIT_VEC,
 +    unit_hash::UNIT_HASH,
 +    unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
 +    unit_types::LET_UNIT_VALUE,
 +    unit_types::UNIT_ARG,
 +    unit_types::UNIT_CMP,
 +    unnamed_address::FN_ADDRESS_COMPARISONS,
 +    unnamed_address::VTABLE_ADDRESS_COMPARISONS,
 +    unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
 +    unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
 +    unnecessary_sort_by::UNNECESSARY_SORT_BY,
 +    unnecessary_wraps::UNNECESSARY_WRAPS,
 +    unnested_or_patterns::UNNESTED_OR_PATTERNS,
 +    unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
 +    unused_async::UNUSED_ASYNC,
 +    unused_io_amount::UNUSED_IO_AMOUNT,
++    unused_rounding::UNUSED_ROUNDING,
 +    unused_self::UNUSED_SELF,
 +    unused_unit::UNUSED_UNIT,
 +    unwrap::PANICKING_UNWRAP,
 +    unwrap::UNNECESSARY_UNWRAP,
 +    unwrap_in_result::UNWRAP_IN_RESULT,
 +    upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +    use_self::USE_SELF,
 +    useless_conversion::USELESS_CONVERSION,
 +    vec::USELESS_VEC,
 +    vec_init_then_push::VEC_INIT_THEN_PUSH,
 +    vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
 +    verbose_file_reads::VERBOSE_FILE_READS,
 +    wildcard_imports::ENUM_GLOB_USE,
 +    wildcard_imports::WILDCARD_IMPORTS,
 +    write::PRINTLN_EMPTY_STRING,
 +    write::PRINT_LITERAL,
 +    write::PRINT_STDERR,
 +    write::PRINT_STDOUT,
 +    write::PRINT_WITH_NEWLINE,
 +    write::USE_DEBUG,
 +    write::WRITELN_EMPTY_STRING,
 +    write::WRITE_LITERAL,
 +    write::WRITE_WITH_NEWLINE,
 +    zero_div_zero::ZERO_DIVIDED_BY_ZERO,
 +    zero_sized_map_values::ZERO_SIZED_MAP_VALUES,
 +])
index 34d1555049da58bbe21e562bec1cd0b3c123a9a6,0000000000000000000000000000000000000000..642d629971d90b678b0509bac6daec53d6882356
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,34 @@@
-     LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
 +// 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.
 +
 +store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
 +    LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
 +    LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
 +    LintId::of(copies::BRANCHES_SHARING_CODE),
 +    LintId::of(equatable_if_let::EQUATABLE_IF_LET),
 +    LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
 +    LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
 +    LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
 +    LintId::of(future_not_send::FUTURE_NOT_SEND),
 +    LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
 +    LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
 +    LintId::of(methods::ITER_WITH_DRAIN),
 +    LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
 +    LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
 +    LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +    LintId::of(mutex_atomic::MUTEX_INTEGER),
 +    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
 +    LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
 +    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
 +    LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
 +    LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
 +    LintId::of(regex::TRIVIAL_REGEX),
-     LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(strings::STRING_LIT_AS_BYTES),
 +    LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +    LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
 +    LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
++    LintId::of(unused_rounding::UNUSED_ROUNDING),
 +    LintId::of(use_self::USE_SELF),
 +])
index 63232fd41130538765c6b4f547c8c9ba7e9d9ab8,0000000000000000000000000000000000000000..48de92ae9452315992114a26fcb9a23df788435f
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,102 @@@
-     LintId::of(match_on_vec_items::MATCH_ON_VEC_ITEMS),
 +// 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.
 +
 +store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
 +    LintId::of(attrs::INLINE_ALWAYS),
 +    LintId::of(bit_mask::VERBOSE_BIT_MASK),
 +    LintId::of(borrow_as_ptr::BORROW_AS_PTR),
 +    LintId::of(bytecount::NAIVE_BYTECOUNT),
 +    LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +    LintId::of(casts::CAST_LOSSLESS),
 +    LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
 +    LintId::of(casts::CAST_POSSIBLE_WRAP),
 +    LintId::of(casts::CAST_PRECISION_LOSS),
 +    LintId::of(casts::CAST_PTR_ALIGNMENT),
 +    LintId::of(casts::CAST_SIGN_LOSS),
 +    LintId::of(casts::PTR_AS_PTR),
 +    LintId::of(checked_conversions::CHECKED_CONVERSIONS),
 +    LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
 +    LintId::of(copy_iterator::COPY_ITERATOR),
 +    LintId::of(default::DEFAULT_TRAIT_ACCESS),
 +    LintId::of(dereference::EXPLICIT_DEREF_METHODS),
 +    LintId::of(dereference::REF_BINDING_TO_REFERENCE),
 +    LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
 +    LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
 +    LintId::of(doc::DOC_MARKDOWN),
 +    LintId::of(doc::MISSING_ERRORS_DOC),
 +    LintId::of(doc::MISSING_PANICS_DOC),
++    LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
 +    LintId::of(empty_enum::EMPTY_ENUM),
 +    LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
 +    LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
 +    LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
 +    LintId::of(functions::MUST_USE_CANDIDATE),
 +    LintId::of(functions::TOO_MANY_LINES),
 +    LintId::of(if_not_else::IF_NOT_ELSE),
 +    LintId::of(implicit_hasher::IMPLICIT_HASHER),
 +    LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +    LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +    LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
 +    LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
 +    LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
 +    LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
 +    LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
 +    LintId::of(let_underscore::LET_UNDERSCORE_DROP),
 +    LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
 +    LintId::of(literal_representation::UNREADABLE_LITERAL),
 +    LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
 +    LintId::of(loops::EXPLICIT_ITER_LOOP),
 +    LintId::of(macro_use::MACRO_USE_IMPORTS),
 +    LintId::of(manual_assert::MANUAL_ASSERT),
 +    LintId::of(manual_ok_or::MANUAL_OK_OR),
 +    LintId::of(matches::MATCH_BOOL),
++    LintId::of(matches::MATCH_ON_VEC_ITEMS),
 +    LintId::of(matches::MATCH_SAME_ARMS),
 +    LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
 +    LintId::of(matches::MATCH_WILD_ERR_ARM),
 +    LintId::of(matches::SINGLE_MATCH_ELSE),
 +    LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
 +    LintId::of(methods::FILTER_MAP_NEXT),
 +    LintId::of(methods::FLAT_MAP_OPTION),
 +    LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
 +    LintId::of(methods::IMPLICIT_CLONE),
 +    LintId::of(methods::INEFFICIENT_TO_STRING),
 +    LintId::of(methods::MAP_UNWRAP_OR),
 +    LintId::of(methods::UNNECESSARY_JOIN),
 +    LintId::of(misc::FLOAT_CMP),
 +    LintId::of(misc::USED_UNDERSCORE_BINDING),
++    LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
 +    LintId::of(mut_mut::MUT_MUT),
 +    LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
 +    LintId::of(needless_continue::NEEDLESS_CONTINUE),
 +    LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
 +    LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
 +    LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
 +    LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
 +    LintId::of(non_expressive_names::SIMILAR_NAMES),
 +    LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
 +    LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
 +    LintId::of(ranges::RANGE_MINUS_ONE),
 +    LintId::of(ranges::RANGE_PLUS_ONE),
 +    LintId::of(redundant_else::REDUNDANT_ELSE),
 +    LintId::of(ref_option_ref::REF_OPTION_REF),
 +    LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
 +    LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
 +    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +    LintId::of(strings::STRING_ADD_ASSIGN),
 +    LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
 +    LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
 +    LintId::of(types::LINKEDLIST),
 +    LintId::of(types::OPTION_OPTION),
 +    LintId::of(unicode::UNICODE_NOT_NFC),
 +    LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS),
 +    LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS),
 +    LintId::of(unused_async::UNUSED_ASYNC),
 +    LintId::of(unused_self::UNUSED_SELF),
 +    LintId::of(wildcard_imports::ENUM_GLOB_USE),
 +    LintId::of(wildcard_imports::WILDCARD_IMPORTS),
 +    LintId::of(zero_sized_map_values::ZERO_SIZED_MAP_VALUES),
 +])
index a6d3a06dc16e34063572e52e326c30ce4a369848,0000000000000000000000000000000000000000..3695012f552389f7d7e7062218c293aac6387db9
mode 100644,000000..100644
--- /dev/null
@@@ -1,81 -1,0 +1,82 @@@
-     LintId::of(arithmetic::FLOAT_ARITHMETIC),
-     LintId::of(arithmetic::INTEGER_ARITHMETIC),
 +// 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.
 +
 +store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
-     LintId::of(try_err::TRY_ERR),
 +    LintId::of(as_conversions::AS_CONVERSIONS),
++    LintId::of(as_underscore::AS_UNDERSCORE),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
 +    LintId::of(create_dir::CREATE_DIR),
 +    LintId::of(dbg_macro::DBG_MACRO),
 +    LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +    LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
 +    LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +    LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +    LintId::of(empty_drop::EMPTY_DROP),
 +    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +    LintId::of(exit::EXIT),
 +    LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +    LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
 +    LintId::of(implicit_return::IMPLICIT_RETURN),
 +    LintId::of(indexing_slicing::INDEXING_SLICING),
 +    LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
 +    LintId::of(integer_division::INTEGER_DIVISION),
 +    LintId::of(large_include_file::LARGE_INCLUDE_FILE),
 +    LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +    LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
 +    LintId::of(map_err_ignore::MAP_ERR_IGNORE),
 +    LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
++    LintId::of(matches::TRY_ERR),
 +    LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +    LintId::of(mem_forget::MEM_FORGET),
 +    LintId::of(methods::CLONE_ON_REF_PTR),
 +    LintId::of(methods::EXPECT_USED),
 +    LintId::of(methods::FILETYPE_IS_FILE),
 +    LintId::of(methods::GET_UNWRAP),
 +    LintId::of(methods::UNWRAP_USED),
 +    LintId::of(misc::FLOAT_CMP_CONST),
 +    LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
 +    LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +    LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +    LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +    LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
 +    LintId::of(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +    LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
++    LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC),
++    LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC),
 +    LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
 +    LintId::of(panic_unimplemented::PANIC),
 +    LintId::of(panic_unimplemented::TODO),
 +    LintId::of(panic_unimplemented::UNIMPLEMENTED),
 +    LintId::of(panic_unimplemented::UNREACHABLE),
 +    LintId::of(pattern_type_mismatch::PATTERN_TYPE_MISMATCH),
 +    LintId::of(pub_use::PUB_USE),
 +    LintId::of(redundant_slicing::DEREF_BY_SLICING),
 +    LintId::of(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
 +    LintId::of(strings::STRING_ADD),
 +    LintId::of(strings::STRING_SLICE),
 +    LintId::of(strings::STRING_TO_STRING),
 +    LintId::of(strings::STR_TO_STRING),
 +    LintId::of(types::RC_BUFFER),
 +    LintId::of(types::RC_MUTEX),
 +    LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
 +    LintId::of(unicode::NON_ASCII_LITERAL),
 +    LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +    LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +    LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
 +    LintId::of(write::PRINT_STDERR),
 +    LintId::of(write::PRINT_STDOUT),
 +    LintId::of(write::USE_DEBUG),
 +])
index 62f26d821a0d6840cc1e02ab404899f50efa8551,0000000000000000000000000000000000000000..35575351784a287786eba32e8b2b7dfc3934227f
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,124 @@@
-     LintId::of(collapsible_match::COLLAPSIBLE_MATCH),
 +// 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.
 +
 +store.register_group(true, "clippy::style", Some("clippy_style"), vec![
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
 +    LintId::of(blacklisted_name::BLACKLISTED_NAME),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
-     LintId::of(manual_map::MANUAL_MAP),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eq_op::OP_REF),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::RESULT_UNIT_ERR),
++    LintId::of(get_first::GET_FIRST),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
++    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
++    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(ptr_eq::PTR_EQ),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +])
index 2de49f1624a4264e8074a1ea453f7af251cb1680,0000000000000000000000000000000000000000..7b13713c36e59abdc34f34765d7c18c91bfd45ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,33 -1,0 +1,36 @@@
-     LintId::of(significant_drop_in_scrutinee::SIGNIFICANT_DROP_IN_SCRUTINEE),
 +// 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.
 +
 +store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
++    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::MUT_RANGE_BOUND),
++    LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
++    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::SUSPICIOUS_MAP),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
++    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
 +])
index 4ac834f72405bbbef41d2d927730e3ef1b04d0c8,0000000000000000000000000000000000000000..ee0416fc0ff5e32400f85c80e09869f9bace1f98
mode 100644,000000..100644
--- /dev/null
@@@ -1,970 -1,0 +1,971 @@@
- mod arithmetic;
 +// error-pattern:cargo-clippy
 +
 +#![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(let_else)]
 +#![feature(lint_reasons)]
 +#![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_pretty;
 +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;
 +extern crate rustc_parse_format;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +extern crate clippy_utils;
 +
 +use clippy_utils::parse_msrv;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_lint::LintId;
 +use rustc_session::Session;
 +
 +/// 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
 +///     /// // Bad
 +///     /// Insert a short example of code that triggers the lint
 +///     ///
 +///     /// // Good
 +///     /// 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
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "internal")]
 +mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
 +mod renamed_lints;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod absurd_extreme_comparisons;
++mod almost_complete_letter_range;
 +mod approx_const;
- mod collapsible_match;
 +mod as_conversions;
++mod as_underscore;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assign_ops;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod bit_mask;
 +mod blacklisted_name;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod booleans;
 +mod borrow_as_ptr;
++mod borrow_deref_ref;
 +mod bytecount;
 +mod bytes_count_to_len;
 +mod cargo;
 +mod case_sensitive_file_extension_comparisons;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
- mod get_last_with_len;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_methods;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
++mod doc_link_with_quotes;
 +mod double_comparison;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duplicate_mod;
 +mod duration_subsec;
 +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 eq_op;
 +mod equatable_if_let;
 +mod erasing_op;
 +mod escape;
 +mod eta_reduction;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_equality_without_abs;
 +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_str_radix_10;
 +mod functions;
 +mod future_not_send;
- mod manual_map;
++mod get_first;
 +mod identity_op;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +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 int_plus_one;
 +mod integer_division;
 +mod invalid_upcast_comparisons;
 +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_unwrap_or;
 +mod manual_non_exhaustive;
 +mod manual_ok_or;
 +mod manual_strip;
- mod match_on_vec_items;
 +mod map_clone;
 +mod map_err_ignore;
 +mod map_unit_fn;
- mod match_str_case_mismatch;
 +mod match_result_ok;
- mod significant_drop_in_scrutinee;
 +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 mixed_read_write_in_expression;
 +mod module_style;
 +mod modulo_arithmetic;
 +mod mut_key;
 +mod mut_mut;
 +mod mut_mutex_lock;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bitwise_bool;
 +mod needless_bool;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_late_init;
 +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 numeric_arithmetic;
 +mod octal_escapes;
 +mod only_used_in_recursion;
 +mod open_options;
 +mod option_env_unwrap;
 +mod option_if_let_else;
 +mod overflow_check_conditional;
 +mod panic_in_result_fn;
 +mod panic_unimplemented;
 +mod partialeq_ne_impl;
 +mod pass_by_ref_or_value;
 +mod path_buf_push_overwrite;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_eq;
 +mod ptr_offset_with_cast;
 +mod pub_use;
 +mod question_mark;
 +mod ranges;
 +mod rc_clone_in_vec_init;
 +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 repeat_once;
 +mod return_self_not_must_use;
 +mod returns;
 +mod same_name_method;
 +mod self_assignment;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
- mod try_err;
 +mod single_char_lifetime_names;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
 +mod stable_sort_primitive;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +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 transmuting_null;
-     store.register_late_pass(|| Box::new(collapsible_match::CollapsibleMatch));
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_hash;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
 +mod unnecessary_sort_by;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
++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 vec_resize_to_zero;
 +mod verbose_file_reads;
 +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 use crate::utils::conf::Conf;
 +use crate::utils::conf::{format_error, TryConf};
 +
 +/// 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. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
 +    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors } = 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.struct_err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(error)
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// 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);
 +
 +    include!("lib.deprecated.rs");
 +
 +    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");
 +
 +    #[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::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
 +    }
 +
 +    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(needless_bitwise_bool::NeedlessBitwiseBool));
 +    store.register_late_pass(|| Box::new(eq_op::EqOp));
 +    store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|| Box::new(ptr::Ptr));
 +    store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
 +    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(identity_op::IdentityOp));
 +    store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
 +    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(get_last_with_len::GetLastWithLen));
 +    store.register_late_pass(|| Box::new(unicode::Unicode));
 +    store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
 +    store.register_late_pass(|| Box::new(unit_hash::UnitHash));
 +    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 = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    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)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
 +    store.register_late_pass(move || Box::new(map_clone::MapClone::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::new(map_err_ignore::MapErrIgnore));
 +    store.register_late_pass(|| Box::new(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::new(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(open_options::OpenOptions));
 +    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(|| Box::new(transmute::Transmute));
 +    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(arithmetic::Arithmetic::default()));
 +    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(absurd_extreme_comparisons::AbsurdExtremeComparisons));
 +    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(new_without_default::NewWithoutDefault::default()));
 +    let blacklisted_names = conf.blacklisted_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(blacklisted_name::BlacklistedName::new(blacklisted_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_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(try_err::TryErr));
++    store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default()));
 +    store.register_late_pass(|| Box::new(assign_ops::AssignOps));
 +    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(match_on_vec_items::MatchOnVecItems));
 +    store.register_late_pass(|| Box::new(bytecount::ByteCount));
 +    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::new(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(double_comparison::DoubleComparisons));
 +    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(duration_subsec::DurationSubsec));
 +    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(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
 +    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(transmuting_null::TransmutingNull));
 +    store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
 +    store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
 +    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_late_pass(|| Box::new(mut_key::MutableKeyType));
 +    store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
 +    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_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::new(default::Default::default()));
 +    store.register_late_pass(|| Box::new(unused_self::UnusedSelf));
 +    store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|| Box::new(exit::Exit));
 +    store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::new(single_component_path_imports::SingleComponentPathImports));
 +    let max_fn_params_bools = conf.max_fn_params_bools;
 +    let max_struct_bools = conf.max_struct_bools;
 +    store.register_early_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::new(verbose_file_reads::VerboseFileReads));
 +    store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
 +    store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
 +    store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
 +    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(mut_mutex_lock::MutMutexLock));
-     store.register_late_pass(|| Box::new(manual_unwrap_or::ManualUnwrapOr));
 +    store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
 +    store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
 +    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::new(macro_use::MacroUseImports::default()));
 +    store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
 +    store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
 +    store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
 +    store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
 +    store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
-     store.register_late_pass(|| Box::new(manual_map::ManualMap));
 +    store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
 +    store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
 +    store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
 +    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::new(vec_init_then_push::VecInitThenPush::default()));
 +    store.register_late_pass(|| {
 +        Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
 +    });
 +    store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
-     store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
 +    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(|| Box::new(significant_drop_in_scrutinee::SignificantDropInScrutinee));
-     store.register_late_pass(|| Box::new(dbg_macro::DbgMacro));
 +    store.register_late_pass(move || Box::new(format_args::FormatArgs));
 +    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(borrow_as_ptr::BorrowAsPtr::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
++    store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
 +    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
++    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 cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move || {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
 +    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
 +    store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
 +    store.register_early_pass(|| Box::new(pub_use::PubUse));
 +    store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
 +    store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
 +    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::new(duplicate_mod::DuplicateMod::default()));
++    store.register_late_pass(|| Box::new(get_first::GetFirst));
++    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(as_underscore::AsUnderscore));
 +    // 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 75d771f992a8c48261afb5deba2a3302237302b1,0000000000000000000000000000000000000000..d61be78895ffc50a41da823a7e10e7dfe5509197
mode 100644,000000..100644
--- /dev/null
@@@ -1,718 -1,0 +1,713 @@@
-     ///
-     /// // Bad
 +mod empty_loop;
 +mod explicit_counter_loop;
 +mod explicit_into_iter_loop;
 +mod explicit_iter_loop;
 +mod for_kv_map;
 +mod for_loops_over_fallibles;
 +mod iter_next_loop;
 +mod manual_flatten;
 +mod manual_memcpy;
 +mod missing_spin_loop;
 +mod mut_range_bound;
 +mod needless_collect;
 +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];
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```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]);
 +    /// }
 +    /// ```
 +    /// Could be written as:
 +    /// ```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() {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    /// can be rewritten to
 +    /// ```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
 +    /// Checks for `for` loops over `Option` or `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. This is more clearly expressed as an `if
 +    /// let`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
-     /// // Good
-     /// if let Some(x) = opt {
++    /// # let res: Result<i32, std::io::Error> = Ok(1);
 +    /// for x in opt {
 +    ///     // ..
 +    /// }
 +    ///
-     /// // or
-     ///
++    /// for x in &res {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
-     ///
-     /// // Bad
-     /// for x in &res {
++    /// Use instead:
 +    /// ```rust
++    /// # let opt = Some(1);
 +    /// # let res: Result<i32, std::io::Error> = Ok(1);
-     /// // Good
++    /// if let Some(x) = opt {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// if let Ok(x) = res {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub FOR_LOOPS_OVER_FALLIBLES,
 +    suspicious,
 +    "for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
 +}
 +
 +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"
 +}
 +
 +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"
 +}
 +
 +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;
 +    /// }
 +    /// ```
 +    /// Could be written as
 +    /// ```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() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[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);
 +    /// }
 +    /// ```
 +    /// could be written as
 +    /// ```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);
 +    /// }
 +    /// ```
 +    /// could be written as
 +    /// ```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.59.0"]
 +    pub MISSING_SPIN_LOOP,
 +    perf,
 +    "An empty busy waiting loop"
 +}
 +
 +declare_lint_pass!(Loops => [
 +    MANUAL_MEMCPY,
 +    MANUAL_FLATTEN,
 +    NEEDLESS_RANGE_LOOP,
 +    EXPLICIT_ITER_LOOP,
 +    EXPLICIT_INTO_ITER_LOOP,
 +    ITER_NEXT_LOOP,
 +    FOR_LOOPS_OVER_FALLIBLES,
 +    WHILE_LET_LOOP,
 +    NEEDLESS_COLLECT,
 +    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,
 +]);
 +
 +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);
 +        }
 +
 +        needless_collect::check(expr, cx);
 +    }
 +}
 +
 +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);
 +}
 +
 +fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
 +    let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
 +
 +    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" => {
 +                next_loop_linted = iter_next_loop::check(cx, arg);
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    if !next_loop_linted {
 +        for_loops_over_fallibles::check(cx, pat, arg);
 +    }
 +}
index 4f85364965b699c4e827894809fb4e226e56129d,0000000000000000000000000000000000000000..a348bb465c8844f32a8164759f594ff16616051f
mode 100644,000000..100644
--- /dev/null
@@@ -1,395 -1,0 +1,380 @@@
- use clippy_utils::{
-     contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq,
- };
 +use super::NEEDLESS_RANGE_LOOP;
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::has_iter_method;
 +use clippy_utils::visitors::is_local_used;
-     if let Some(higher::Range { start: Some(start), ref end, limits }) = higher::Range::hir(arg) {
++use clippy_utils::{contains_name, higher, is_integer_const, match_trait_method, paths, sugg, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
 +use rustc_lint::LateContext;
 +use rustc_middle::middle::region;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::symbol::{sym, Symbol};
 +use std::iter::{self, Iterator};
 +use std::mem;
 +
 +/// Checks for looping over a range and then indexing a sequence with it.
 +/// The iteratee must be a range literal.
 +#[expect(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    pat: &'tcx Pat<'_>,
 +    arg: &'tcx Expr<'_>,
 +    body: &'tcx Expr<'_>,
 +    expr: &'tcx Expr<'_>,
 +) {
-                     if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty)
-                     {
++    if let Some(higher::Range {
++        start: Some(start),
++        ref end,
++        limits,
++    }) = higher::Range::hir(arg)
++    {
 +        // the var must be a single name
 +        if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
 +            let mut visitor = VarVisitor {
 +                cx,
 +                var: canonical_id,
 +                indexed_mut: FxHashSet::default(),
 +                indexed_indirectly: FxHashMap::default(),
 +                indexed_directly: FxHashMap::default(),
 +                referenced: FxHashSet::default(),
 +                nonindex: false,
 +                prefer_mutable: false,
 +            };
 +            walk_expr(&mut visitor, body);
 +
 +            // linting condition: we only indexed one variable, and indexed it directly
 +            if visitor.indexed_indirectly.is_empty() && visitor.indexed_directly.len() == 1 {
 +                let (indexed, (indexed_extent, indexed_ty)) = visitor
 +                    .indexed_directly
 +                    .into_iter()
 +                    .next()
 +                    .expect("already checked that we have exactly 1 element");
 +
 +                // ensure that the indexed variable was declared before the loop, see #601
 +                if let Some(indexed_extent) = indexed_extent {
 +                    let parent_def_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +                    let region_scope_tree = cx.tcx.region_scope_tree(parent_def_id);
 +                    let pat_extent = region_scope_tree.var_scope(pat.hir_id.local_id).unwrap();
 +                    if region_scope_tree.is_subscope_of(indexed_extent, pat_extent) {
 +                        return;
 +                    }
 +                }
 +
 +                // don't lint if the container that is indexed does not have .iter() method
 +                let has_iter = has_iter_method(cx, indexed_ty);
 +                if has_iter.is_none() {
 +                    return;
 +                }
 +
 +                // don't lint if the container that is indexed into is also used without
 +                // indexing
 +                if visitor.referenced.contains(&indexed) {
 +                    return;
 +                }
 +
 +                let starts_at_zero = is_integer_const(cx, start, 0);
 +
 +                let skip = if starts_at_zero {
 +                    String::new()
 +                } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, start) {
 +                    return;
 +                } else {
 +                    format!(".skip({})", snippet(cx, start.span, ".."))
 +                };
 +
 +                let mut end_is_start_plus_val = false;
 +
 +                let take = if let Some(end) = *end {
 +                    let mut take_expr = end;
 +
 +                    if let ExprKind::Binary(ref op, left, right) = end.kind {
 +                        if op.node == BinOpKind::Add {
 +                            let start_equal_left = SpanlessEq::new(cx).eq_expr(start, left);
 +                            let start_equal_right = SpanlessEq::new(cx).eq_expr(start, right);
 +
 +                            if start_equal_left {
 +                                take_expr = right;
 +                            } else if start_equal_right {
 +                                take_expr = left;
 +                            }
 +
 +                            end_is_start_plus_val = start_equal_left | start_equal_right;
 +                        }
 +                    }
 +
-                     } else if visitor.indexed_mut.contains(&indexed)
-                         && contains_name(indexed, take_expr)
-                     {
++                    if is_len_call(end, indexed) || is_end_eq_array_len(cx, end, limits, indexed_ty) {
 +                        String::new()
-                             }
++                    } else if visitor.indexed_mut.contains(&indexed) && contains_name(indexed, take_expr) {
 +                        return;
 +                    } else {
 +                        match limits {
 +                            ast::RangeLimits::Closed => {
 +                                let take_expr = sugg::Sugg::hir(cx, take_expr, "<count>");
 +                                format!(".take({})", take_expr + sugg::ONE)
-                             }
++                            },
 +                            ast::RangeLimits::HalfOpen => {
 +                                format!(".take({})", snippet(cx, take_expr.span, ".."))
-                         &format!(
-                             "the loop variable `{}` is used to index `{}`",
-                             ident.name, indexed
-                         ),
++                            },
 +                        }
 +                    }
 +                } else {
 +                    String::new()
 +                };
 +
 +                let (ref_mut, method) = if visitor.indexed_mut.contains(&indexed) {
 +                    ("mut ", "iter_mut")
 +                } else {
 +                    ("", "iter")
 +                };
 +
 +                let take_is_empty = take.is_empty();
 +                let mut method_1 = take;
 +                let mut method_2 = skip;
 +
 +                if end_is_start_plus_val {
 +                    mem::swap(&mut method_1, &mut method_2);
 +                }
 +
 +                if visitor.nonindex {
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        arg.span,
-                                         format!(
-                                             "{}.{}().enumerate(){}{}",
-                                             indexed, method, method_1, method_2
-                                         ),
++                        &format!("the loop variable `{}` is used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![
 +                                    (pat.span, format!("({}, <item>)", ident.name)),
 +                                    (
 +                                        arg.span,
-                         &format!(
-                             "the loop variable `{}` is only used to index `{}`",
-                             ident.name, indexed
-                         ),
++                                        format!("{}.{}().enumerate(){}{}", indexed, method, method_1, method_2),
 +                                    ),
 +                                ],
 +                            );
 +                        },
 +                    );
 +                } else {
 +                    let repl = if starts_at_zero && take_is_empty {
 +                        format!("&{}{}", ref_mut, indexed)
 +                    } else {
 +                        format!("{}.{}(){}{}", indexed, method, method_1, method_2)
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        NEEDLESS_RANGE_LOOP,
 +                        arg.span,
-     fn check(
-         &mut self,
-         idx: &'tcx Expr<'_>,
-         seqexpr: &'tcx Expr<'_>,
-         expr: &'tcx Expr<'_>,
-     ) -> bool {
++                        &format!("the loop variable `{}` is only used to index `{}`", ident.name, indexed),
 +                        |diag| {
 +                            multispan_sugg(
 +                                diag,
 +                                "consider using an iterator",
 +                                vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, len_args, _) = expr.kind;
 +        if len_args.len() == 1;
 +        if method.ident.name == sym::len;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = len_args[0].kind;
 +        if path.segments.len() == 1;
 +        if path.segments[0].ident.name == var;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn is_end_eq_array_len<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    end: &Expr<'_>,
 +    limits: ast::RangeLimits,
 +    indexed_ty: Ty<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let ExprKind::Lit(ref lit) = end.kind;
 +        if let ast::LitKind::Int(end_int, _) = lit.node;
 +        if let ty::Array(_, arr_len_const) = indexed_ty.kind();
 +        if let Some(arr_len) = arr_len_const.try_eval_usize(cx.tcx, cx.param_env);
 +        then {
 +            return match limits {
 +                ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
 +                ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
 +            };
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct VarVisitor<'a, 'tcx> {
 +    /// context reference
 +    cx: &'a LateContext<'tcx>,
 +    /// var name to look for as index
 +    var: HirId,
 +    /// indexed variables that are used mutably
 +    indexed_mut: FxHashSet<Symbol>,
 +    /// indirectly indexed variables (`v[(i + 4) % N]`), the extend is `None` for global
 +    indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
 +    /// subset of `indexed` of vars that are indexed directly: `v[i]`
 +    /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
 +    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
 +    /// Any names that are used outside an index operation.
 +    /// Used to detect things like `&mut vec` used together with `vec[i]`
 +    referenced: FxHashSet<Symbol>,
 +    /// has the loop variable been used in expressions other than the index of
 +    /// an index op?
 +    nonindex: bool,
 +    /// Whether we are inside the `$` in `&mut $` or `$ = foo` or `$.bar`, where bar
 +    /// takes `&mut self`
 +    prefer_mutable: bool,
 +}
 +
 +impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
-             }
++    fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
 +        if_chain! {
 +            // the indexed container is referenced by a name
 +            if let ExprKind::Path(ref seqpath) = seqexpr.kind;
 +            if let QPath::Resolved(None, seqvar) = *seqpath;
 +            if seqvar.segments.len() == 1;
 +            if is_local_used(self.cx, idx, self.var);
 +            then {
 +                if self.prefer_mutable {
 +                    self.indexed_mut.insert(seqvar.segments[0].ident.name);
 +                }
 +                let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
 +                let res = self.cx.qpath_res(seqpath, seqexpr.hir_id);
 +                match res {
 +                    Res::Local(hir_id) => {
 +                        let parent_def_id = self.cx.tcx.hir().get_parent_item(expr.hir_id);
 +                        let extent = self.cx
 +                            .tcx
 +                            .region_scope_tree(parent_def_id)
 +                            .var_scope(hir_id.local_id)
 +                            .unwrap();
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (Some(extent), self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        } else {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, Some(extent));
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    Res::Def(DefKind::Static (_)| DefKind::Const, ..) => {
 +                        if index_used_directly {
 +                            self.indexed_directly.insert(
 +                                seqvar.segments[0].ident.name,
 +                                (None, self.cx.typeck_results().node_type(seqexpr.hir_id)),
 +                            );
 +                        } else {
 +                            self.indexed_indirectly.insert(seqvar.segments[0].ident.name, None);
 +                        }
 +                        return false;  // no need to walk further *on the variable*
 +                    }
 +                    _ => (),
 +                }
 +            }
 +        }
 +        true
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // a range index op
 +            if let ExprKind::MethodCall(meth, [args_0, args_1, ..], _) = &expr.kind;
 +            if (meth.ident.name == sym::index && match_trait_method(self.cx, expr, &paths::INDEX))
 +                || (meth.ident.name == sym::index_mut && match_trait_method(self.cx, expr, &paths::INDEX_MUT));
 +            if !self.check(args_1, args_0, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // an index op
 +            if let ExprKind::Index(seqexpr, idx) = expr.kind;
 +            if !self.check(idx, seqexpr, expr);
 +            then { return }
 +        }
 +
 +        if_chain! {
 +            // directly using a variable
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind;
 +            if let Res::Local(local_id) = path.res;
 +            then {
 +                if local_id == self.var {
 +                    self.nonindex = true;
 +                } else {
 +                    // not the correct variable, but still a variable
 +                    self.referenced.insert(path.segments[0].ident.name);
 +                }
 +            }
 +        }
 +
 +        let old = self.prefer_mutable;
 +        match expr.kind {
 +            ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => {
 +                self.prefer_mutable = true;
 +                self.visit_expr(lhs);
 +                self.prefer_mutable = false;
 +                self.visit_expr(rhs);
-             }
++            },
 +            ExprKind::AddrOf(BorrowKind::Ref, mutbl, expr) => {
 +                if mutbl == Mutability::Mut {
 +                    self.prefer_mutable = true;
 +                }
 +                self.visit_expr(expr);
-             }
++            },
 +            ExprKind::Call(f, args) => {
 +                self.visit_expr(f);
 +                for expr in args {
 +                    let ty = self.cx.typeck_results().expr_ty_adjusted(expr);
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
-                 for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args)
-                 {
++            },
 +            ExprKind::MethodCall(_, args, _) => {
 +                let def_id = self.cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
-             }
++                for (ty, expr) in iter::zip(self.cx.tcx.fn_sig(def_id).inputs().skip_binder(), args) {
 +                    self.prefer_mutable = false;
 +                    if let ty::Ref(_, _, mutbl) = *ty.kind() {
 +                        if mutbl == Mutability::Mut {
 +                            self.prefer_mutable = true;
 +                        }
 +                    }
 +                    self.visit_expr(expr);
 +                }
-             }
++            },
 +            ExprKind::Closure(_, _, body_id, ..) => {
 +                let body = self.cx.tcx.hir().body(body_id);
 +                self.visit_expr(&body.value);
++            },
 +            _ => walk_expr(self, expr),
 +        }
 +        self.prefer_mutable = old;
 +    }
 +}
index 70a118d6b353930ea8c751c0eef2fe0a96b47e0b,0000000000000000000000000000000000000000..c025f5972d5195ff67ea0f6e2c47c7ee65759a55
mode 100644,000000..100644
--- /dev/null
@@@ -1,212 -1,0 +1,212 @@@
-                 let arms = never_loop_expr_branch(&mut arms.iter().map(|a| &*a.body), main_loop_id);
 +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;
 +use rustc_hir::{Block, 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<'_>>,
 +) {
 +    match never_loop_block(block, 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 => (),
 +    }
 +}
 +
 +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,
 +    }
 +}
 +
 +fn never_loop_block(block: &Block<'_>, main_loop_id: HirId) -> NeverLoopResult {
 +    let mut iter = block.stmts.iter().filter_map(stmt_to_expr).chain(block.expr);
 +    never_loop_expr_seq(&mut iter, main_loop_id)
 +}
 +
 +fn never_loop_expr_seq<'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))
 +        .fold(NeverLoopResult::Otherwise, combine_seq)
 +}
 +
 +fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    match stmt.kind {
 +        StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
 +        StmtKind::Local(local) => local.init,
 +        StmtKind::Item(..) => None,
 +    }
 +}
 +
 +fn never_loop_expr(expr: &Expr<'_>, 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::Struct(_, _, Some(e))
 +        | ExprKind::Repeat(e, _)
 +        | 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::MethodCall(_, es, _) | ExprKind::Tup(es) => {
 +            never_loop_expr_all(&mut es.iter(), main_loop_id)
 +        },
 +        ExprKind::Call(e, es) => never_loop_expr_all(&mut once(e).chain(es.iter()), main_loop_id),
 +        ExprKind::Binary(_, e1, e2)
 +        | ExprKind::Assign(e1, e2, _)
 +        | ExprKind::AssignOp(_, e1, e2)
 +        | ExprKind::Index(e1, e2) => never_loop_expr_all(&mut [e1, e2].iter().copied(), main_loop_id),
 +        ExprKind::Loop(b, _, _, _) => {
 +            // Break can come from the inner loop so remove them.
 +            absorb_break(&never_loop_block(b, main_loop_id))
 +        },
 +        ExprKind::If(e, e2, e3) => {
 +            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));
 +            combine_seq(e1, combine_branches(e2, e3))
 +        },
 +        ExprKind::Match(e, arms, _) => {
 +            let e = never_loop_expr(e, main_loop_id);
 +            if arms.is_empty() {
 +                e
 +            } else {
++                let arms = never_loop_expr_branch(&mut arms.iter().map(|a| a.body), main_loop_id);
 +                combine_seq(e, arms)
 +            }
 +        },
 +        ExprKind::Block(b, _) => never_loop_block(b, main_loop_id),
 +        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
 +            }
 +        },
 +        ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
 +            combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
 +        }),
 +        ExprKind::InlineAsm(asm) => asm
 +            .operands
 +            .iter()
 +            .map(|(o, _)| match o {
 +                InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
 +                    never_loop_expr(expr, main_loop_id)
 +                },
 +                InlineAsmOperand::Out { expr, .. } => never_loop_expr_all(&mut expr.iter(), main_loop_id),
 +                InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => {
 +                    never_loop_expr_all(&mut once(in_expr).chain(out_expr.iter()), main_loop_id)
 +                },
 +                InlineAsmOperand::Const { .. }
 +                | InlineAsmOperand::SymFn { .. }
 +                | InlineAsmOperand::SymStatic { .. } => NeverLoopResult::Otherwise,
 +            })
 +            .fold(NeverLoopResult::Otherwise, combine_both),
 +        ExprKind::Struct(_, _, None)
 +        | ExprKind::Yield(_, _)
 +        | ExprKind::Closure(_, _, _, _, _)
 +        | ExprKind::Path(_)
 +        | ExprKind::ConstBlock(_)
 +        | ExprKind::Lit(_)
 +        | ExprKind::Err => NeverLoopResult::Otherwise,
 +    }
 +}
 +
 +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))
 +        .fold(NeverLoopResult::Otherwise, combine_both)
 +}
 +
 +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))
 +        .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}) = {iter}.next()",
 +        pat = pat_snippet,
 +        iter = iter_snippet
 +    )
 +}
index 80845ace3f94071404cc6b208a969552bc308e7a,0000000000000000000000000000000000000000..14f5faafd7cb9da8198f020ecfbf212bacccef0c
mode 100644,000000..100644
--- /dev/null
@@@ -1,220 -1,0 +1,220 @@@
-                 _ => Some(Err(())),
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs};
 +use rustc_ast::ast::{self, VisibilityKind};
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 +use rustc_hir::{self as hir, Expr, ExprKind, QPath};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::ty::DefIdTree;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::{DefId, LocalDefId};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of the non-exhaustive pattern.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the #[non_exhaustive] attribute expresses better the intent
 +    /// and allows possible optimizations when applied to enums.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    ///     _c: (),
 +    /// }
 +    ///
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    ///     #[doc(hidden)]
 +    ///     _C,
 +    /// }
 +    ///
 +    /// struct T(pub i32, pub i32, ());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[non_exhaustive]
 +    /// struct S {
 +    ///     pub a: i32,
 +    ///     pub b: i32,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// enum E {
 +    ///     A,
 +    ///     B,
 +    /// }
 +    ///
 +    /// #[non_exhaustive]
 +    /// struct T(pub i32, pub i32);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_NON_EXHAUSTIVE,
 +    style,
 +    "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]"
 +}
 +
 +#[expect(clippy::module_name_repetitions)]
 +pub struct ManualNonExhaustiveStruct {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualNonExhaustiveStruct {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]);
 +
 +#[expect(clippy::module_name_repetitions)]
 +pub struct ManualNonExhaustiveEnum {
 +    msrv: Option<RustcVersion>,
 +    constructed_enum_variants: FxHashSet<(DefId, DefId)>,
 +    potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>,
 +}
 +
 +impl ManualNonExhaustiveEnum {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            constructed_enum_variants: FxHashSet::default(),
 +            potential_enums: Vec::new(),
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 +
 +impl EarlyLintPass for ManualNonExhaustiveStruct {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
 +        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
 +            return;
 +        }
 +
 +        if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
 +            let (fields, delimiter) = match variant_data {
 +                ast::VariantData::Struct(fields, _) => (&**fields, '{'),
 +                ast::VariantData::Tuple(fields, _) => (&**fields, '('),
 +                ast::VariantData::Unit(_) => return,
 +            };
 +            if fields.len() <= 1 {
 +                return;
 +            }
 +            let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
 +                VisibilityKind::Public => None,
 +                VisibilityKind::Inherited => Some(Ok(f)),
++                VisibilityKind::Restricted { .. } => Some(Err(())),
 +            });
 +            if let Some(Ok(field)) = iter.next()
 +                && iter.next().is_none()
 +                && field.ty.kind.is_unit()
 +                && field.ident.map_or(true, |name| name.as_str().starts_with('_'))
 +            {
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_NON_EXHAUSTIVE,
 +                    item.span,
 +                    "this seems like a manual implementation of the non-exhaustive pattern",
 +                    |diag| {
 +                        if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
 +                            && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
 +                            && let Some(snippet) = snippet_opt(cx, header_span)
 +                        {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {}", snippet),
 +                                Applicability::Unspecified,
 +                            );
 +                        }
 +                        diag.span_help(field.span, "remove this field");
 +                    }
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Enum(def, _) = &item.kind
 +            && def.variants.len() > 1
 +        {
 +            let mut iter = def.variants.iter().filter_map(|v| {
 +                let id = cx.tcx.hir().local_def_id(v.id);
 +                (matches!(v.data, hir::VariantData::Unit(_))
 +                    && v.ident.as_str().starts_with('_')
 +                    && is_doc_hidden(cx.tcx.hir().attrs(v.id)))
 +                .then(|| (id, v.span))
 +            });
 +            if let Some((id, span)) = iter.next()
 +                && iter.next().is_none()
 +            {
 +                self.potential_enums.push((item.def_id, id, item.span, span));
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind
 +            && let [.., name] = p.segments
 +            && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
 +            && name.ident.as_str().starts_with('_')
 +        {
 +            let variant_id = cx.tcx.parent(id);
 +            let enum_id = cx.tcx.parent(variant_id);
 +
 +            self.constructed_enum_variants.insert((enum_id, variant_id));
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        for &(enum_id, _, enum_span, variant_span) in
 +            self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| {
 +                !self
 +                    .constructed_enum_variants
 +                    .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
 +                    && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
 +            })
 +        {
 +            span_lint_and_then(
 +                cx,
 +                MANUAL_NON_EXHAUSTIVE,
 +                enum_span,
 +                "this seems like a manual implementation of the non-exhaustive pattern",
 +                |diag| {
 +                    if !cx.tcx.adt_def(enum_id).is_variant_list_non_exhaustive()
 +                        && let header_span = cx.sess().source_map().span_until_char(enum_span, '{')
 +                        && let Some(snippet) = snippet_opt(cx, header_span)
 +                    {
 +                            diag.span_suggestion(
 +                                header_span,
 +                                "add the attribute",
 +                                format!("#[non_exhaustive] {}", snippet),
 +                                Applicability::Unspecified,
 +                            );
 +                    }
 +                    diag.span_help(variant_span, "remove this variant");
 +                },
 +            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..07021f1bcad8db24b6f625a618a2c03d24477392
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,143 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::higher::IfLetOrMatch;
++use clippy_utils::visitors::is_local_used;
++use clippy_utils::{is_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq};
++use if_chain::if_chain;
++use rustc_errors::MultiSpan;
++use rustc_hir::LangItem::OptionNone;
++use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
++use rustc_lint::LateContext;
++use rustc_span::Span;
++
++use super::COLLAPSIBLE_MATCH;
++
++pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
++    if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
++        for arm in arms {
++            check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
++        }
++    }
++}
++
++pub(super) fn check_if_let<'tcx>(
++    cx: &LateContext<'tcx>,
++    pat: &'tcx Pat<'_>,
++    body: &'tcx Expr<'_>,
++    else_expr: Option<&'tcx Expr<'_>>,
++) {
++    check_arm(cx, false, pat, body, None, else_expr);
++}
++
++fn check_arm<'tcx>(
++    cx: &LateContext<'tcx>,
++    outer_is_match: bool,
++    outer_pat: &'tcx Pat<'tcx>,
++    outer_then_body: &'tcx Expr<'tcx>,
++    outer_guard: Option<&'tcx Guard<'tcx>>,
++    outer_else_body: Option<&'tcx Expr<'tcx>>,
++) {
++    let inner_expr = peel_blocks_with_stmt(outer_then_body);
++    if_chain! {
++        if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr);
++        if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
++            IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)),
++            IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! {
++                // if there are more than two arms, collapsing would be non-trivial
++                if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none());
++                // one of the arms must be "wild-like"
++                if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a));
++                then {
++                    let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]);
++                    Some((scrutinee, then.pat, Some(els.body)))
++                } else {
++                    None
++                }
++            },
++        };
++        if outer_pat.span.ctxt() == inner_scrutinee.span.ctxt();
++        // match expression must be a local binding
++        // match <local> { .. }
++        if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee));
++        if !pat_contains_or(inner_then_pat);
++        // the binding must come from the pattern of the containing match arm
++        // ..<local>.. => match <local> { .. }
++        if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
++        // the "else" branches must be equal
++        if match (outer_else_body, inner_else_body) {
++            (None, None) => true,
++            (None, Some(e)) | (Some(e), None) => is_unit_expr(e),
++            (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
++        };
++        // the binding must not be used in the if guard
++        if outer_guard.map_or(
++            true,
++            |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)
++        );
++        // ...or anywhere in the inner expression
++        if match inner {
++            IfLetOrMatch::IfLet(_, _, body, els) => {
++                !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
++            },
++            IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
++        };
++        then {
++            let msg = format!(
++                "this `{}` can be collapsed into the outer `{}`",
++                if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" },
++                if outer_is_match { "match" } else { "if let" },
++            );
++            span_lint_and_then(
++                cx,
++                COLLAPSIBLE_MATCH,
++                inner_expr.span,
++                &msg,
++                |diag| {
++                    let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
++                    help_span.push_span_label(binding_span, "replace this binding");
++                    help_span.push_span_label(inner_then_pat.span, "with this pattern");
++                    diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
++                },
++            );
++        }
++    }
++}
++
++/// A "wild-like" arm has a wild (`_`) or `None` pattern and no guard. Such arms can be "collapsed"
++/// into a single wild arm without any significant loss in semantics or readability.
++fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
++    if arm.guard.is_some() {
++        return false;
++    }
++    match arm.pat.kind {
++        PatKind::Binding(..) | PatKind::Wild => true,
++        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
++        _ => false,
++    }
++}
++
++fn find_pat_binding(pat: &Pat<'_>, hir_id: HirId) -> Option<Span> {
++    let mut span = None;
++    pat.walk_short(|p| match &p.kind {
++        // ignore OR patterns
++        PatKind::Or(_) => false,
++        PatKind::Binding(_bm, _, _ident, _) => {
++            let found = p.hir_id == hir_id;
++            if found {
++                span = Some(p.span);
++            }
++            !found
++        },
++        _ => true,
++    });
++    span
++}
++
++fn pat_contains_or(pat: &Pat<'_>) -> bool {
++    let mut result = false;
++    pat.walk(|p| {
++        let is_or = matches!(p.kind, PatKind::Or(_));
++        result |= is_or;
++        !is_or
++    });
++    result
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..542905a2d763cffa4879b4f75d7acefa3e4aa7a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,306 @@@
++use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
++use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
++use clippy_utils::{
++    can_move_expr_to_closure, is_else_clause, is_lang_ctor, is_lint_allowed, path_to_local_id, peel_blocks,
++    peel_hir_expr_refs, peel_hir_expr_while, CaptureKind,
++};
++use rustc_ast::util::parser::PREC_POSTFIX;
++use rustc_errors::Applicability;
++use rustc_hir::LangItem::{OptionNone, OptionSome};
++use rustc_hir::{
++    def::Res, Arm, BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path,
++    QPath, UnsafeSource,
++};
++use rustc_lint::LateContext;
++use rustc_span::{sym, SyntaxContext};
++
++use super::MANUAL_MAP;
++
++pub(super) fn check_match<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    scrutinee: &'tcx Expr<'_>,
++    arms: &'tcx [Arm<'_>],
++) {
++    if let [arm1, arm2] = arms
++        && arm1.guard.is_none()
++        && arm2.guard.is_none()
++    {
++        check(cx, expr, scrutinee, arm1.pat, arm1.body, Some(arm2.pat), arm2.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);
++}
++
++#[expect(clippy::too_many_lines)]
++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<'_>,
++) {
++    let (scrutinee_ty, ty_ref_count, ty_mutability) =
++        peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
++    if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option)
++        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option))
++    {
++        return;
++    }
++
++    let expr_ctxt = expr.span.ctxt();
++    let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
++        try_parse_pattern(cx, then_pat, expr_ctxt),
++        else_pat.map_or(Some(OptionPat::Wild), |p| try_parse_pattern(cx, p, expr_ctxt)),
++    ) {
++        (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
++            (else_body, pattern, ref_count, true)
++        },
++        (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_body) => {
++            (else_body, pattern, ref_count, false)
++        },
++        (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_body) => {
++            (then_body, pattern, ref_count, true)
++        },
++        (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_body) => {
++            (then_body, pattern, ref_count, false)
++        },
++        _ => return,
++    };
++
++    // Top level or patterns aren't allowed in closures.
++    if matches!(some_pat.kind, PatKind::Or(_)) {
++        return;
++    }
++
++    let some_expr = match get_some_expr(cx, some_expr, false, expr_ctxt) {
++        Some(expr) => expr,
++        None => return,
++    };
++
++    // These two lints will go back and forth with each other.
++    if cx.typeck_results().expr_ty(some_expr.expr) == cx.tcx.types.unit
++        && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
++    {
++        return;
++    }
++
++    // `map` won't perform any adjustments.
++    if !cx.typeck_results().expr_adjustments(some_expr.expr).is_empty() {
++        return;
++    }
++
++    // Determine which binding mode to use.
++    let explicit_ref = some_pat.contains_explicit_ref_binding();
++    let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
++
++    let as_ref_str = match binding_ref {
++        Some(Mutability::Mut) => ".as_mut()",
++        Some(Mutability::Not) => ".as_ref()",
++        None => "",
++    };
++
++    match can_move_expr_to_closure(cx, some_expr.expr) {
++        Some(captures) => {
++            // 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 let Some(binding_ref_mutability) = binding_ref {
++                let e = peel_hir_expr_while(scrutinee, |e| match e.kind {
++                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
++                    _ => None,
++                });
++                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(l), .. })) = e.kind {
++                    match captures.get(l) {
++                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return,
++                        Some(CaptureKind::Ref(Mutability::Not)) if binding_ref_mutability == Mutability::Mut => {
++                            return;
++                        },
++                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
++                    }
++                }
++            }
++        },
++        None => return,
++    };
++
++    let mut app = Applicability::MachineApplicable;
++
++    // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
++    // it's being passed by value.
++    let scrutinee = peel_hir_expr_refs(scrutinee).0;
++    let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
++    let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
++        format!("({})", scrutinee_str)
++    } else {
++        scrutinee_str.into()
++    };
++
++    let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
++        if_chain! {
++            if !some_expr.needs_unsafe_block;
++            if let Some(func) = can_pass_as_func(cx, id, some_expr.expr);
++            if func.span.ctxt() == some_expr.expr.span.ctxt();
++            then {
++                snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
++            } else {
++                if path_to_local_id(some_expr.expr, id)
++                    && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
++                    && binding_ref.is_some()
++                {
++                    return;
++                }
++
++                // `ref` and `ref mut` annotations were handled earlier.
++                let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
++                    "mut "
++                } else {
++                    ""
++                };
++                let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
++                if some_expr.needs_unsafe_block {
++                    format!("|{}{}| unsafe {{ {} }}", annotation, some_binding, expr_snip)
++                } else {
++                    format!("|{}{}| {}", annotation, some_binding, expr_snip)
++                }
++            }
++        }
++    } else if !is_wild_none && explicit_ref.is_none() {
++        // TODO: handle explicit reference annotations.
++        let pat_snip = snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0;
++        let expr_snip = snippet_with_context(cx, some_expr.expr.span, expr_ctxt, "..", &mut app).0;
++        if some_expr.needs_unsafe_block {
++            format!("|{}| unsafe {{ {} }}", pat_snip, expr_snip)
++        } else {
++            format!("|{}| {}", pat_snip, expr_snip)
++        }
++    } else {
++        // Refutable bindings and mixed reference annotations can't be handled by `map`.
++        return;
++    };
++
++    span_lint_and_sugg(
++        cx,
++        MANUAL_MAP,
++        expr.span,
++        "manual implementation of `Option::map`",
++        "try this",
++        if else_pat.is_none() && is_else_clause(cx.tcx, expr) {
++            format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
++        } else {
++            format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
++        },
++        app,
++    );
++}
++
++// Checks whether the expression could be passed as a function, or whether a closure is needed.
++// Returns the function to be passed to `map` if it exists.
++fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
++    match expr.kind {
++        ExprKind::Call(func, [arg])
++            if path_to_local_id(arg, binding)
++                && cx.typeck_results().expr_adjustments(arg).is_empty()
++                && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
++        {
++            Some(func)
++        },
++        _ => None,
++    }
++}
++
++enum OptionPat<'a> {
++    Wild,
++    None,
++    Some {
++        // The pattern contained in the `Some` tuple.
++        pattern: &'a Pat<'a>,
++        // The number of references before the `Some` tuple.
++        // e.g. `&&Some(_)` has a ref count of 2.
++        ref_count: usize,
++    },
++}
++
++struct SomeExpr<'tcx> {
++    expr: &'tcx Expr<'tcx>,
++    needs_unsafe_block: bool,
++}
++
++// Try to parse into a recognized `Option` pattern.
++// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
++fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
++    fn f<'tcx>(
++        cx: &LateContext<'tcx>,
++        pat: &'tcx Pat<'_>,
++        ref_count: usize,
++        ctxt: SyntaxContext,
++    ) -> Option<OptionPat<'tcx>> {
++        match pat.kind {
++            PatKind::Wild => Some(OptionPat::Wild),
++            PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
++            PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
++            PatKind::TupleStruct(ref qpath, [pattern], _)
++                if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
++            {
++                Some(OptionPat::Some { pattern, ref_count })
++            },
++            _ => None,
++        }
++    }
++    f(cx, pat, 0, ctxt)
++}
++
++// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
++fn get_some_expr<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    needs_unsafe_block: bool,
++    ctxt: SyntaxContext,
++) -> Option<SomeExpr<'tcx>> {
++    // TODO: Allow more complex expressions.
++    match expr.kind {
++        ExprKind::Call(
++            Expr {
++                kind: ExprKind::Path(ref qpath),
++                ..
++            },
++            [arg],
++        ) if ctxt == expr.span.ctxt() && is_lang_ctor(cx, qpath, OptionSome) => Some(SomeExpr {
++            expr: arg,
++            needs_unsafe_block,
++        }),
++        ExprKind::Block(
++            Block {
++                stmts: [],
++                expr: Some(expr),
++                rules,
++                ..
++            },
++            _,
++        ) => get_some_expr(
++            cx,
++            expr,
++            needs_unsafe_block || *rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
++            ctxt,
++        ),
++        _ => None,
++    }
++}
++
++// Checks for the `None` value.
++fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1111c80f2fe287712dd64b73b656060b9038e3f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++use clippy_utils::consts::constant_simple;
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::usage::contains_return_break_continue_macro;
++use clippy_utils::{is_lang_ctor, path_to_local_id, sugg};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
++use rustc_hir::{Arm, Expr, PatKind};
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::MANUAL_UNWRAP_OR;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
++    let ty = cx.typeck_results().expr_ty(scrutinee);
++    if_chain! {
++        if let Some(ty_name) = if is_type_diagnostic_item(cx, ty, sym::Option) {
++            Some("Option")
++        } else if is_type_diagnostic_item(cx, ty, sym::Result) {
++            Some("Result")
++        } else {
++            None
++        };
++        if let Some(or_arm) = applicable_or_arm(cx, arms);
++        if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
++        if let Some(indent) = indent_of(cx, expr.span);
++        if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
++        then {
++            let reindented_or_body =
++                reindent_multiline(or_body_snippet.into(), true, Some(indent));
++
++            let suggestion = if scrutinee.span.from_expansion() {
++                    // we don't want parentheses around macro, e.g. `(some_macro!()).unwrap_or(0)`
++                    sugg::Sugg::hir_with_macro_callsite(cx, scrutinee, "..")
++                }
++                else {
++                    sugg::Sugg::hir(cx, scrutinee, "..").maybe_par()
++                };
++
++            span_lint_and_sugg(
++                cx,
++                MANUAL_UNWRAP_OR, expr.span,
++                &format!("this pattern reimplements `{}::unwrap_or`", ty_name),
++                "replace with",
++                format!(
++                    "{}.unwrap_or({})",
++                    suggestion,
++                    reindented_or_body,
++                ),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
++
++fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
++    if_chain! {
++        if arms.len() == 2;
++        if arms.iter().all(|arm| arm.guard.is_none());
++        if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| {
++            match arm.pat.kind {
++                PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
++                PatKind::TupleStruct(ref qpath, [pat], _) =>
++                    matches!(pat.kind, PatKind::Wild) && is_lang_ctor(cx, qpath, ResultErr),
++                _ => false,
++            }
++        });
++        let unwrap_arm = &arms[1 - idx];
++        if let PatKind::TupleStruct(ref qpath, [unwrap_pat], _) = unwrap_arm.pat.kind;
++        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk);
++        if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
++        if path_to_local_id(unwrap_arm.body, binding_hir_id);
++        if cx.typeck_results().expr_adjustments(unwrap_arm.body).is_empty();
++        if !contains_return_break_continue_macro(or_arm.body);
++        then {
++            Some(or_arm)
++        } else {
++            None
++        }
++    }
++}
index 90c50b994d2bfe11245881843feef728b1829ad5,0000000000000000000000000000000000000000..1c216e135704c3ca4bdfba15937b5c5a64805cc0
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,75 @@@
-                                 LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
-                                 LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::is_unit_expr;
 +use clippy_utils::source::{expr_block, snippet};
 +use clippy_utils::sugg::Sugg;
 +use rustc_ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, Expr, ExprKind, PatKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +
 +use super::MATCH_BOOL;
 +
 +pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    // Type of expression is `bool`.
 +    if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
 +        span_lint_and_then(
 +            cx,
 +            MATCH_BOOL,
 +            expr.span,
 +            "you seem to be trying to match on a boolean expression",
 +            move |diag| {
 +                if arms.len() == 2 {
 +                    // no guards
 +                    let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
 +                        if let ExprKind::Lit(ref lit) = arm_bool.kind {
 +                            match lit.node {
++                                LitKind::Bool(true) => Some((arms[0].body, arms[1].body)),
++                                LitKind::Bool(false) => Some((arms[1].body, arms[0].body)),
 +                                _ => None,
 +                            }
 +                        } else {
 +                            None
 +                        }
 +                    } else {
 +                        None
 +                    };
 +
 +                    if let Some((true_expr, false_expr)) = exprs {
 +                        let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
 +                            (false, false) => Some(format!(
 +                                "if {} {} else {}",
 +                                snippet(cx, ex.span, "b"),
 +                                expr_block(cx, true_expr, None, "..", Some(expr.span)),
 +                                expr_block(cx, false_expr, None, "..", Some(expr.span))
 +                            )),
 +                            (false, true) => Some(format!(
 +                                "if {} {}",
 +                                snippet(cx, ex.span, "b"),
 +                                expr_block(cx, true_expr, None, "..", Some(expr.span))
 +                            )),
 +                            (true, false) => {
 +                                let test = Sugg::hir(cx, ex, "..");
 +                                Some(format!(
 +                                    "if {} {}",
 +                                    !test,
 +                                    expr_block(cx, false_expr, None, "..", Some(expr.span))
 +                                ))
 +                            },
 +                            (true, true) => None,
 +                        };
 +
 +                        if let Some(sugg) = sugg {
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using an `if`/`else` expression",
 +                                sugg,
 +                                Applicability::HasPlaceholders,
 +                            );
 +                        }
 +                    }
 +                }
 +            },
 +        );
 +    }
 +}
index 2e1f7646eb400b3924ff813738c90091205e139f,0000000000000000000000000000000000000000..a68eec842abc51a08418e793c9b670e93e5b45a6
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,171 @@@
- use clippy_utils::{higher, is_wild};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::is_wild;
 +use clippy_utils::source::snippet_with_applicability;
- pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-     if let Some(higher::IfLet {
-         let_pat,
 +use rustc_ast::{Attribute, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty;
 +use rustc_span::source_map::Spanned;
 +
 +use super::MATCH_LIKE_MATCHES_MACRO;
 +
 +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
-         if_then,
-         if_else: Some(if_else),
-     }) = higher::IfLet::hir(cx, expr)
-     {
-         find_matches_sugg(
-             cx,
-             let_expr,
-             IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
-             expr,
-             true,
-         );
-     }
++pub(crate) 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<'_>,
++) {
++    find_matches_sugg(
++        cx,
 +        let_expr,
++        IntoIterator::into_iter([
++            (&[][..], Some(let_pat), then_expr, None),
++            (&[][..], None, else_expr, None),
++        ]),
++        expr,
++        true,
++    );
 +}
 +
 +pub(super) fn check_match<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'tcx>],
 +) -> bool {
 +    find_matches_sugg(
 +        cx,
 +        scrutinee,
 +        arms.iter().map(|arm| {
 +            (
 +                cx.tcx.hir().attrs(arm.hir_id),
 +                Some(arm.pat),
 +                arm.body,
 +                arm.guard.as_ref(),
 +            )
 +        }),
 +        e,
 +        false,
 +    )
 +}
 +
 +/// Lint a `match` or `if let` for replacement by `matches!`
 +fn find_matches_sugg<'a, 'b, I>(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    mut iter: I,
 +    expr: &Expr<'_>,
 +    is_if_let: bool,
 +) -> bool
 +where
 +    'b: 'a,
 +    I: Clone
 +        + DoubleEndedIterator
 +        + ExactSizeIterator
 +        + Iterator<
 +            Item = (
 +                &'a [Attribute],
 +                Option<&'a Pat<'b>>,
 +                &'a Expr<'b>,
 +                Option<&'a Guard<'b>>,
 +            ),
 +        >,
 +{
 +    if_chain! {
 +        if iter.len() >= 2;
 +        if cx.typeck_results().expr_ty(expr).is_bool();
 +        if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
 +        let iter_without_last = iter.clone();
 +        if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
 +        if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
 +        if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
 +        if b0 != b1;
 +        if first_guard.is_none() || iter.len() == 0;
 +        if first_attrs.is_empty();
 +        if iter
 +            .all(|arm| {
 +                find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
 +            });
 +        then {
 +            if let Some(last_pat) = last_pat_opt {
 +                if !is_wild(last_pat) {
 +                    return false;
 +                }
 +            }
 +
 +            // The suggestion may be incorrect, because some arms can have `cfg` attributes
 +            // evaluated into `false` and so such arms will be stripped before.
 +            let mut applicability = Applicability::MaybeIncorrect;
 +            let pat = {
 +                use itertools::Itertools as _;
 +                iter_without_last
 +                    .filter_map(|arm| {
 +                        let pat_span = arm.1?.span;
 +                        Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
 +                    })
 +                    .join(" | ")
 +            };
 +            let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
 +                format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
 +            } else {
 +                pat
 +            };
 +
 +            // strip potential borrows (#6503), but only if the type is a reference
 +            let mut ex_new = ex;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, .., ex_inner) = ex.kind {
 +                if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() {
 +                    ex_new = ex_inner;
 +                }
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_LIKE_MATCHES_MACRO,
 +                expr.span,
 +                &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
 +                "try this",
 +                format!(
 +                    "{}matches!({}, {})",
 +                    if b0 { "" } else { "!" },
 +                    snippet_with_applicability(cx, ex_new.span, "..", &mut applicability),
 +                    pat_and_guard,
 +                ),
 +                applicability,
 +            );
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Extract a `bool` or `{ bool }`
 +fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
 +    match ex {
 +        ExprKind::Lit(Spanned {
 +            node: LitKind::Bool(b), ..
 +        }) => Some(*b),
 +        ExprKind::Block(
 +            rustc_hir::Block {
 +                stmts: &[],
 +                expr: Some(exp),
 +                ..
 +            },
 +            _,
 +        ) if is_if_let => {
 +            if let ExprKind::Lit(Spanned {
 +                node: LitKind::Bool(b), ..
 +            }) = exp.kind
 +            {
 +                Some(b)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2917f85c45f53250d889ec05242dfb5234ba15fa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, LangItem};
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::MATCH_ON_VEC_ITEMS;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) {
++    if_chain! {
++        if let Some(idx_expr) = is_vec_indexing(cx, scrutinee);
++        if let ExprKind::Index(vec, idx) = idx_expr.kind;
++
++        then {
++            // FIXME: could be improved to suggest surrounding every pattern with Some(_),
++            // but only when `or_patterns` are stabilized.
++            span_lint_and_sugg(
++                cx,
++                MATCH_ON_VEC_ITEMS,
++                scrutinee.span,
++                "indexing into a vector may panic",
++                "try this",
++                format!(
++                    "{}.get({})",
++                    snippet(cx, vec.span, ".."),
++                    snippet(cx, idx.span, "..")
++                ),
++                Applicability::MaybeIncorrect
++            );
++        }
++    }
++}
++
++fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
++    if_chain! {
++        if let ExprKind::Index(array, index) = expr.kind;
++        if is_vector(cx, array);
++        if !is_full_range(cx, index);
++
++        then {
++            return Some(expr);
++        }
++    }
++
++    None
++}
++
++fn is_vector(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    let ty = cx.typeck_results().expr_ty(expr);
++    let ty = ty.peel_refs();
++    is_type_diagnostic_item(cx, ty, sym::Vec)
++}
++
++fn is_full_range(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    let ty = cx.typeck_results().expr_ty(expr);
++    let ty = ty.peel_refs();
++    is_type_lang_item(cx, ty, LangItem::RangeFull)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8302ce426e5707e257db4116b21740abb3e95a74
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,125 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::is_type_diagnostic_item;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, Visitor};
++use rustc_hir::{Arm, Expr, ExprKind, PatKind};
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++use rustc_span::symbol::Symbol;
++use rustc_span::{sym, Span};
++
++use super::MATCH_STR_CASE_MISMATCH;
++
++#[derive(Debug)]
++enum CaseMethod {
++    LowerCase,
++    AsciiLowerCase,
++    UpperCase,
++    AsciiUppercase,
++}
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
++    if_chain! {
++        if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind();
++        if let ty::Str = ty.kind();
++        then {
++            let mut visitor = MatchExprVisitor {
++                cx,
++                case_method: None,
++            };
++
++            visitor.visit_expr(scrutinee);
++
++            if let Some(case_method) = visitor.case_method {
++                if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
++                    lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
++                }
++            }
++        }
++    }
++}
++
++struct MatchExprVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'tcx>,
++    case_method: Option<CaseMethod>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
++    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
++        match ex.kind {
++            ExprKind::MethodCall(segment, [receiver], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
++            _ => walk_expr(self, ex),
++        }
++    }
++}
++
++impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
++    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
++        if let Some(case_method) = get_case_method(segment_ident) {
++            let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
++
++            if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str {
++                self.case_method = Some(case_method);
++                return true;
++            }
++        }
++
++        false
++    }
++}
++
++fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
++    match segment_ident_str {
++        "to_lowercase" => Some(CaseMethod::LowerCase),
++        "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
++        "to_uppercase" => Some(CaseMethod::UpperCase),
++        "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
++        _ => None,
++    }
++}
++
++fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, Symbol)> {
++    let case_check = match case_method {
++        CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(|c| c.to_lowercase().next() == Some(c)) },
++        CaseMethod::AsciiLowerCase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_uppercase()) },
++        CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(|c| c.to_uppercase().next() == Some(c)) },
++        CaseMethod::AsciiUppercase => |input: &str| -> bool { !input.chars().any(|c| c.is_ascii_lowercase()) },
++    };
++
++    for arm in arms {
++        if_chain! {
++            if let PatKind::Lit(Expr {
++                                kind: ExprKind::Lit(lit),
++                                ..
++                            }) = arm.pat.kind;
++            if let LitKind::Str(symbol, _) = lit.node;
++            let input = symbol.as_str();
++            if !case_check(input);
++            then {
++                return Some((lit.span, symbol));
++            }
++        }
++    }
++
++    None
++}
++
++fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) {
++    let (method_str, suggestion) = match case_method {
++        CaseMethod::LowerCase => ("to_lowercase", bad_case_str.to_lowercase()),
++        CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()),
++        CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()),
++        CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()),
++    };
++
++    span_lint_and_sugg(
++        cx,
++        MATCH_STR_CASE_MISMATCH,
++        bad_case_span,
++        "this `match` arm has a differing case than its expression",
++        &*format!("consider changing the case of this arm to respect `{}`", method_str),
++        format!("\"{}\"", suggestion),
++        Applicability::MachineApplicable,
++    );
++}
index 3d8391bce2b28ff49a7c0e87fa20bec7f9700684,0000000000000000000000000000000000000000..d1e42f39e470dde07bdf5daee81a034846d5dd97
mode 100644,000000..100644
--- /dev/null
@@@ -1,796 -1,0 +1,1121 @@@
- use clippy_utils::{meets_msrv, msrvs};
 +use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
- use rustc_lint::{LateContext, LateLintPass};
++use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
 +use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
 +use rustc_lexer::{tokenize, TokenKind};
-         if expr.span.from_expansion() {
++use rustc_lint::{LateContext, LateLintPass, LintContext};
++use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, SpanData, SyntaxContext};
 +
++mod collapsible_match;
 +mod infallible_destructuring_match;
++mod manual_map;
++mod manual_unwrap_or;
 +mod match_as_ref;
 +mod match_bool;
 +mod match_like_matches;
++mod match_on_vec_items;
 +mod match_ref_pats;
 +mod match_same_arms;
 +mod match_single_binding;
++mod match_str_case_mismatch;
 +mod match_wild_enum;
 +mod match_wild_err_arm;
 +mod needless_match;
 +mod overlapping_arms;
 +mod redundant_pattern_match;
 +mod rest_pat_in_fully_bound_struct;
++mod significant_drop_in_scrutinee;
 +mod single_match;
++mod try_err;
 +mod wild_in_or_pats;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with a single arm where an `if let`
 +    /// will usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn bar(stool: &str) {}
 +    /// # let x = Some("abc");
 +    /// // Bad
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => (),
 +    /// }
 +    ///
 +    /// // Good
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_MATCH,
 +    style,
 +    "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches with two arms where an `if let else` will
 +    /// usually suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `if let` nests less than a `match`.
 +    ///
 +    /// ### Known problems
 +    /// Personal style preferences may differ.
 +    ///
 +    /// ### Example
 +    /// Using `match`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// match x {
 +    ///     Some(ref foo) => bar(foo),
 +    ///     _ => bar(&other_ref),
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `if let` with `else`:
 +    ///
 +    /// ```rust
 +    /// # fn bar(foo: &usize) {}
 +    /// # let other_ref: usize = 1;
 +    /// # let x: Option<&usize> = Some(&1);
 +    /// if let Some(ref foo) = x {
 +    ///     bar(foo);
 +    /// } else {
 +    ///     bar(&other_ref);
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_MATCH_ELSE,
 +    pedantic,
 +    "a `match` statement with two arms where the second arm's pattern is a placeholder instead of a specific match pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where all arms match a reference,
 +    /// suggesting to remove the reference and deref the matched expression
 +    /// instead. It also checks for `if let &foo = bar` blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// It just makes the code less readable. That reference
 +    /// destructuring adds nothing to the code.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// match x {
 +    ///     &A(ref y) => foo(y),
 +    ///     &B => bar(),
 +    ///     _ => frob(&x),
 +    /// }
 +    ///
 +    /// // Good
 +    /// match *x {
 +    ///     A(ref y) => foo(y),
 +    ///     B => bar(),
 +    ///     _ => frob(x),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_REF_PATS,
 +    style,
 +    "a `match` or `if let` with all arms prefixed with `&` instead of deref-ing the match expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches where match expression is a `bool`. It
 +    /// suggests to replace the expression with an `if...else` block.
 +    ///
 +    /// ### Why is this bad?
 +    /// It makes the code less readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// match condition {
 +    ///     true => foo(),
 +    ///     false => bar(),
 +    /// }
 +    /// ```
 +    /// Use if/else instead:
 +    /// ```rust
 +    /// # fn foo() {}
 +    /// # fn bar() {}
 +    /// let condition: bool = true;
 +    /// if condition {
 +    ///     foo();
 +    /// } else {
 +    ///     bar();
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_BOOL,
 +    pedantic,
 +    "a `match` on a boolean expression instead of an `if..else` block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for overlapping match arms.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is likely to be an error and if not, makes the code
 +    /// less obvious.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 5;
 +    /// match x {
 +    ///     1..=10 => println!("1 ... 10"),
 +    ///     5..=15 => println!("5 ... 15"),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_OVERLAPPING_ARM,
 +    style,
 +    "a `match` with overlapping arms"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arm which matches all errors with `Err(_)`
 +    /// and take drastic actions like `panic!`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is generally a bad practice, similar to
 +    /// catching all exceptions in java with `catch(Exception)`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Result<i32, &str> = Ok(3);
 +    /// match x {
 +    ///     Ok(_) => println!("ok"),
 +    ///     Err(_) => panic!("err"),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_WILD_ERR_ARM,
 +    pedantic,
 +    "a `match` with `Err(_)` arm and take drastic actions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for match which is used to add a reference to an
 +    /// `Option` value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `as_ref()` or `as_mut()` instead is shorter.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: Option<()> = None;
 +    ///
 +    /// // Bad
 +    /// let r: Option<&()> = match x {
 +    ///     None => None,
 +    ///     Some(ref v) => Some(v),
 +    /// };
 +    ///
 +    /// // Good
 +    /// let r: Option<&()> = x.as_ref();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_AS_REF,
 +    complexity,
 +    "a `match` on an Option value instead of using `as_ref()` or `as_mut`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches using `_`.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may be incorrect if guards exhaustively cover some
 +    /// variants, and also may not use correct path to enum if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A(usize), B(usize) }
 +    /// # let x = Foo::B(1);
 +    /// // Bad
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match x {
 +    ///     Foo::A(_) => {},
 +    ///     Foo::B(_) => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub WILDCARD_ENUM_MATCH_ARM,
 +    restriction,
 +    "a wildcard enum match arm using `_`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard enum matches for a single variant.
 +    ///
 +    /// ### Why is this bad?
 +    /// New enum variants added by library updates can be missed.
 +    ///
 +    /// ### Known problems
 +    /// Suggested replacements may not use correct path to enum
 +    /// if it's not present in the current scope.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # enum Foo { A, B, C }
 +    /// # let x = Foo::B;
 +    /// // Bad
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match x {
 +    ///     Foo::A => {},
 +    ///     Foo::B => {},
 +    ///     Foo::C => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    pedantic,
 +    "a wildcard enum match for a single variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for wildcard pattern used with others patterns in same match arm.
 +    ///
 +    /// ### Why is this bad?
 +    /// Wildcard pattern already covers any other pattern as it will match anyway.
 +    /// It makes the code less readable, especially to spot wildcard pattern use in match arm.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// match "foo" {
 +    ///     "a" => {},
 +    ///     "bar" | _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match "foo" {
 +    ///     "a" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub WILDCARD_IN_OR_PATTERNS,
 +    complexity,
 +    "a wildcard pattern used with others patterns in same match arm"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for matches being used to destructure a single-variant enum
 +    /// or tuple struct where a `let` will suffice.
 +    ///
 +    /// ### Why is this bad?
 +    /// Just readability – `let` doesn't nest, whereas a `match` does.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    ///
 +    /// let data = match wrapper {
 +    ///     Wrapper::Data(i) => i,
 +    /// };
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    /// ```rust
 +    /// enum Wrapper {
 +    ///     Data(i32),
 +    /// }
 +    ///
 +    /// let wrapper = Wrapper::Data(42);
 +    /// let Wrapper::Data(data) = wrapper;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INFALLIBLE_DESTRUCTURING_MATCH,
 +    style,
 +    "a `match` statement with a single infallible arm instead of a `let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for useless match that binds to only one value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    ///  Suggested replacements may be incorrect when `match`
 +    /// is actually binding temporary value, bringing a 'dropped while borrowed' error.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = 1;
 +    /// # let b = 2;
 +    ///
 +    /// // Bad
 +    /// match (a, b) {
 +    ///     (c, d) => {
 +    ///         // useless match
 +    ///     }
 +    /// }
 +    ///
 +    /// // Good
 +    /// let (c, d) = (a, b);
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub MATCH_SINGLE_BINDING,
 +    complexity,
 +    "a match with a single binding instead of using `let` statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
 +    ///
 +    /// ### Why is this bad?
 +    /// Correctness and readability. It's like having a wildcard pattern after
 +    /// matching all enum variants explicitly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A { a: i32 }
 +    /// let a = A { a: 5 };
 +    ///
 +    /// // Bad
 +    /// match a {
 +    ///     A { a: 5, .. } => {},
 +    ///     _ => {},
 +    /// }
 +    ///
 +    /// // Good
 +    /// match a {
 +    ///     A { a: 5 } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    restriction,
 +    "a match on a struct that binds all fields but still uses the wildcard pattern"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Lint for redundant pattern matching over `Result`, `Option`,
 +    /// `std::task::Poll` or `std::net::IpAddr`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more concise and clear to just use the proper
 +    /// utility function
 +    ///
 +    /// ### Known problems
 +    /// This will change the drop order for the matched type. Both `if let` and
 +    /// `while let` will drop the value at the end of the block, both `if` and `while` will drop the
 +    /// value before entering the block. For most types this change will not matter, but for a few
 +    /// types this will not be an acceptable change (e.g. locks). See the
 +    /// [reference](https://doc.rust-lang.org/reference/destructors.html#drop-scopes) for more about
 +    /// drop order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if let Ok(_) = Ok::<i32, i32>(42) {}
 +    /// if let Err(_) = Err::<i32, i32>(42) {}
 +    /// if let None = None::<()> {}
 +    /// if let Some(_) = Some(42) {}
 +    /// if let Poll::Pending = Poll::Pending::<()> {}
 +    /// if let Poll::Ready(_) = Poll::Ready(42) {}
 +    /// if let IpAddr::V4(_) = IpAddr::V4(Ipv4Addr::LOCALHOST) {}
 +    /// if let IpAddr::V6(_) = IpAddr::V6(Ipv6Addr::LOCALHOST) {}
 +    /// match Ok::<i32, i32>(42) {
 +    ///     Ok(_) => true,
 +    ///     Err(_) => false,
 +    /// };
 +    /// ```
 +    ///
 +    /// The more idiomatic use would be:
 +    ///
 +    /// ```rust
 +    /// # use std::task::Poll;
 +    /// # use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
 +    /// if Ok::<i32, i32>(42).is_ok() {}
 +    /// if Err::<i32, i32>(42).is_err() {}
 +    /// if None::<()>.is_none() {}
 +    /// if Some(42).is_some() {}
 +    /// if Poll::Pending::<()>.is_pending() {}
 +    /// if Poll::Ready(42).is_ready() {}
 +    /// if IpAddr::V4(Ipv4Addr::LOCALHOST).is_ipv4() {}
 +    /// if IpAddr::V6(Ipv6Addr::LOCALHOST).is_ipv6() {}
 +    /// Ok::<i32, i32>(42).is_ok();
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub REDUNDANT_PATTERN_MATCHING,
 +    style,
 +    "use the proper utility function avoiding an `if let`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match`  or `if let` expressions producing a
 +    /// `bool` that could be written using `matches!`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability and needless complexity.
 +    ///
 +    /// ### Known problems
 +    /// This lint falsely triggers, if there are arms with
 +    /// `cfg` attributes that remove an arm evaluating to `false`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(5);
 +    ///
 +    /// // Bad
 +    /// let a = match x {
 +    ///     Some(0) => true,
 +    ///     _ => false,
 +    /// };
 +    ///
 +    /// let a = if let Some(0) = x {
 +    ///     true
 +    /// } else {
 +    ///     false
 +    /// };
 +    ///
 +    /// // Good
 +    /// let a = matches!(x, Some(0));
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MATCH_LIKE_MATCHES_MACRO,
 +    style,
 +    "a match that could be written with the matches! macro"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `match` with identical arm bodies.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error. If arm bodies
 +    /// are the same on purpose, you can factor them
 +    /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
 +    ///
 +    /// ### Known problems
 +    /// False positive possible with order dependent `match`
 +    /// (see issue
 +    /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => bar(), // <= oops
 +    /// }
 +    /// ```
 +    ///
 +    /// This should probably be
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar => bar(),
 +    ///     Quz => quz(),
 +    ///     Baz => baz(), // <= fixed
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo:
 +    /// ```rust,ignore
 +    /// match foo {
 +    ///     Bar | Baz => bar(), // <= shows the intent better
 +    ///     Quz => quz(),
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MATCH_SAME_ARMS,
 +    pedantic,
 +    "`match` with identical arm bodies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary `match` or match-like `if let` returns for `Option` and `Result`
 +    /// when function signatures are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// This `match` block does nothing and might not be what the coder intended.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     match result {
 +    ///         Ok(val) => Ok(val),
 +    ///         Err(err) => Err(err),
 +    ///     }
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     if let Some(val) = option {
 +    ///         Some(val)
 +    ///     } else {
 +    ///         None
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be replaced as
 +    ///
 +    /// ```rust,ignore
 +    /// fn foo() -> Result<(), i32> {
 +    ///     result
 +    /// }
 +    ///
 +    /// fn bar() -> Option<i32> {
 +    ///     option
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub NEEDLESS_MATCH,
 +    complexity,
 +    "`match` or match-like `if let` that are unnecessary"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Finds nested `match` or `if let` expressions where the patterns may be "collapsed" together
++    /// without adding any branches.
++    ///
++    /// Note that this lint is not intended to find _all_ cases where nested match patterns can be merged, but only
++    /// cases where merging would most likely make the code more readable.
++    ///
++    /// ### Why is this bad?
++    /// It is unnecessarily verbose and complex.
++    ///
++    /// ### Example
++    /// ```rust
++    /// fn func(opt: Option<Result<u64, String>>) {
++    ///     let n = match opt {
++    ///         Some(n) => match n {
++    ///             Ok(n) => n,
++    ///             _ => return,
++    ///         }
++    ///         None => return,
++    ///     };
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// fn func(opt: Option<Result<u64, String>>) {
++    ///     let n = match opt {
++    ///         Some(Ok(n)) => n,
++    ///         _ => return,
++    ///     };
++    /// }
++    /// ```
++    #[clippy::version = "1.50.0"]
++    pub COLLAPSIBLE_MATCH,
++    style,
++    "Nested `match` or `if let` expressions where the patterns may be \"collapsed\" together."
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
++    ///
++    /// ### Why is this bad?
++    /// Concise code helps focusing on behavior instead of boilerplate.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let foo: Option<i32> = None;
++    /// match foo {
++    ///     Some(v) => v,
++    ///     None => 1,
++    /// };
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// let foo: Option<i32> = None;
++    /// foo.unwrap_or(1);
++    /// ```
++    #[clippy::version = "1.49.0"]
++    pub MANUAL_UNWRAP_OR,
++    complexity,
++    "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `match vec[idx]` or `match vec[n..m]`.
++    ///
++    /// ### Why is this bad?
++    /// This can panic at runtime.
++    ///
++    /// ### Example
++    /// ```rust, no_run
++    /// let arr = vec![0, 1, 2, 3];
++    /// let idx = 1;
++    ///
++    /// // Bad
++    /// match arr[idx] {
++    ///     0 => println!("{}", 0),
++    ///     1 => println!("{}", 3),
++    ///     _ => {},
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust, no_run
++    /// let arr = vec![0, 1, 2, 3];
++    /// let idx = 1;
++    ///
++    /// // Good
++    /// match arr.get(idx) {
++    ///     Some(0) => println!("{}", 0),
++    ///     Some(1) => println!("{}", 3),
++    ///     _ => {},
++    /// }
++    /// ```
++    #[clippy::version = "1.45.0"]
++    pub MATCH_ON_VEC_ITEMS,
++    pedantic,
++    "matching on vector elements can panic"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
++    ///
++    /// ### Why is this bad?
++    /// The arm is unreachable, which is likely a mistake
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let text = "Foo";
++    /// match &*text.to_ascii_lowercase() {
++    ///     "foo" => {},
++    ///     "Bar" => {},
++    ///     _ => {},
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # let text = "Foo";
++    /// match &*text.to_ascii_lowercase() {
++    ///     "foo" => {},
++    ///     "bar" => {},
++    ///     _ => {},
++    /// }
++    /// ```
++    #[clippy::version = "1.58.0"]
++    pub MATCH_STR_CASE_MISMATCH,
++    correctness,
++    "creation of a case altering match expression with non-compliant arms"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Check for temporaries returned from function calls in a match scrutinee that have the
++    /// `clippy::has_significant_drop` attribute.
++    ///
++    /// ### Why is this bad?
++    /// The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have
++    /// an important side-effect, such as unlocking a mutex, making it important for users to be
++    /// able to accurately understand their lifetimes. When a temporary is returned in a function
++    /// call in a match scrutinee, its lifetime lasts until the end of the match block, which may
++    /// be surprising.
++    ///
++    /// For `Mutex`es this can lead to a deadlock. This happens when the match scrutinee uses a
++    /// function call that returns a `MutexGuard` and then tries to lock again in one of the match
++    /// arms. In that case the `MutexGuard` in the scrutinee will not be dropped until the end of
++    /// the match block and thus will not unlock.
++    ///
++    /// ### Example
++    /// ```rust.ignore
++    /// # use std::sync::Mutex;
++    ///
++    /// # struct State {}
++    ///
++    /// # impl State {
++    /// #     fn foo(&self) -> bool {
++    /// #         true
++    /// #     }
++    ///
++    /// #     fn bar(&self) {}
++    /// # }
++    ///
++    ///
++    /// let mutex = Mutex::new(State {});
++    ///
++    /// match mutex.lock().unwrap().foo() {
++    ///     true => {
++    ///         mutex.lock().unwrap().bar(); // Deadlock!
++    ///     }
++    ///     false => {}
++    /// };
++    ///
++    /// println!("All done!");
++    ///
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # use std::sync::Mutex;
++    ///
++    /// # struct State {}
++    ///
++    /// # impl State {
++    /// #     fn foo(&self) -> bool {
++    /// #         true
++    /// #     }
++    ///
++    /// #     fn bar(&self) {}
++    /// # }
++    ///
++    /// let mutex = Mutex::new(State {});
++    ///
++    /// let is_foo = mutex.lock().unwrap().foo();
++    /// match is_foo {
++    ///     true => {
++    ///         mutex.lock().unwrap().bar();
++    ///     }
++    ///     false => {}
++    /// };
++    ///
++    /// println!("All done!");
++    /// ```
++    #[clippy::version = "1.60.0"]
++    pub SIGNIFICANT_DROP_IN_SCRUTINEE,
++    suspicious,
++    "warns when a temporary of a type with a drop with a significant side-effect might have a surprising lifetime"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for usages of `Err(x)?`.
++    ///
++    /// ### Why is this bad?
++    /// The `?` operator is designed to allow calls that
++    /// can fail to be easily chained. For example, `foo()?.bar()` or
++    /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
++    /// always return), it is more clear to write `return Err(x)`.
++    ///
++    /// ### Example
++    /// ```rust
++    /// fn foo(fail: bool) -> Result<i32, String> {
++    ///     if fail {
++    ///       Err("failed")?;
++    ///     }
++    ///     Ok(0)
++    /// }
++    /// ```
++    /// Could be written:
++    ///
++    /// ```rust
++    /// fn foo(fail: bool) -> Result<i32, String> {
++    ///     if fail {
++    ///       return Err("failed".into());
++    ///     }
++    ///     Ok(0)
++    /// }
++    /// ```
++    #[clippy::version = "1.38.0"]
++    pub TRY_ERR,
++    restriction,
++    "return errors explicitly rather than hiding them behind a `?`"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for usages of `match` which could be implemented using `map`
++    ///
++    /// ### Why is this bad?
++    /// Using the `map` method is clearer and more concise.
++    ///
++    /// ### Example
++    /// ```rust
++    /// match Some(0) {
++    ///     Some(x) => Some(x + 1),
++    ///     None => None,
++    /// };
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// Some(0).map(|x| x + 1);
++    /// ```
++    #[clippy::version = "1.52.0"]
++    pub MANUAL_MAP,
++    style,
++    "reimplementation of `map`"
++}
++
 +#[derive(Default)]
 +pub struct Matches {
 +    msrv: Option<RustcVersion>,
 +    infallible_destructuring_match_linted: bool,
 +}
 +
 +impl Matches {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            msrv,
 +            ..Matches::default()
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Matches => [
 +    SINGLE_MATCH,
 +    MATCH_REF_PATS,
 +    MATCH_BOOL,
 +    SINGLE_MATCH_ELSE,
 +    MATCH_OVERLAPPING_ARM,
 +    MATCH_WILD_ERR_ARM,
 +    MATCH_AS_REF,
 +    WILDCARD_ENUM_MATCH_ARM,
 +    MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    WILDCARD_IN_OR_PATTERNS,
 +    MATCH_SINGLE_BINDING,
 +    INFALLIBLE_DESTRUCTURING_MATCH,
 +    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    REDUNDANT_PATTERN_MATCHING,
 +    MATCH_LIKE_MATCHES_MACRO,
 +    MATCH_SAME_ARMS,
 +    NEEDLESS_MATCH,
++    COLLAPSIBLE_MATCH,
++    MANUAL_UNWRAP_OR,
++    MATCH_ON_VEC_ITEMS,
++    MATCH_STR_CASE_MISMATCH,
++    SIGNIFICANT_DROP_IN_SCRUTINEE,
++    TRY_ERR,
++    MANUAL_MAP,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Matches {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-             if !span_starts_with(cx, expr.span, "match") {
++        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
++        let from_expansion = expr.span.from_expansion();
 +
 +        if let ExprKind::Match(ex, arms, source) = expr.kind {
-             if !contains_cfg_arm(cx, expr, ex, arms) {
++            if source == MatchSource::Normal && !span_starts_with(cx, expr.span, "match") {
 +                return;
 +            }
-             // These don't depend on a relationship between multiple arms
-             match_wild_err_arm::check(cx, ex, arms);
-             wild_in_or_pats::check(cx, arms);
-         } else {
-             if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
-                 match_like_matches::check(cx, expr);
++            if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
++                significant_drop_in_scrutinee::check(cx, expr, ex, source);
++            }
++
++            collapsible_match::check_match(cx, arms);
++            if !from_expansion {
++                // These don't depend on a relationship between multiple arms
++                match_wild_err_arm::check(cx, ex, arms);
++                wild_in_or_pats::check(cx, arms);
++            }
++
++            if source == MatchSource::TryDesugar {
++                try_err::check(cx, expr, ex);
++            }
++
++            if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) {
 +                if source == MatchSource::Normal {
 +                    if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO)
 +                        && match_like_matches::check_match(cx, expr, ex, arms))
 +                    {
 +                        match_same_arms::check(cx, arms);
 +                    }
 +
 +                    redundant_pattern_match::check_match(cx, expr, ex, arms);
 +                    single_match::check(cx, ex, arms, expr);
 +                    match_bool::check(cx, ex, arms, expr);
 +                    overlapping_arms::check(cx, ex, arms);
 +                    match_wild_enum::check(cx, ex, arms);
 +                    match_as_ref::check(cx, ex, arms, expr);
 +                    needless_match::check_match(cx, ex, arms, expr);
++                    match_on_vec_items::check(cx, ex);
++                    match_str_case_mismatch::check(cx, ex, arms);
++
++                    if !in_constant(cx, expr.hir_id) {
++                        manual_unwrap_or::check(cx, expr, ex, arms);
++                        manual_map::check_match(cx, expr, ex, arms);
++                    }
 +
 +                    if self.infallible_destructuring_match_linted {
 +                        self.infallible_destructuring_match_linted = false;
 +                    } else {
 +                        match_single_binding::check(cx, ex, arms, expr);
 +                    }
 +                }
 +                match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
 +            }
-             needless_match::check(cx, expr);
++        } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
++            collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else);
++            if !from_expansion {
++                if let Some(else_expr) = if_let.if_else {
++                    if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) {
++                        match_like_matches::check_if_let(
++                            cx,
++                            expr,
++                            if_let.let_pat,
++                            if_let.let_expr,
++                            if_let.if_then,
++                            else_expr,
++                        );
++                    }
++                    if !in_constant(cx, expr.hir_id) {
++                        manual_map::check_if_let(cx, expr, if_let.let_pat, if_let.let_expr, if_let.if_then, else_expr);
++                    }
++                }
++                redundant_pattern_match::check_if_let(
++                    cx,
++                    expr,
++                    if_let.let_pat,
++                    if_let.let_expr,
++                    if_let.if_else.is_some(),
++                );
++                needless_match::check_if_let(cx, expr, &if_let);
 +            }
++        } else if !from_expansion {
 +            redundant_pattern_match::check(cx, expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 +        self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local);
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        rest_pat_in_fully_bound_struct::check(cx, pat);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +/// Checks if there are any arms with a `#[cfg(..)]` attribute.
 +fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
 +    let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else {
 +        // Shouldn't happen, but treat this as though a `cfg` attribute were found
 +        return true;
 +    };
 +
 +    let start = scrutinee_span.hi();
 +    let mut arm_spans = arms.iter().map(|arm| {
 +        let data = arm.span.data();
 +        (data.ctxt == SyntaxContext::root()).then(|| (data.lo, data.hi))
 +    });
 +    let end = e.span.hi();
 +
 +    // Walk through all the non-code space before each match arm. The space trailing the final arm is
 +    // handled after the `try_fold` e.g.
 +    //
 +    // match foo {
 +    // _________^-                      everything between the scrutinee and arm1
 +    //|    arm1 => (),
 +    //|---^___________^                 everything before arm2
 +    //|    #[cfg(feature = "enabled")]
 +    //|    arm2 => some_code(),
 +    //|---^____________________^        everything before arm3
 +    //|    // some comment about arm3
 +    //|    arm3 => some_code(),
 +    //|---^____________________^        everything after arm3
 +    //|    #[cfg(feature = "disabled")]
 +    //|    arm4 = some_code(),
 +    //|};
 +    //|^
 +    let found = arm_spans.try_fold(start, |start, range| {
 +        let Some((end, next_start)) = range else {
 +            // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute were
 +            // found.
 +            return Err(());
 +        };
 +        let span = SpanData {
 +            lo: start,
 +            hi: end,
 +            ctxt: SyntaxContext::root(),
 +            parent: None,
 +        }
 +        .span();
 +        (!span_contains_cfg(cx, span)).then(|| next_start).ok_or(())
 +    });
 +    match found {
 +        Ok(start) => {
 +            let span = SpanData {
 +                lo: start,
 +                hi: end,
 +                ctxt: SyntaxContext::root(),
 +                parent: None,
 +            }
 +            .span();
 +            span_contains_cfg(cx, span)
 +        },
 +        Err(()) => true,
 +    }
 +}
 +
 +/// Checks if the given span contains a `#[cfg(..)]` attribute
 +fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
 +    let Some(snip) = snippet_opt(cx, s) else {
 +        // Assume true. This would require either an invalid span, or one which crosses file boundaries.
 +        return true;
 +    };
 +    let mut pos = 0usize;
 +    let mut iter = tokenize(&snip).map(|t| {
 +        let start = pos;
 +        pos += t.len;
 +        (t.kind, start..pos)
 +    });
 +
 +    // Search for the token sequence [`#`, `[`, `cfg`]
 +    while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
 +        let mut iter = iter.by_ref().skip_while(|(t, _)| {
 +            matches!(
 +                t,
 +                TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
 +            )
 +        });
 +        if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
 +            && matches!(iter.next(), Some((TokenKind::Ident, range)) if &snip[range.clone()] == "cfg")
 +        {
 +            return true;
 +        }
 +    }
 +    false
 +}
index f920ad4651f9d4b8c8d304cfb55bd1e99eb1d73c,0000000000000000000000000000000000000000..fa19cddd35ec7afff4258f68eb901967e01b411b
mode 100644,000000..100644
--- /dev/null
@@@ -1,209 -1,0 +1,207 @@@
- pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
-     if let Some(ref if_let) = higher::IfLet::hir(cx, ex) {
-         if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) {
-             let mut applicability = Applicability::MachineApplicable;
-             span_lint_and_sugg(
-                 cx,
-                 NEEDLESS_MATCH,
-                 ex.span,
-                 "this if-let expression is unnecessary",
-                 "replace it with",
-                 snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
-                 applicability,
-             );
-         }
 +use super::NEEDLESS_MATCH;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
 +use clippy_utils::{
 +    eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
 +    peel_blocks_with_stmt,
 +};
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::OptionNone;
 +use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            NEEDLESS_MATCH,
 +            expr.span,
 +            "this match expression is unnecessary",
 +            "replace it with",
 +            snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
 +            applicability,
 +        );
 +    }
 +}
 +
 +/// Check for nop `if let` expression that assembled as unnecessary match
 +///
 +/// ```rust,ignore
 +/// if let Some(a) = option {
 +///     Some(a)
 +/// } else {
 +///     None
 +/// }
 +/// ```
 +/// OR
 +/// ```rust,ignore
 +/// if let SomeEnum::A = some_enum {
 +///     SomeEnum::A
 +/// } else if let SomeEnum::B = some_enum {
 +///     SomeEnum::B
 +/// } else {
 +///     some_enum
 +/// }
 +/// ```
- fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
++pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) {
++    if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) {
++        let mut applicability = Applicability::MachineApplicable;
++        span_lint_and_sugg(
++            cx,
++            NEEDLESS_MATCH,
++            ex.span,
++            "this if-let expression is unnecessary",
++            "replace it with",
++            snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
++            applicability,
++        );
 +    }
 +}
 +
 +fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
 +    for arm in arms {
 +        let arm_expr = peel_blocks_with_stmt(arm.body);
 +        if let PatKind::Wild = arm.pat.kind {
 +            return eq_expr_value(cx, match_expr, strip_return(arm_expr));
 +        } else if !pat_same_as_expr(arm.pat, arm_expr) {
 +            return false;
 +        }
 +    }
 +
 +    true
 +}
 +
-             return check_if_let(cx, nested_if_let);
++fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
 +    if let Some(if_else) = if_let.if_else {
 +        if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
 +            return false;
 +        }
 +
 +        // Recursively check for each `else if let` phrase,
 +        if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
++            return check_if_let_inner(cx, nested_if_let);
 +        }
 +
 +        if matches!(if_else.kind, ExprKind::Block(..)) {
 +            let else_expr = peel_blocks_with_stmt(if_else);
 +            if matches!(else_expr.kind, ExprKind::Block(..)) {
 +                return false;
 +            }
 +            let ret = strip_return(else_expr);
 +            let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
 +            if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
 +                if let ExprKind::Path(ref qpath) = ret.kind {
 +                    return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
 +                }
 +                return false;
 +            }
 +            return eq_expr_value(cx, if_let.let_expr, ret);
 +        }
 +    }
 +
 +    false
 +}
 +
 +/// Strip `return` keyword if the expression type is `ExprKind::Ret`.
 +fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    if let ExprKind::Ret(Some(ret)) = expr.kind {
 +        ret
 +    } else {
 +        expr
 +    }
 +}
 +
 +/// Manually check for coercion casting by checking if the type of the match operand or let expr
 +/// differs with the assigned local variable or the function return type.
 +fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
 +    if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
 +        match p_node {
 +            // Compare match_expr ty with local in `let local = match match_expr {..}`
 +            Node::Local(local) => {
 +                let results = cx.typeck_results();
 +                return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
 +            },
 +            // compare match_expr ty with RetTy in `fn foo() -> RetTy`
 +            Node::Item(..) => {
 +                if let Some(fn_decl) = p_node.fn_decl() {
 +                    if let FnRetTy::Return(ret_ty) = fn_decl.output {
 +                        return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr));
 +                    }
 +                }
 +            },
 +            // check the parent expr for this whole block `{ match match_expr {..} }`
 +            Node::Block(block) => {
 +                if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) {
 +                    return expr_ty_matches_p_ty(cx, expr, block_parent_expr);
 +                }
 +            },
 +            // recursively call on `if xxx {..}` etc.
 +            Node::Expr(p_expr) => {
 +                return expr_ty_matches_p_ty(cx, expr, p_expr);
 +            },
 +            _ => {},
 +        }
 +    }
 +    false
 +}
 +
 +fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
 +    let expr = strip_return(expr);
 +    match (&pat.kind, &expr.kind) {
 +        // Example: `Some(val) => Some(val)`
 +        (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => {
 +            if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
 +                return over(path.segments, call_path.segments, |pat_seg, call_seg| {
 +                    pat_seg.ident.name == call_seg.ident.name
 +                }) && same_non_ref_symbols(tuple_params, call_params);
 +            }
 +        },
 +        // Example: `val => val`
 +        (
 +            PatKind::Binding(annot, _, pat_ident, _),
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: [first_seg, ..],
 +                    ..
 +                },
 +            )),
 +        ) => {
 +            return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut)
 +                && pat_ident.name == first_seg.ident.name;
 +        },
 +        // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
 +        (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
 +            return over(p_path.segments, e_path.segments, |p_seg, e_seg| {
 +                p_seg.ident.name == e_seg.ident.name
 +            });
 +        },
 +        // Example: `5 => 5`
 +        (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
 +            if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind {
 +                return pat_spanned.node == expr_spanned.node;
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    false
 +}
 +
 +fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool {
 +    if pats.len() != exprs.len() {
 +        return false;
 +    }
 +
 +    for i in 0..pats.len() {
 +        if !pat_same_as_expr(&pats[i], &exprs[i]) {
 +            return false;
 +        }
 +    }
 +
 +    true
 +}
index 1a8b9d15f370f1e3df5843b5b6bf22b6d36ae1fc,0000000000000000000000000000000000000000..095cd43ea13fb2f9bda22d36d3a7d9dc690fd8b9
mode 100644,000000..100644
--- /dev/null
@@@ -1,378 -1,0 +1,380 @@@
-     if let Some(higher::IfLet {
-         if_else,
-         let_pat,
-         let_expr,
-         ..
-     }) = higher::IfLet::hir(cx, expr)
-     {
-         find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some());
-     } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
 +use super::REDUNDANT_PATTERN_MATCHING;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::needs_ordered_drop;
 +use clippy_utils::{higher, match_def_path};
 +use clippy_utils::{is_lang_ctor, is_trait_method, paths};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, PollPending};
 +use rustc_hir::{
 +    intravisit::{walk_expr, Visitor},
 +    Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
 +use rustc_span::sym;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++    if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
 +        find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false);
 +    }
 +}
 +
++pub(super) fn check_if_let<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    pat: &'tcx Pat<'_>,
++    scrutinee: &'tcx Expr<'_>,
++    has_else: bool,
++) {
++    find_sugg_for_if_let(cx, expr, pat, scrutinee, "if", has_else);
++}
++
 +// Extract the generic arguments out of a type
 +fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
 +    if_chain! {
 +        if let ty::Adt(_, subs) = ty.kind();
 +        if let Some(sub) = subs.get(index);
 +        if let GenericArgKind::Type(sub_ty) = sub.unpack();
 +        then {
 +            Some(sub_ty)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +// Checks if there are any temporaries created in the given expression for which drop order
 +// matters.
 +fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    struct V<'a, 'tcx> {
 +        cx: &'a LateContext<'tcx>,
 +        res: bool,
 +    }
 +    impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
 +        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +            match expr.kind {
 +                // Taking the reference of a value leaves a temporary
 +                // e.g. In `&String::new()` the string is a temporary value.
 +                // Remaining fields are temporary values
 +                // e.g. In `(String::new(), 0).1` the string is a temporary value.
 +                ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
 +                    if !matches!(expr.kind, ExprKind::Path(_)) {
 +                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
 +                            self.res = true;
 +                        } else {
 +                            self.visit_expr(expr);
 +                        }
 +                    }
 +                },
 +                // the base type is alway taken by reference.
 +                // e.g. In `(vec![0])[0]` the vector is a temporary value.
 +                ExprKind::Index(base, index) => {
 +                    if !matches!(base.kind, ExprKind::Path(_)) {
 +                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
 +                            self.res = true;
 +                        } else {
 +                            self.visit_expr(base);
 +                        }
 +                    }
 +                    self.visit_expr(index);
 +                },
 +                // Method calls can take self by reference.
 +                // e.g. In `String::new().len()` the string is a temporary value.
 +                ExprKind::MethodCall(_, [self_arg, args @ ..], _) => {
 +                    if !matches!(self_arg.kind, ExprKind::Path(_)) {
 +                        let self_by_ref = self
 +                            .cx
 +                            .typeck_results()
 +                            .type_dependent_def_id(expr.hir_id)
 +                            .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
 +                        if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
 +                            self.res = true;
 +                        } else {
 +                            self.visit_expr(self_arg);
 +                        }
 +                    }
 +                    args.iter().for_each(|arg| self.visit_expr(arg));
 +                },
 +                // Either explicitly drops values, or changes control flow.
 +                ExprKind::DropTemps(_)
 +                | ExprKind::Ret(_)
 +                | ExprKind::Break(..)
 +                | ExprKind::Yield(..)
 +                | ExprKind::Block(Block { expr: None, .. }, _)
 +                | ExprKind::Loop(..) => (),
 +
 +                // Only consider the final expression.
 +                ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
 +
 +                _ => walk_expr(self, expr),
 +            }
 +        }
 +    }
 +
 +    let mut v = V { cx, res: false };
 +    v.visit_expr(expr);
 +    v.res
 +}
 +
 +fn find_sugg_for_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +    keyword: &'static str,
 +    has_else: bool,
 +) {
 +    // also look inside refs
 +    // if we have &None for example, peel it so we can detect "if let None = x"
 +    let check_pat = match let_pat.kind {
 +        PatKind::Ref(inner, _mutability) => inner,
 +        _ => let_pat,
 +    };
 +    let op_ty = cx.typeck_results().expr_ty(let_expr);
 +    // Determine which function should be used, and the type contained by the corresponding
 +    // variant.
 +    let (good_method, inner_ty) = match check_pat.kind {
 +        PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
 +            if let PatKind::Wild = sub_pat.kind {
 +                let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
 +                let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
 +                let lang_items = cx.tcx.lang_items();
 +                if Some(id) == lang_items.result_ok_variant() {
 +                    ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
 +                } else if Some(id) == lang_items.result_err_variant() {
 +                    ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
 +                } else if Some(id) == lang_items.option_some_variant() {
 +                    ("is_some()", op_ty)
 +                } else if Some(id) == lang_items.poll_ready_variant() {
 +                    ("is_ready()", op_ty)
 +                } else if match_def_path(cx, id, &paths::IPADDR_V4) {
 +                    ("is_ipv4()", op_ty)
 +                } else if match_def_path(cx, id, &paths::IPADDR_V6) {
 +                    ("is_ipv6()", op_ty)
 +                } else {
 +                    return;
 +                }
 +            } else {
 +                return;
 +            }
 +        },
 +        PatKind::Path(ref path) => {
 +            let method = if is_lang_ctor(cx, path, OptionNone) {
 +                "is_none()"
 +            } else if is_lang_ctor(cx, path, PollPending) {
 +                "is_pending()"
 +            } else {
 +                return;
 +            };
 +            // `None` and `Pending` don't have an inner type.
 +            (method, cx.tcx.types.unit)
 +        },
 +        _ => return,
 +    };
 +
 +    // If this is the last expression in a block or there is an else clause then the whole
 +    // type needs to be considered, not just the inner type of the branch being matched on.
 +    // Note the last expression in a block is dropped after all local bindings.
 +    let check_ty = if has_else
 +        || (keyword == "if" && matches!(cx.tcx.hir().parent_iter(expr.hir_id).next(), Some((_, Node::Block(..)))))
 +    {
 +        op_ty
 +    } else {
 +        inner_ty
 +    };
 +
 +    // All temporaries created in the scrutinee expression are dropped at the same time as the
 +    // scrutinee would be, so they have to be considered as well.
 +    // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
 +    // for the duration if body.
 +    let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
 +
 +    // check that `while_let_on_iterator` lint does not trigger
 +    if_chain! {
 +        if keyword == "while";
 +        if let ExprKind::MethodCall(method_path, _, _) = let_expr.kind;
 +        if method_path.ident.name == sym::next;
 +        if is_trait_method(cx, let_expr, sym::Iterator);
 +        then {
 +            return;
 +        }
 +    }
 +
 +    let result_expr = match &let_expr.kind {
 +        ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +        ExprKind::Unary(UnOp::Deref, deref) => deref,
 +        _ => let_expr,
 +    };
 +
 +    span_lint_and_then(
 +        cx,
 +        REDUNDANT_PATTERN_MATCHING,
 +        let_pat.span,
 +        &format!("redundant pattern matching, consider using `{}`", good_method),
 +        |diag| {
 +            // if/while let ... = ... { ... }
 +            // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +            let expr_span = expr.span;
 +
 +            // if/while let ... = ... { ... }
 +            //                 ^^^
 +            let op_span = result_expr.span.source_callsite();
 +
 +            // if/while let ... = ... { ... }
 +            // ^^^^^^^^^^^^^^^^^^^
 +            let span = expr_span.until(op_span.shrink_to_hi());
 +
 +            let app = if needs_drop {
 +                Applicability::MaybeIncorrect
 +            } else {
 +                Applicability::MachineApplicable
 +            };
 +
 +            let sugg = Sugg::hir_with_macro_callsite(cx, result_expr, "_")
 +                .maybe_par()
 +                .to_string();
 +
 +            diag.span_suggestion(span, "try this", format!("{} {}.{}", keyword, sugg, good_method), app);
 +
 +            if needs_drop {
 +                diag.note("this will change drop order of the result, as well as all temporaries");
 +                diag.note("add `#[allow(clippy::redundant_pattern_matching)]` if this is important");
 +            }
 +        },
 +    );
 +}
 +
 +pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
 +    if arms.len() == 2 {
 +        let node_pair = (&arms[0].pat.kind, &arms[1].pat.kind);
 +
 +        let found_good_method = match node_pair {
 +            (
 +                PatKind::TupleStruct(ref path_left, patterns_left, _),
 +                PatKind::TupleStruct(ref path_right, patterns_right, _),
 +            ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
 +                if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
 +                    find_good_method_for_match(
 +                        cx,
 +                        arms,
 +                        path_left,
 +                        path_right,
 +                        &paths::RESULT_OK,
 +                        &paths::RESULT_ERR,
 +                        "is_ok()",
 +                        "is_err()",
 +                    )
 +                    .or_else(|| {
 +                        find_good_method_for_match(
 +                            cx,
 +                            arms,
 +                            path_left,
 +                            path_right,
 +                            &paths::IPADDR_V4,
 +                            &paths::IPADDR_V6,
 +                            "is_ipv4()",
 +                            "is_ipv6()",
 +                        )
 +                    })
 +                } else {
 +                    None
 +                }
 +            },
 +            (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
 +            | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
 +                if patterns.len() == 1 =>
 +            {
 +                if let PatKind::Wild = patterns[0].kind {
 +                    find_good_method_for_match(
 +                        cx,
 +                        arms,
 +                        path_left,
 +                        path_right,
 +                        &paths::OPTION_SOME,
 +                        &paths::OPTION_NONE,
 +                        "is_some()",
 +                        "is_none()",
 +                    )
 +                    .or_else(|| {
 +                        find_good_method_for_match(
 +                            cx,
 +                            arms,
 +                            path_left,
 +                            path_right,
 +                            &paths::POLL_READY,
 +                            &paths::POLL_PENDING,
 +                            "is_ready()",
 +                            "is_pending()",
 +                        )
 +                    })
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        };
 +
 +        if let Some(good_method) = found_good_method {
 +            let span = expr.span.to(op.span);
 +            let result_expr = match &op.kind {
 +                ExprKind::AddrOf(_, _, borrowed) => borrowed,
 +                _ => op,
 +            };
 +            span_lint_and_then(
 +                cx,
 +                REDUNDANT_PATTERN_MATCHING,
 +                expr.span,
 +                &format!("redundant pattern matching, consider using `{}`", good_method),
 +                |diag| {
 +                    diag.span_suggestion(
 +                        span,
 +                        "try this",
 +                        format!("{}.{}", snippet(cx, result_expr.span, "_"), good_method),
 +                        Applicability::MaybeIncorrect, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[expect(clippy::too_many_arguments)]
 +fn find_good_method_for_match<'a>(
 +    cx: &LateContext<'_>,
 +    arms: &[Arm<'_>],
 +    path_left: &QPath<'_>,
 +    path_right: &QPath<'_>,
 +    expected_left: &[&str],
 +    expected_right: &[&str],
 +    should_be_left: &'a str,
 +    should_be_right: &'a str,
 +) -> Option<&'a str> {
 +    let left_id = cx
 +        .typeck_results()
 +        .qpath_res(path_left, arms[0].pat.hir_id)
 +        .opt_def_id()?;
 +    let right_id = cx
 +        .typeck_results()
 +        .qpath_res(path_right, arms[1].pat.hir_id)
 +        .opt_def_id()?;
 +    let body_node_pair = if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
 +        (&(*arms[0].body).kind, &(*arms[1].body).kind)
 +    } else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
 +        (&(*arms[1].body).kind, &(*arms[0].body).kind)
 +    } else {
 +        return None;
 +    };
 +
 +    match body_node_pair {
 +        (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
 +            (LitKind::Bool(true), LitKind::Bool(false)) => Some(should_be_left),
 +            (LitKind::Bool(false), LitKind::Bool(true)) => Some(should_be_right),
 +            _ => None,
 +        },
 +        _ => None,
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a211dc18f9e1e75c2676fcc371ec21694de5f9b0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,332 @@@
++use crate::FxHashSet;
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::get_attr;
++use clippy_utils::source::{indent_of, snippet};
++use rustc_errors::{Applicability, Diagnostic};
++use rustc_hir::intravisit::{walk_expr, Visitor};
++use rustc_hir::{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<'_>,
++    source: MatchSource,
++) {
++    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);
++            });
++        }
++    }
++}
++
++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 = *{};\n{}", original, 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!("{};\n{}", original, trailing_indent)
++    } else {
++        format!("let value = {};\n{}", original, 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.
++fn has_significant_drop_in_scrutinee<'tcx, 'a>(
++    cx: &'a LateContext<'tcx>,
++    scrutinee: &'tcx Expr<'tcx>,
++    source: MatchSource,
++) -> Option<(Vec<FoundSigDrop>, &'static str)> {
++    let mut helper = SigDropHelper::new(cx);
++    helper.find_sig_drop(scrutinee).map(|drops| {
++        let message = if source == MatchSource::Normal {
++            "temporary with significant drop in match scrutinee"
++        } else {
++            "temporary with significant drop in for loop"
++        };
++        (drops, message)
++    })
++}
++
++struct SigDropHelper<'a, 'tcx> {
++    cx: &'a LateContext<'tcx>,
++    is_chain_end: bool,
++    seen_types: FxHashSet<Ty<'tcx>>,
++    has_significant_drop: bool,
++    current_sig_drop: Option<FoundSigDrop>,
++    sig_drop_spans: Option<Vec<FoundSigDrop>>,
++    special_handling_for_binary_op: bool,
++}
++
++#[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,
++            seen_types: FxHashSet::default(),
++            has_significant_drop: false,
++            current_sig_drop: None,
++            sig_drop_spans: None,
++            special_handling_for_binary_op: false,
++        }
++    }
++
++    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.
++    /// However, if we are at the 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.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 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 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;
++    }
++
++    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,
++        }
++    }
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
++    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
++        if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) {
++            self.has_significant_drop = true;
++            return;
++        }
++        self.is_chain_end = false;
++
++        match ex.kind {
++            ExprKind::MethodCall(_, [ref 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(..) |
++            ExprKind::MethodCall(..) => 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);
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0491a0679f37abc89393aec264321c296316b721
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,145 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::{get_parent_expr, is_lang_ctor, match_def_path, paths};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::LangItem::ResultErr;
++use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, Ty};
++use rustc_span::{hygiene, sym};
++
++use super::TRY_ERR;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>) {
++    // Looks for a structure like this:
++    // match ::std::ops::Try::into_result(Err(5)) {
++    //     ::std::result::Result::Err(err) =>
++    //         #[allow(unreachable_code)]
++    //         return ::std::ops::Try::from_error(::std::convert::From::from(err)),
++    //     ::std::result::Result::Ok(val) =>
++    //         #[allow(unreachable_code)]
++    //         val,
++    // };
++    if_chain! {
++        if let ExprKind::Call(match_fun, try_args) = scrutinee.kind;
++        if let ExprKind::Path(ref match_fun_path) = match_fun.kind;
++        if matches!(match_fun_path, QPath::LangItem(LangItem::TryTraitBranch, ..));
++        if let Some(try_arg) = try_args.get(0);
++        if let ExprKind::Call(err_fun, err_args) = try_arg.kind;
++        if let Some(err_arg) = err_args.get(0);
++        if let ExprKind::Path(ref err_fun_path) = err_fun.kind;
++        if is_lang_ctor(cx, err_fun_path, ResultErr);
++        if let Some(return_ty) = find_return_type(cx, &expr.kind);
++        then {
++            let prefix;
++            let suffix;
++            let err_ty;
++
++            if let Some(ty) = result_error_type(cx, return_ty) {
++                prefix = "Err(";
++                suffix = ")";
++                err_ty = ty;
++            } else if let Some(ty) = poll_result_error_type(cx, return_ty) {
++                prefix = "Poll::Ready(Err(";
++                suffix = "))";
++                err_ty = ty;
++            } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
++                prefix = "Poll::Ready(Some(Err(";
++                suffix = ")))";
++                err_ty = ty;
++            } else {
++                return;
++            };
++
++            let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
++            let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
++            let mut applicability = Applicability::MachineApplicable;
++            let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
++            let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
++                "" // already returns
++            } else {
++                "return "
++            };
++            let suggestion = if err_ty == expr_err_ty {
++                format!("{}{}{}{}", ret_prefix, prefix, origin_snippet, suffix)
++            } else {
++                format!("{}{}{}.into(){}", ret_prefix, prefix, origin_snippet, suffix)
++            };
++
++            span_lint_and_sugg(
++                cx,
++                TRY_ERR,
++                expr.span,
++                "returning an `Err(_)` with the `?` operator",
++                "try this",
++                suggestion,
++                applicability,
++            );
++        }
++    }
++}
++
++/// Finds function return type by examining return expressions in match arms.
++fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> Option<Ty<'tcx>> {
++    if let ExprKind::Match(_, arms, MatchSource::TryDesugar) = expr {
++        for arm in arms.iter() {
++            if let ExprKind::Ret(Some(ret)) = arm.body.kind {
++                return Some(cx.typeck_results().expr_ty(ret));
++            }
++        }
++    }
++    None
++}
++
++/// Extracts the error type from Result<T, E>.
++fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
++    if_chain! {
++        if let ty::Adt(_, subst) = ty.kind();
++        if is_type_diagnostic_item(cx, ty, sym::Result);
++        then {
++            Some(subst.type_at(1))
++        } else {
++            None
++        }
++    }
++}
++
++/// Extracts the error type from Poll<Result<T, E>>.
++fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
++    if_chain! {
++        if let ty::Adt(def, subst) = ty.kind();
++        if match_def_path(cx, def.did(), &paths::POLL);
++        let ready_ty = subst.type_at(0);
++
++        if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
++        if cx.tcx.is_diagnostic_item(sym::Result, ready_def.did());
++        then {
++            Some(ready_subst.type_at(1))
++        } else {
++            None
++        }
++    }
++}
++
++/// Extracts the error type from Poll<Option<Result<T, E>>>.
++fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
++    if_chain! {
++        if let ty::Adt(def, subst) = ty.kind();
++        if match_def_path(cx, def.did(), &paths::POLL);
++        let ready_ty = subst.type_at(0);
++
++        if let ty::Adt(ready_def, ready_subst) = ready_ty.kind();
++        if cx.tcx.is_diagnostic_item(sym::Option, ready_def.did());
++        let some_ty = ready_subst.type_at(0);
++
++        if let ty::Adt(some_def, some_subst) = some_ty.kind();
++        if cx.tcx.is_diagnostic_item(sym::Result, some_def.did());
++        then {
++            Some(some_subst.type_at(1))
++        } else {
++            None
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23368238ef5cce6511ff799e008287b5b9fc4ccd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,55 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::SpanlessEq;
++use rustc_ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++use rustc_span::source_map::Spanned;
++use rustc_span::sym;
++
++use super::GET_LAST_WITH_LEN;
++
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
++    // Argument to "get" is a subtraction
++    if let ExprKind::Binary(
++        Spanned {
++            node: BinOpKind::Sub, ..
++        },
++        lhs,
++        rhs,
++    ) = arg.kind
++
++        // LHS of subtraction is "x.len()"
++        && let ExprKind::MethodCall(lhs_path, [lhs_recv], _) = &lhs.kind
++        && lhs_path.ident.name == sym::len
++
++        // RHS of subtraction is 1
++        && let ExprKind::Lit(rhs_lit) = &rhs.kind
++        && let LitKind::Int(1, ..) = rhs_lit.node
++
++        // check that recv == lhs_recv `recv.get(lhs_recv.len() - 1)`
++        && SpanlessEq::new(cx).eq_expr(recv, lhs_recv)
++        && !recv.can_have_side_effects()
++    {
++        let method = match cx.typeck_results().expr_ty_adjusted(recv).peel_refs().kind() {
++            ty::Adt(def, _) if cx.tcx.is_diagnostic_item(sym::VecDeque, def.did()) => "back",
++            ty::Slice(_) => "last",
++            _ => return,
++        };
++
++        let mut applicability = Applicability::MachineApplicable;
++        let recv_snippet = snippet_with_applicability(cx, recv.span, "_", &mut applicability);
++
++        span_lint_and_sugg(
++            cx,
++            GET_LAST_WITH_LEN,
++            expr.span,
++            &format!("accessing last element with `{recv_snippet}.get({recv_snippet}.len() - 1)`"),
++            "try",
++            format!("{recv_snippet}.{method}()"),
++            applicability,
++        );
++    }
++}
index d053ff567565c39686da673061745860d2d0eaba,0000000000000000000000000000000000000000..b8d1dabe007641423c65a180080412c750222117
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,74 @@@
-                     format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx),
 +use super::utils::derefs_to_slice;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{get_parent_expr, higher};
 +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;
 +use rustc_span::symbol::sym;
 +
 +use super::ITER_NEXT_SLICE;
 +
 +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, caller_expr: &'tcx hir::Expr<'_>) {
 +    // Skip lint if the `iter().next()` expression is a for loop argument,
 +    // since it is already covered by `&loops::ITER_NEXT_LOOP`
 +    let mut parent_expr_opt = get_parent_expr(cx, expr);
 +    while let Some(parent_expr) = parent_expr_opt {
 +        if higher::ForLoop::hir(parent_expr).is_some() {
 +            return;
 +        }
 +        parent_expr_opt = get_parent_expr(cx, parent_expr);
 +    }
 +
 +    if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() {
 +        // caller is a Slice
 +        if_chain! {
 +            if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
 +            if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
 +                = higher::Range::hir(index_expr);
 +            if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
 +            if let ast::LitKind::Int(start_idx, _) = start_lit.node;
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
++                let suggest = if start_idx == 0 {
++                    format!("{}.first()", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability))
++                } else {
++                    format!("{}.get({})", snippet_with_applicability(cx, caller_var.span, "..", &mut applicability), start_idx)
++                };
 +                span_lint_and_sugg(
 +                    cx,
 +                    ITER_NEXT_SLICE,
 +                    expr.span,
 +                    "using `.iter().next()` on a Slice without end index",
 +                    "try calling",
-                 "{}.get(0)",
++                    suggest,
 +                    applicability,
 +                );
 +            }
 +        }
 +    } else if is_vec_or_array(cx, caller_expr) {
 +        // caller is a Vec or an Array
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            ITER_NEXT_SLICE,
 +            expr.span,
 +            "using `.iter().next()` on an array",
 +            "try calling",
 +            format!(
++                "{}.first()",
 +                snippet_with_applicability(cx, caller_expr.span, "..", &mut applicability)
 +            ),
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
 +    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec)
 +        || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
 +}
index 3bf48e18019dfb4b2cbb6771e8bb20e65c6df9cd,0000000000000000000000000000000000000000..7308e74c323e3d542bcbe52fab2641695e230495
mode 100644,000000..100644
--- /dev/null
@@@ -1,2938 -1,0 +1,2977 @@@
-     /// # let opt = Some(1);
-     ///
-     /// // Bad
-     /// opt.unwrap();
-     ///
-     /// // Good
-     /// opt.expect("more helpful message");
 +mod bind_instead_of_map;
 +mod bytes_nth;
 +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 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_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_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
 +mod map_collect_result_unit;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod needless_option_as_deref;
 +mod needless_option_take;
++mod no_effect_replace;
 +mod ok_expect;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
 +use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +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};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +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 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.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    ///
 +    /// // Bad
 +    /// vec.iter().cloned().take(10);
 +    ///
 +    /// // Good
 +    /// vec.iter().take(10).cloned();
 +    ///
 +    /// // Bad
 +    /// vec.iter().cloned().last();
 +    ///
 +    /// // Good
 +    /// vec.iter().last().cloned();
 +    ///
 +    /// ```
 +    /// ### 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.
 +    ///
 +    #[clippy::version = "1.59.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()` calls on `Option`s and on `Result`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
-     /// // or
-     ///
++    /// # let option = Some(1);
++    /// # let result: Result<usize, ()> = Ok(1);
++    /// option.unwrap();
++    /// result.unwrap();
 +    /// ```
 +    ///
-     /// # let res: Result<usize, ()> = Ok(1);
-     ///
-     /// // Bad
-     /// res.unwrap();
-     ///
-     /// // Good
-     /// res.expect("more helpful message");
++    /// Use instead:
 +    /// ```rust
-     /// # let opt = Some(1);
-     ///
-     /// // Bad
-     /// opt.expect("one");
-     ///
-     /// // Good
-     /// let opt = Some(1);
-     /// opt?;
++    /// # let option = Some(1);
++    /// # let result: Result<usize, ()> = Ok(1);
++    /// option.expect("more helpful message");
++    /// result.expect("more helpful message");
 +    /// ```
 +    #[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()` calls on `Option`s and `Result`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
-     /// // or
-     ///
-     /// ```rust
-     /// # let res: Result<usize, ()> = Ok(1);
++    /// # let option = Some(1);
++    /// # let result: Result<usize, ()> = Ok(1);
++    /// option.expect("one");
++    /// result.expect("one");
 +    /// ```
 +    ///
-     /// // Bad
-     /// res.expect("one");
++    /// Use instead:
++    /// ```rust,ignore
++    /// # let option = Some(1);
++    /// # let result: Result<usize, ()> = Ok(1);
++    /// option?;
 +    ///
-     /// // Good
-     /// res?;
-     /// # Ok::<(), ()>(())
++    /// // or
 +    ///
-     /// # let x = Some(1);
-     ///
-     /// // Bad
-     /// x.map(|a| a + 1).unwrap_or(0);
-     ///
-     /// // Good
-     /// x.map_or(0, |a| a + 1);
++    /// 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::<_, ()>(());
 +    ///
 +    /// // Bad
 +    /// x.ok().expect("why did I do this again?");
 +    ///
 +    /// // Good
 +    /// 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.61.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);
 +    ///
 +    /// // Bad
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    ///
 +    /// // Good
 +    /// 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
-     /// // or
-     ///
++    /// # 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);
 +    /// ```
 +    ///
-     /// # let x: Result<usize, ()> = Ok(1);
++    /// Use instead:
 +    /// ```rust
-     ///
-     /// // Bad
-     /// x.map(|a| a + 1).unwrap_or_else(some_function);
-     ///
-     /// // Good
-     /// x.map_or_else(some_function, |a| a + 1);
++    /// # let option = Some(1);
++    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
-     /// this can instead be written:
++    /// 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);
 +    ///
 +    /// // Bad
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    ///
 +    /// // Good
 +    /// 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
 +    /// Bad:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```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();
 +    /// ```
 +    /// Could be written as
 +    /// ```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();
 +    /// ```
 +    /// Could be written as
 +    /// ```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);
 +    ///
 +    /// // Bad
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    ///
 +    /// // Good
 +    /// 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
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (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
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```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
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// let _ = "hello world".find("world").is_none();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// let _ = !"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('_') {};
 +    /// ```
 +    /// Could be written as
 +    /// ```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(..))`,
 +    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
 +    /// `unwrap_or_default` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
-     /// ```
-     /// or
-     /// ```rust
++    ///
++    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
-     /// ```
-     /// or
-     /// ```rust
++    ///
++    /// // or
++    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OR_FUN_CALL,
 +    perf,
 +    "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));
-     /// # let err_code = "418";
-     /// # let err_msg = "I'm a teapot";
++    ///
++    /// // or
++    ///
 +    /// # let foo = Some(String::new());
-     /// this can instead be written:
 +    /// 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);
 +    ///
 +    /// // Bad
 +    /// x.clone();
 +    ///
 +    /// // Good
 +    /// 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
 +    /// // Bad
 +    /// _.split("x");
 +    ///
 +    /// // Good
 +    /// _.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;
 +    /// // Bad
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    ///
 +    /// // Good
 +    /// # 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
++    /// // Bad
++    /// let x = vec![2, 3, 5];
++    /// let last_element = x.get(x.len() - 1);
++    ///
++    /// // Good
++    /// 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];
 +    ///
 +    /// // Bad
 +    /// a.extend(b.drain(..));
 +    ///
 +    /// // Good
 +    /// 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 = "_";
 +    ///
 +    /// // Bad
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    ///
 +    /// // Good
 +    /// 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
 +    /// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    /// This could be written as:
 +    /// ```rust
 +    /// let _ = (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
 +    /// // Bad
 +    /// let _ = (&vec![3, 4, 5]).into_iter();
 +    ///
 +    /// // Good
 +    /// let _ = (&vec![3, 4, 5]).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's 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");
 +    /// ```
 +    /// Could be written as
 +    /// ```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.52.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
 +    /// // Bad
 +    /// let _ = "Hello".bytes().nth(3);
 +    ///
 +    /// // Good
 +    /// let _ = "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
 +    /// // Bad
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.iter().count();
 +    /// let _ = &some_vec[..].iter().count();
 +    ///
 +    /// // Good
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.len();
 +    /// let _ = &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 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
 +    /// // Bad
 +    /// let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // use x
 +    /// }
 +    ///
 +    /// // Good
 +    /// let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // use x
 +    /// }
 +    /// ```
 +    #[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
 +    /// // Bad
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    ///
 +    /// // Good
 +    /// 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
 +    /// // Bad
 +    /// 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
 +    /// // Good
 +    /// 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
 +    /// // Bad
 +    /// let str = "key=value=add";
 +    /// let _ = str.splitn(3, '=').next().unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // Good
 +    /// let str = "key=value=add";
 +    /// let _ = str.split('=').next().unwrap();
 +    /// ```
 +    #[clippy::version = "1.58.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.58.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>
 +    /// ```
 +    /// Could be written as:
 +    /// ```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.61.0"]
 +    pub IS_DIGIT_ASCII_RADIX,
 +    style,
 +    "use of `char::is_digit(..)` with literal radix of 10 or 16"
 +}
 +
 +declare_clippy_lint! {
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref().take();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref();
 +    /// ```
 +    #[clippy::version = "1.61.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.62.0"]
++    pub NO_EFFECT_REPLACE,
++    suspicious,
++    "replace with no effect"
++}
++
 +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,
 +    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_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,
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) {
 +            let name = path.ident.name.as_str();
 +            return Some((name, args, path.ident.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, args, _) => {
 +                let method_span = method_call.ident.span;
 +                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
 +                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
 +                single_char_add_str::check(cx, expr, args);
 +                into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, args);
 +                unnecessary_to_owned::check(cx, expr, method_call.ident.name, 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());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.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) &&
 +                            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.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    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());
 +
 +            // 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 contains_ty(ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // 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 {
 +                            ty::Term::Ty(ty) => ty,
 +                            ty::Term::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 contains_ty(assoc_ty, self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_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.def_id.to_def_id()).self_ty().skip_binder();
 +            if !contains_ty(ret_ty, self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +impl Methods {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((name, [recv, args @ ..], span)) = 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),
 +                ("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);
 +                        }
 +                    },
 +                    _ => {},
 +                },
 +                (name @ "count", args @ []) => match method_call(recv) {
 +                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                    Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
 +                        iter_count::check(cx, expr, recv2, name2);
 +                    },
 +                    Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                    _ => {},
 +                },
 +                ("drain", [arg]) => {
 +                    iter_with_drain::check(cx, expr, recv, span, arg);
 +                },
 +                ("expect", [_]) => match method_call(recv) {
 +                    Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                    Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
 +                    _ => expect_used::check(cx, expr, recv, 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);
 +                },
 +                (name @ "flatten", args @ []) => 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, recv2, name, args),
 +                    _ => {},
 +                },
 +                ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +                ("for_each", [_]) => {
 +                    if let Some(("inspect", [_, _], span2)) = method_call(recv) {
 +                        inspect_for_each::check(cx, expr, span2);
 +                    }
 +                },
++                ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
 +                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +                ("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),
 +                ("join", [join_arg]) => {
 +                    if let Some(("collect", _, span)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", args @ []) | ("skip", args @ [_]) => {
 +                    if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv2, name, args);
 +                        }
 +                    }
 +                },
 +                (name @ ("map" | "map_err"), [m_arg]) => {
 +                    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),
 +                (name @ "next", args @ []) => {
 +                    if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
 +                        match (name2, args2) {
 +                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                            ("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", args @ [n_arg]) => match method_call(recv) {
 +                    Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv2, name, args),
 +                    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"),
 +                ("or_else", [arg]) => {
 +                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                    }
 +                },
 +                ("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", args @ [_arg]) => {
 +                    if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv2, name, args);
 +                        }
 +                    }
 +                },
 +                ("take", []) => needless_option_take::check(cx, expr, recv),
 +                ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
 +                    implicit_clone::check(cx, name, expr, recv);
 +                },
 +                ("unwrap", []) => {
 +                    match method_call(recv) {
 +                        Some(("get", [recv, get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
 +                        Some(("get_mut", [recv, get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
 +                        Some(("or", [recv, or_arg], or_span)) => {
 +                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                        },
 +                        _ => {},
 +                    }
 +                    unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests);
 +                },
 +                ("unwrap_or", [u_arg]) => match method_call(recv) {
 +                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
 +                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                    },
 +                    Some(("map", [m_recv, m_arg], span)) => {
 +                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                    },
 +                    _ => {},
 +                },
 +                ("unwrap_or_else", [u_arg]) => match method_call(recv) {
 +                    Some(("map", [recv, map_arg], _))
 +                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
 +                    _ => {
 +                        unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                        unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                    },
 +                },
++                ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
++                    no_effect_replace::check(cx, expr, arg1, arg2);
++                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    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,
 +}
 +
 +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_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => 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 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
 +    }
 +}
 +
 +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..a76341855b6db8fbb34cb2fb56f98317091acb2b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,47 @@@
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::SpanlessEq;
++use if_chain::if_chain;
++use rustc_ast::LitKind;
++use rustc_hir::ExprKind;
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::NO_EFFECT_REPLACE;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx rustc_hir::Expr<'_>,
++    arg1: &'tcx rustc_hir::Expr<'_>,
++    arg2: &'tcx rustc_hir::Expr<'_>,
++) {
++    let ty = cx.typeck_results().expr_ty(expr).peel_refs();
++    if !(ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)) {
++        return;
++    }
++
++    if_chain! {
++        if let ExprKind::Lit(spanned) = &arg1.kind;
++        if let Some(param1) = lit_string_value(&spanned.node);
++
++        if let ExprKind::Lit(spanned) = &arg2.kind;
++        if let LitKind::Str(param2, _) = &spanned.node;
++        if param1 == param2.as_str();
++
++        then {
++            span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
++        }
++    }
++
++    if SpanlessEq::new(cx).eq_expr(arg1, arg2) {
++        span_lint(cx, NO_EFFECT_REPLACE, expr.span, "replacing text with itself");
++    }
++}
++
++fn lit_string_value(node: &LitKind) -> Option<String> {
++    match node {
++        LitKind::Char(value) => Some(value.to_string()),
++        LitKind::Str(value, _) => Some(value.as_str().to_owned()),
++        _ => None,
++    }
++}
index 76bc9466ed81800e23cdf94f4f8b771a2cc1a895,0000000000000000000000000000000000000000..8989db54f6c5fb20bb19c3c3a1ef9540c32320d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,123 @@@
-         return span_lint_and_sugg(
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_lang_ctor, path_def_id};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::DefIdTree;
 +use rustc_span::symbol::sym;
 +
 +use super::OPTION_MAP_OR_NONE;
 +use super::RESULT_MAP_OR_INTO_OPTION;
 +
 +// The expression inside a closure may or may not have surrounding braces
 +// which causes problems when generating a suggestion.
 +fn reduce_unit_expression<'a>(expr: &'a hir::Expr<'_>) -> Option<(&'a hir::Expr<'a>, &'a [hir::Expr<'a>])> {
 +    match expr.kind {
 +        hir::ExprKind::Call(func, arg_char) => Some((func, arg_char)),
 +        hir::ExprKind::Block(block, _) => {
 +            match (block.stmts, block.expr) {
 +                (&[], Some(inner_expr)) => {
 +                    // If block only contains an expression,
 +                    // reduce `|x| { x + 1 }` to `|x| x + 1`
 +                    reduce_unit_expression(inner_expr)
 +                },
 +                _ => None,
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// lint use of `_.map_or(None, _)` for `Option`s and `Result`s
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx hir::Expr<'_>,
 +    recv: &'tcx hir::Expr<'_>,
 +    def_arg: &'tcx hir::Expr<'_>,
 +    map_arg: &'tcx hir::Expr<'_>,
 +) {
 +    let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
 +    let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
 +
 +    // There are two variants of this `map_or` lint:
 +    // (1) using `map_or` as an adapter from `Result<T,E>` to `Option<T>`
 +    // (2) using `map_or` as a combinator instead of `and_then`
 +    //
 +    // (For this lint) we don't care if any other type calls `map_or`
 +    if !is_option && !is_result {
 +        return;
 +    }
 +
 +    let default_arg_is_none = if let hir::ExprKind::Path(ref qpath) = def_arg.kind {
 +        is_lang_ctor(cx, qpath, OptionNone)
 +    } else {
 +        return;
 +    };
 +
 +    if !default_arg_is_none {
 +        // nothing to lint!
 +        return;
 +    }
 +
 +    let f_arg_is_some = if let hir::ExprKind::Path(ref qpath) = map_arg.kind {
 +        is_lang_ctor(cx, qpath, OptionSome)
 +    } else {
 +        false
 +    };
 +
 +    if is_option {
 +        let self_snippet = snippet(cx, recv.span, "..");
 +        if_chain! {
 +        if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
 +            let arg_snippet = snippet(cx, span, "..");
 +            let body = cx.tcx.hir().body(id);
 +                if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
 +                if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
 +                if Some(id) == cx.tcx.lang_items().option_some_variant();
 +                then {
 +                    let func_snippet = snippet(cx, arg_char.span, "..");
 +                    let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
 +                       `map(..)` instead";
 +                    return span_lint_and_sugg(
 +                        cx,
 +                        OPTION_MAP_OR_NONE,
 +                        expr.span,
 +                        msg,
 +                        "try using `map` instead",
 +                        format!("{0}.map({1} {2})", self_snippet, arg_snippet,func_snippet),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +
 +        }
 +
 +        let func_snippet = snippet(cx, map_arg.span, "..");
 +        let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
 +                       `and_then(..)` instead";
-         return span_lint_and_sugg(
++        span_lint_and_sugg(
 +            cx,
 +            OPTION_MAP_OR_NONE,
 +            expr.span,
 +            msg,
 +            "try using `and_then` instead",
 +            format!("{0}.and_then({1})", self_snippet, func_snippet),
 +            Applicability::MachineApplicable,
 +        );
 +    } else if f_arg_is_some {
 +        let msg = "called `map_or(None, Some)` on a `Result` value. This can be done more directly by calling \
 +                       `ok()` instead";
 +        let self_snippet = snippet(cx, recv.span, "..");
++        span_lint_and_sugg(
 +            cx,
 +            RESULT_MAP_OR_INTO_OPTION,
 +            expr.span,
 +            msg,
 +            "try using `ok` instead",
 +            format!("{0}.ok()", self_snippet),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 97c4feb3122a08fbaf684b56cd6d994bbf81f844,0000000000000000000000000000000000000000..b4c6bfb31ed1cb54a59ff276b96235aa4b84d7e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,431 -1,0 +1,431 @@@
-     is_clone_like(cx, &*method_name.as_str(), method_def_id)
 +use super::implicit_clone::is_clone_like;
 +use super::unnecessary_iter_cloned::{self, is_into_iter};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{
 +    contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
 +};
 +use clippy_utils::{meets_msrv, msrvs};
 +
 +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
 +use rustc_errors::Applicability;
 +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 +use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_span::{sym, Symbol};
 +use std::cmp::max;
 +
 +use super::UNNECESSARY_TO_OWNED;
 +
 +pub fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    args: &'tcx [Expr<'tcx>],
 +    msrv: Option<RustcVersion>,
 +) {
 +    if_chain! {
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if let [receiver] = args;
 +        then {
 +            if is_cloned_or_copied(cx, method_name, method_def_id) {
 +                unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
 +            } else if is_to_owned_like(cx, method_name, method_def_id) {
 +                // At this point, we know the call is of a `to_owned`-like function. The functions
 +                // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
 +                // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
 +                // argument in a `into_iter` call, or an argument in the call of some other function.
 +                if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
 +                    return;
 +                }
 +                if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
 +                    return;
 +                }
 +                check_other_call_arg(cx, expr, method_name, receiver);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +#[allow(clippy::too_many_lines)]
 +fn check_addr_of_expr(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +    receiver: &Expr<'_>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
 +        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
 +        if let
 +            // For matching uses of `Cow::from`
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching uses of arrays
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Pointer(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching everything else
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ] = adjustments[..];
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
 +        let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
 +        // Only flag cases satisfying at least one of the following three conditions:
 +        // * the referent and receiver types are distinct
 +        // * the referent/receiver type is a copyable array
 +        // * the method is `Cow::into_owned`
 +        // This restriction is to ensure there is no overlap between `redundant_clone` and this
 +        // lint. It also avoids the following false positive:
 +        //  https://github.com/rust-lang/rust-clippy/issues/8759
 +        //   Arrays are a bit of a corner case. Non-copyable arrays are handled by
 +        // `redundant_clone`, but copyable arrays are not.
 +        if *referent_ty != receiver_ty
 +            || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
 +            || is_cow_into_owned(cx, method_name, method_def_id);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNNECESSARY_TO_OWNED,
 +                    parent.span,
 +                    &format!("unnecessary use of `{}`", method_name),
 +                    "use",
 +                    format!(
 +                        "{:&>width$}{}",
 +                        "",
 +                        receiver_snippet,
 +                        width = n_target_refs - n_receiver_refs
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +                return true;
 +            }
 +            if_chain! {
 +                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
 +                if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
 +                then {
 +                    if n_receiver_refs > 0 {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            parent.span,
 +                            &format!("unnecessary use of `{}`", method_name),
 +                            "use",
 +                            receiver_snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    } else {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            expr.span.with_lo(receiver.span.hi()),
 +                            &format!("unnecessary use of `{}`", method_name),
 +                            "remove this",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                    return true;
 +                }
 +            }
 +            if_chain! {
 +                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
 +                then {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNNECESSARY_TO_OWNED,
 +                        parent.span,
 +                        &format!("unnecessary use of `{}`", method_name),
 +                        "use",
 +                        format!("{}.as_ref()", receiver_snippet),
 +                        Applicability::MachineApplicable,
 +                    );
 +                    return true;
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +fn check_into_iter_call_arg(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let Some(callee_def_id) = fn_def_id(cx, parent);
 +        if is_into_iter(cx, callee_def_id);
 +        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        let parent_ty = cx.typeck_results().expr_ty(parent);
 +        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
 +        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
 +                return true;
 +            }
 +            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
 +                "copied"
 +            } else {
 +                "cloned"
 +            };
 +            // The next suggestion may be incorrect because the removal of the `to_owned`-like
 +            // function could cause the iterator to hold a reference to a resource that is used
 +            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                parent.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
 +                Applicability::MaybeIncorrect,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
 +/// of a `to_owned`-like function is unnecessary.
 +fn check_other_call_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
 +        if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call);
 +        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +        if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
 +        if let Some(input) = fn_sig.inputs().get(i);
 +        let (input, n_refs) = peel_mid_ty_refs(*input);
 +        if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
 +        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
 +        if let [trait_predicate] = trait_predicates
 +            .iter()
 +            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
 +            .collect::<Vec<_>>()[..];
 +        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +        if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
 +        // types of `trait_predicate.trait_ref.substs`.
 +        if if trait_predicate.def_id() == deref_trait_id {
 +            if let [projection_predicate] = projection_predicates[..] {
 +                let normalized_ty =
 +                    cx.tcx
 +                        .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
 +                implements_trait(cx, receiver_ty, deref_trait_id, &[])
 +                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
 +                        .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
 +            } else {
 +                false
 +            }
 +        } else if trait_predicate.def_id() == as_ref_trait_id {
 +            let composed_substs = compose_substs(
 +                cx,
 +                &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
 +                call_substs,
 +            );
 +            implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
 +        } else {
 +            false
 +        };
 +        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
 +        // `Target = T`.
 +        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
 +        let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
 +        // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
 +        // `T` must not be instantiated with a reference
 +        // (https://github.com/rust-lang/rust-clippy/issues/8507).
 +        if (n_refs == 0 && !receiver_ty.is_ref())
 +            || trait_predicate.def_id() != as_ref_trait_id
 +            || !contains_ty(fn_sig.output(), input);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                maybe_arg.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
 +                Applicability::MachineApplicable,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
 +/// expression found (if any) along with the immediately prior expression.
 +fn skip_addr_of_ancestors<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mut expr: &'tcx Expr<'tcx>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
 +    while let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
 +            expr = parent;
 +        } else {
 +            return Some((parent, expr));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
 +/// `Substs`, and arguments.
 +fn get_callee_substs_and_args<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
 +    if_chain! {
 +        if let ExprKind::Call(callee, args) = expr.kind;
 +        let callee_ty = cx.typeck_results().expr_ty(callee);
 +        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
 +        then {
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            return Some((*callee_def_id, substs, args));
 +        }
 +    }
 +    if_chain! {
 +        if let ExprKind::MethodCall(_, args, _) = expr.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        then {
 +            let substs = cx.typeck_results().node_substs(expr.hir_id);
 +            return Some((method_def_id, substs, args));
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
 +fn get_input_traits_and_projections<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    callee_def_id: DefId,
 +    input: Ty<'tcx>,
 +) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
 +    let mut trait_predicates = Vec::new();
 +    let mut projection_predicates = Vec::new();
 +    for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
 +        // `substs` should have 1 + n elements. The first is the type on the left hand side of an
 +        // `as`. The remaining n are trait parameters.
 +        let is_input_substs = |substs: SubstsRef<'tcx>| {
 +            if_chain! {
 +                if let Some(arg) = substs.iter().next();
 +                if let GenericArgKind::Type(arg_ty) = arg.unpack();
 +                if arg_ty == input;
 +                then { true } else { false }
 +            }
 +        };
 +        match predicate.kind().skip_binder() {
 +            PredicateKind::Trait(trait_predicate) => {
 +                if is_input_substs(trait_predicate.trait_ref.substs) {
 +                    trait_predicates.push(trait_predicate);
 +                }
 +            },
 +            PredicateKind::Projection(projection_predicate) => {
 +                if is_input_substs(projection_predicate.projection_ty.substs) {
 +                    projection_predicates.push(projection_predicate);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    (trait_predicates, projection_predicates)
 +}
 +
 +/// Composes two substitutions by applying the latter to the types of the former.
 +fn compose_substs<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    left: &[GenericArg<'tcx>],
 +    right: SubstsRef<'tcx>,
 +) -> Vec<GenericArg<'tcx>> {
 +    left.iter()
 +        .map(|arg| {
 +            if let GenericArgKind::Type(arg_ty) = arg.unpack() {
 +                let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
 +                GenericArg::from(normalized_ty)
 +            } else {
 +                *arg
 +            }
 +        })
 +        .collect()
 +}
 +
 +/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
 +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
 +        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
 +}
 +
 +/// Returns true if the named method can be used to convert the receiver to its "owned"
 +/// representation.
 +fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
++    is_clone_like(cx, method_name.as_str(), method_def_id)
 +        || is_cow_into_owned(cx, method_name, method_def_id)
 +        || is_to_string(cx, method_name, method_def_id)
 +}
 +
 +/// Returns true if the named method is `Cow::into_owned`.
 +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
 +}
 +
 +/// Returns true if the named method is `ToString::to_string`.
 +fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "to_string" && is_diag_trait_item(cx, method_def_id, sym::ToString)
 +}
index 65d1f440b76391031109a7dbb7a1049cb9d869b3,0000000000000000000000000000000000000000..a081cde85725b3518b6f3ae863b2eee3d14f1e44
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-     /// ```ignore
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{match_trait_method, paths};
 +use if_chain::if_chain;
 +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::cmp::Ordering;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where `std::cmp::min` and `max` are
 +    /// used to clamp values, but switched so that the result is constant.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is in all probability not the intended outcome. At
 +    /// the least it hurts readability of the code.
 +    ///
 +    /// ### Example
-     /// ```
-     /// or
-     /// ```ignore
++    /// ```rust,ignore
 +    /// min(0, max(100, x))
++    ///
++    /// // or
++    ///
 +    /// x.max(100).min(0)
 +    /// ```
 +    /// It will always be equal to `0`. Probably the author meant to clamp the value
 +    /// between 0 and 100, but has erroneously swapped `min` and `max`.
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MIN_MAX,
 +    correctness,
 +    "`min(_, max(_, _))` (or vice versa) with bounds clamping the result to a constant"
 +}
 +
 +declare_lint_pass!(MinMaxPass => [MIN_MAX]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MinMaxPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
 +            if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) {
 +                if outer_max == inner_max {
 +                    return;
 +                }
 +                match (
 +                    outer_max,
 +                    Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(ie), &outer_c, &inner_c),
 +                ) {
 +                    (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (),
 +                    _ => {
 +                        span_lint(
 +                            cx,
 +                            MIN_MAX,
 +                            expr.span,
 +                            "this `min`/`max` combination leads to constant result",
 +                        );
 +                    },
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(PartialEq, Eq, Debug, Clone, Copy)]
 +enum MinMax {
 +    Min,
 +    Max,
 +}
 +
 +fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
 +    match expr.kind {
 +        ExprKind::Call(path, args) => {
 +            if let ExprKind::Path(ref qpath) = path.kind {
 +                cx.typeck_results()
 +                    .qpath_res(qpath, path.hir_id)
 +                    .opt_def_id()
 +                    .and_then(|def_id| match cx.tcx.get_diagnostic_name(def_id) {
 +                        Some(sym::cmp_min) => fetch_const(cx, args, MinMax::Min),
 +                        Some(sym::cmp_max) => fetch_const(cx, args, MinMax::Max),
 +                        _ => None,
 +                    })
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(path, args, _) => {
 +            if_chain! {
 +                if let [obj, _] = args;
 +                if cx.typeck_results().expr_ty(obj).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
 +                then {
 +                    if path.ident.name == sym!(max) {
 +                        fetch_const(cx, args, MinMax::Max)
 +                    } else if path.ident.name == sym!(min) {
 +                        fetch_const(cx, args, MinMax::Min)
 +                    } else {
 +                        None
 +                    }
 +                } else {
 +                    None
 +                }
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn fetch_const<'a>(cx: &LateContext<'_>, args: &'a [Expr<'a>], m: MinMax) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
 +    if args.len() != 2 {
 +        return None;
 +    }
 +    constant_simple(cx, cx.typeck_results(), &args[0]).map_or_else(
 +        || constant_simple(cx, cx.typeck_results(), &args[1]).map(|c| (m, c, &args[0])),
 +        |c| {
 +            if constant_simple(cx, cx.typeck_results(), &args[1]).is_none() {
 +                // otherwise ignore
 +                Some((m, c, &args[1]))
 +            } else {
 +                None
 +            }
 +        },
 +    )
 +}
index 7fdc28c5a062d3376472f4e2834f30d3934139ef,0000000000000000000000000000000000000000..5566569945322db0f70b110946eeca6b9bb02c00
mode 100644,000000..100644
--- /dev/null
@@@ -1,807 -1,0 +1,813 @@@
-     /// // Bad
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet, snippet_opt};
 +use clippy_utils::ty::{implements_trait, is_copy};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
 +    StmtKind, TyKind, UnOp,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::hygiene::DesugaringKind;
 +use rustc_span::source_map::{ExpnKind, Span};
 +use rustc_span::symbol::sym;
 +
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{
 +    get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment,
 +    match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
 +};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for function arguments and let bindings denoted as
 +    /// `ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `ref` declaration makes the function take an owned
 +    /// value, but turns the argument into a reference (which means that the value
 +    /// is destroyed when exiting the function). This adds not much value: either
 +    /// take a reference type, or take an owned value and create references in the
 +    /// body.
 +    ///
 +    /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
 +    /// type of `x` is more obvious with the former.
 +    ///
 +    /// ### Known problems
 +    /// If the argument is dereferenced within the function,
 +    /// removing the `ref` will lead to errors. This can be fixed by removing the
 +    /// dereferences, e.g., changing `*x` to `x` within the function.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// fn foo(ref x: u8) -> bool {
 +    ///     true
 +    /// }
 +    ///
 +    /// // Good
 +    /// fn foo(x: &u8) -> bool {
 +    ///     true
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TOPLEVEL_REF_ARG,
 +    style,
 +    "an entire binding declared as `ref`, in a function argument or a `let` statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for comparisons to NaN.
 +    ///
 +    /// ### Why is this bad?
 +    /// NaN does not compare meaningfully to anything – not
 +    /// even itself – so those comparisons are simply wrong.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1.0;
 +    ///
 +    /// // Bad
 +    /// if x == f32::NAN { }
 +    ///
 +    /// // Good
 +    /// if x.is_nan() { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_NAN,
 +    correctness,
 +    "comparisons to `NAN`, which will always return false, probably not intended"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for (in-)equality comparisons on floating-point
 +    /// values (apart from zero), except in functions called `*eq*` (which probably
 +    /// implement equality for a type involving floats).
 +    ///
 +    /// ### Why is this bad?
 +    /// Floating point calculations are usually imprecise, so
 +    /// asking if two values are *exactly* equal is asking for trouble. For a good
 +    /// guide on what to do, see [the floating point
 +    /// guide](http://www.floating-point-gui.de/errors/comparison).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1.2331f64;
 +    /// let y = 1.2332f64;
 +    ///
-     /// // Good
 +    /// if y == 1.23f64 { }
 +    /// if y != x {} // where both are floats
++    /// ```
 +    ///
-     /// // Bad
++    /// Use instead:
++    /// ```rust
++    /// # let x = 1.2331f64;
++    /// # let y = 1.2332f64;
 +    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
 +    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
 +    /// // let error_margin = std::f64::EPSILON;
 +    /// if (y - 1.23f64).abs() < error_margin { }
 +    /// if (y - x).abs() > error_margin { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FLOAT_CMP,
 +    pedantic,
 +    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for conversions to owned values just for the sake
 +    /// of a comparison.
 +    ///
 +    /// ### Why is this bad?
 +    /// The comparison can operate on a reference, so creating
 +    /// an owned value effectively throws it away directly afterwards, which is
 +    /// needlessly consuming code and heap space.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = "foo";
 +    /// # let y = String::from("foo");
 +    /// if x.to_owned() == y {}
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let x = "foo";
 +    /// # let y = String::from("foo");
 +    /// if x == y {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_OWNED,
 +    perf,
 +    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for getting the remainder of a division by one or minus
 +    /// one.
 +    ///
 +    /// ### Why is this bad?
 +    /// The result for a divisor of one can only ever be zero; for
 +    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
 +    /// the respective integer type) or results in zero. No one will write such code
 +    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
 +    /// contest, it's probably a bad idea. Use something more underhanded.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// let a = x % 1;
 +    /// let a = x % -1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MODULO_ONE,
 +    correctness,
 +    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of bindings with a single leading
 +    /// underscore.
 +    ///
 +    /// ### Why is this bad?
 +    /// A single leading underscore is usually used to indicate
 +    /// that a binding will not be used. Using such a binding breaks this
 +    /// expectation.
 +    ///
 +    /// ### Known problems
 +    /// The lint does not work properly with desugaring and
 +    /// macro, it has been allowed in the mean time.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _x = 0;
 +    /// let y = _x + 1; // Here we are using `_x`, even though it has a leading
 +    ///                 // underscore. We should rename `_x` to `x`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USED_UNDERSCORE_BINDING,
 +    pedantic,
 +    "using a binding which is prefixed with an underscore"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of short circuit boolean conditions as
 +    /// a
 +    /// statement.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using a short circuit boolean condition as a statement
 +    /// may hide the fact that the second part is executed or not depending on the
 +    /// outcome of the first part.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// f() && g(); // We should write `if f() { g(); }`.
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHORT_CIRCUIT_STATEMENT,
 +    complexity,
 +    "using a short circuit boolean condition as a statement"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Catch casts from `0` to some pointer type
 +    ///
 +    /// ### Why is this bad?
 +    /// This generally means `null` and is better expressed as
 +    /// {`std`, `core`}`::ptr::`{`null`, `null_mut`}.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let a = 0 as *const u32;
 +    ///
 +    /// // Good
 +    /// let a = std::ptr::null::<u32>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ZERO_PTR,
 +    style,
 +    "using `0 as *{const, mut} T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for (in-)equality comparisons on floating-point
 +    /// value and constant, except in functions called `*eq*` (which probably
 +    /// implement equality for a type involving floats).
 +    ///
 +    /// ### Why is this bad?
 +    /// Floating point calculations are usually imprecise, so
 +    /// asking if two values are *exactly* equal is asking for trouble. For a good
 +    /// guide on what to do, see [the floating point
 +    /// guide](http://www.floating-point-gui.de/errors/comparison).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: f64 = 1.0;
 +    /// const ONE: f64 = 1.00;
 +    ///
-     /// // Good
 +    /// if x == ONE { } // where both are floats
++    /// ```
 +    ///
++    /// Use instead:
++    /// ```rust
++    /// # let x: f64 = 1.0;
++    /// # const ONE: f64 = 1.00;
 +    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
 +    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
 +    /// // let error_margin = std::f64::EPSILON;
 +    /// if (x - ONE).abs() < error_margin { }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FLOAT_CMP_CONST,
 +    restriction,
 +    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
 +}
 +
 +declare_lint_pass!(MiscLints => [
 +    TOPLEVEL_REF_ARG,
 +    CMP_NAN,
 +    FLOAT_CMP,
 +    CMP_OWNED,
 +    MODULO_ONE,
 +    USED_UNDERSCORE_BINDING,
 +    SHORT_CIRCUIT_STATEMENT,
 +    ZERO_PTR,
 +    FLOAT_CMP_CONST
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MiscLints {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        k: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
 +        if let FnKind::Closure = k {
 +            // Does not apply to closures
 +            return;
 +        }
 +        if in_external_macro(cx.tcx.sess, span) {
 +            return;
 +        }
 +        for arg in iter_input_pats(decl, body) {
 +            if let PatKind::Binding(BindingAnnotation::Ref | BindingAnnotation::RefMut, ..) = arg.pat.kind {
 +                span_lint(
 +                    cx,
 +                    TOPLEVEL_REF_ARG,
 +                    arg.pat.span,
 +                    "`ref` directly on a function argument is ignored. \
 +                    Consider using a reference type instead",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.tcx.sess, stmt.span);
 +            if let StmtKind::Local(local) = stmt.kind;
 +            if let PatKind::Binding(an, .., name, None) = local.pat.kind;
 +            if let Some(init) = local.init;
 +            if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut;
 +            then {
 +                // use the macro callsite when the init span (but not the whole local span)
 +                // comes from an expansion like `vec![1, 2, 3]` in `let ref _ = vec![1, 2, 3];`
 +                let sugg_init = if init.span.from_expansion() && !local.span.from_expansion() {
 +                    Sugg::hir_with_macro_callsite(cx, init, "..")
 +                } else {
 +                    Sugg::hir(cx, init, "..")
 +                };
 +                let (mutopt, initref) = if an == BindingAnnotation::RefMut {
 +                    ("mut ", sugg_init.mut_addr())
 +                } else {
 +                    ("", sugg_init.addr())
 +                };
 +                let tyopt = if let Some(ty) = local.ty {
 +                    format!(": &{mutopt}{ty}", mutopt=mutopt, ty=snippet(cx, ty.span, ".."))
 +                } else {
 +                    String::new()
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    TOPLEVEL_REF_ARG,
 +                    init.hir_id,
 +                    local.pat.span,
 +                    "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "try",
 +                            format!(
 +                                "let {name}{tyopt} = {initref};",
 +                                name=snippet(cx, name.span, ".."),
 +                                tyopt=tyopt,
 +                                initref=initref,
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                );
 +            }
 +        };
 +        if_chain! {
 +            if let StmtKind::Semi(expr) = stmt.kind;
 +            if let ExprKind::Binary(ref binop, a, b) = expr.kind;
 +            if binop.node == BinOpKind::And || binop.node == BinOpKind::Or;
 +            if let Some(sugg) = Sugg::hir_opt(cx, a);
 +            then {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    SHORT_CIRCUIT_STATEMENT,
 +                    expr.hir_id,
 +                    stmt.span,
 +                    "boolean short circuit operator in statement may be clearer using an explicit test",
 +                    |diag| {
 +                        let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg };
 +                        diag.span_suggestion(
 +                            stmt.span,
 +                            "replace it with",
 +                            format!(
 +                                "if {} {{ {}; }}",
 +                                sugg,
 +                                &snippet(cx, b.span, ".."),
 +                            ),
 +                            Applicability::MachineApplicable, // snippet
 +                        );
 +                    });
 +            }
 +        };
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        match expr.kind {
 +            ExprKind::Cast(e, ty) => {
 +                check_cast(cx, expr.span, e, ty);
 +                return;
 +            },
 +            ExprKind::Binary(ref cmp, left, right) => {
 +                check_binary(cx, expr, cmp, left, right);
 +                return;
 +            },
 +            _ => {},
 +        }
 +        if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
 +            // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
 +            return;
 +        }
 +        let sym;
 +        let binding = match expr.kind {
 +            ExprKind::Path(ref qpath) if !matches!(qpath, hir::QPath::LangItem(..)) => {
 +                let binding = last_path_segment(qpath).ident.as_str();
 +                if binding.starts_with('_') &&
 +                    !binding.starts_with("__") &&
 +                    binding != "_result" && // FIXME: #944
 +                    is_used(cx, expr) &&
 +                    // don't lint if the declaration is in a macro
 +                    non_macro_local(cx, cx.qpath_res(qpath, expr.hir_id))
 +                {
 +                    Some(binding)
 +                } else {
 +                    None
 +                }
 +            },
 +            ExprKind::Field(_, ident) => {
 +                sym = ident.name;
 +                let name = sym.as_str();
 +                if name.starts_with('_') && !name.starts_with("__") {
 +                    Some(name)
 +                } else {
 +                    None
 +                }
 +            },
 +            _ => None,
 +        };
 +        if let Some(binding) = binding {
 +            span_lint(
 +                cx,
 +                USED_UNDERSCORE_BINDING,
 +                expr.span,
 +                &format!(
 +                    "used binding `{}` which is prefixed with an underscore. A leading \
 +                     underscore signals that a binding will not be used",
 +                    binding
 +                ),
 +            );
 +        }
 +    }
 +}
 +
 +fn get_lint_and_message(
 +    is_comparing_constants: bool,
 +    is_comparing_arrays: bool,
 +) -> (&'static rustc_lint::Lint, &'static str) {
 +    if is_comparing_constants {
 +        (
 +            FLOAT_CMP_CONST,
 +            if is_comparing_arrays {
 +                "strict comparison of `f32` or `f64` constant arrays"
 +            } else {
 +                "strict comparison of `f32` or `f64` constant"
 +            },
 +        )
 +    } else {
 +        (
 +            FLOAT_CMP,
 +            if is_comparing_arrays {
 +                "strict comparison of `f32` or `f64` arrays"
 +            } else {
 +                "strict comparison of `f32` or `f64`"
 +            },
 +        )
 +    }
 +}
 +
 +fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
 +    if_chain! {
 +        if !in_constant(cx, cmp_expr.hir_id);
 +        if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
 +        if match value {
 +            Constant::F32(num) => num.is_nan(),
 +            Constant::F64(num) => num.is_nan(),
 +            _ => false,
 +        };
 +        then {
 +            span_lint(
 +                cx,
 +                CMP_NAN,
 +                cmp_expr.span,
 +                "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
 +            );
 +        }
 +    }
 +}
 +
 +fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
 +        res
 +    } else {
 +        false
 +    }
 +}
 +
 +fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    match constant(cx, cx.typeck_results(), expr) {
 +        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
 +        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
 +        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
 +            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
 +            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
 +            _ => false,
 +        }),
 +        _ => false,
 +    }
 +}
 +
 +// Return true if `expr` is the result of `signum()` invoked on a float value.
 +fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    // The negation of a signum is still a signum
 +    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
 +        return is_signum(cx, child_expr);
 +    }
 +
 +    if_chain! {
 +        if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
 +        if sym!(signum) == method_name.ident.name;
 +        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
 +        // the method call)
 +        then {
 +            return is_float(cx, self_arg);
 +        }
 +    }
 +    false
 +}
 +
 +fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
 +
 +    if let ty::Array(arr_ty, _) = value {
 +        return matches!(arr_ty.kind(), ty::Float(_));
 +    };
 +
 +    matches!(value, ty::Float(_))
 +}
 +
 +fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
 +}
 +
 +#[expect(clippy::too_many_lines)]
 +fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
 +    #[derive(Default)]
 +    struct EqImpl {
 +        ty_eq_other: bool,
 +        other_eq_ty: bool,
 +    }
 +
 +    impl EqImpl {
 +        fn is_implemented(&self) -> bool {
 +            self.ty_eq_other || self.other_eq_ty
 +        }
 +    }
 +
 +    fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
 +        cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
 +            ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
 +            other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
 +        })
 +    }
 +
 +    let typeck = cx.typeck_results();
 +    let (arg, arg_span) = match expr.kind {
 +        ExprKind::MethodCall(.., [arg], _)
 +            if typeck
 +                .type_dependent_def_id(expr.hir_id)
 +                .and_then(|id| cx.tcx.trait_of_item(id))
 +                .map_or(false, |id| {
 +                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
 +                }) =>
 +        {
 +            (arg, arg.span)
 +        },
 +        ExprKind::Call(path, [arg])
 +            if path_def_id(cx, path)
 +                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
 +                .map_or(false, |idx| match idx {
 +                    0 => true,
 +                    1 => !is_copy(cx, typeck.expr_ty(expr)),
 +                    _ => false,
 +                }) =>
 +        {
 +            (arg, arg.span)
 +        },
 +        _ => return,
 +    };
 +
 +    let arg_ty = typeck.expr_ty(arg);
 +    let other_ty = typeck.expr_ty(other);
 +
 +    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
 +    let with_deref = arg_ty
 +        .builtin_deref(true)
 +        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
 +        .unwrap_or_default();
 +
 +    if !with_deref.is_implemented() && !without_deref.is_implemented() {
 +        return;
 +    }
 +
 +    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
 +
 +    let lint_span = if other_gets_derefed {
 +        expr.span.to(other.span)
 +    } else {
 +        expr.span
 +    };
 +
 +    span_lint_and_then(
 +        cx,
 +        CMP_OWNED,
 +        lint_span,
 +        "this creates an owned instance just for comparison",
 +        |diag| {
 +            // This also catches `PartialEq` implementations that call `to_owned`.
 +            if other_gets_derefed {
 +                diag.span_label(lint_span, "try implementing the comparison without allocating");
 +                return;
 +            }
 +
 +            let arg_snip = snippet(cx, arg_span, "..");
 +            let expr_snip;
 +            let eq_impl;
 +            if with_deref.is_implemented() {
 +                expr_snip = format!("*{}", arg_snip);
 +                eq_impl = with_deref;
 +            } else {
 +                expr_snip = arg_snip.to_string();
 +                eq_impl = without_deref;
 +            };
 +
 +            let span;
 +            let hint;
 +            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
 +                span = expr.span;
 +                hint = expr_snip;
 +            } else {
 +                span = expr.span.to(other.span);
 +
 +                let cmp_span = if other.span < expr.span {
 +                    other.span.between(expr.span)
 +                } else {
 +                    expr.span.between(other.span)
 +                };
 +                if eq_impl.ty_eq_other {
 +                    hint = format!(
 +                        "{}{}{}",
 +                        expr_snip,
 +                        snippet(cx, cmp_span, ".."),
 +                        snippet(cx, other.span, "..")
 +                    );
 +                } else {
 +                    hint = format!(
 +                        "{}{}{}",
 +                        snippet(cx, other.span, ".."),
 +                        snippet(cx, cmp_span, ".."),
 +                        expr_snip
 +                    );
 +                }
 +            }
 +
 +            diag.span_suggestion(
 +                span,
 +                "try",
 +                hint,
 +                Applicability::MachineApplicable, // snippet
 +            );
 +        },
 +    );
 +}
 +
 +/// Heuristic to see if an expression is used. Should be compatible with
 +/// `unused_variables`'s idea
 +/// of what it means for an expression to be "used".
 +fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
 +        ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
 +        _ => is_used(cx, parent),
 +    })
 +}
 +
 +/// Tests whether an expression is in a macro expansion (e.g., something
 +/// generated by `#[derive(...)]` or the like).
 +fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
 +    use rustc_span::hygiene::MacroKind;
 +    if expr.span.from_expansion() {
 +        let data = expr.span.ctxt().outer_expn_data();
 +        matches!(data.kind, ExpnKind::Macro(MacroKind::Attr, _))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Tests whether `res` is a variable defined outside a macro.
 +fn non_macro_local(cx: &LateContext<'_>, res: def::Res) -> bool {
 +    if let def::Res::Local(id) = res {
 +        !cx.tcx.hir().span(id).from_expansion()
 +    } else {
 +        false
 +    }
 +}
 +
 +fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) {
 +    if_chain! {
 +        if let TyKind::Ptr(ref mut_ty) = ty.kind;
 +        if let ExprKind::Lit(ref lit) = e.kind;
 +        if let LitKind::Int(0, _) = lit.node;
 +        if !in_constant(cx, e.hir_id);
 +        then {
 +            let (msg, sugg_fn) = match mut_ty.mutbl {
 +                Mutability::Mut => ("`0 as *mut _` detected", "std::ptr::null_mut"),
 +                Mutability::Not => ("`0 as *const _` detected", "std::ptr::null"),
 +            };
 +
 +            let (sugg, appl) = if let TyKind::Infer = mut_ty.ty.kind {
 +                (format!("{}()", sugg_fn), Applicability::MachineApplicable)
 +            } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
 +                (format!("{}::<{}>()", sugg_fn, mut_ty_snip), Applicability::MachineApplicable)
 +            } else {
 +                // `MaybeIncorrect` as type inference may not work with the suggested code
 +                (format!("{}()", sugg_fn), Applicability::MaybeIncorrect)
 +            };
 +            span_lint_and_sugg(cx, ZERO_PTR, span, msg, "try", sugg, appl);
 +        }
 +    }
 +}
 +
 +fn check_binary<'a>(
 +    cx: &LateContext<'a>,
 +    expr: &Expr<'_>,
 +    cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
 +    left: &'a Expr<'_>,
 +    right: &'a Expr<'_>,
 +) {
 +    let op = cmp.node;
 +    if op.is_comparison() {
 +        check_nan(cx, left, expr);
 +        check_nan(cx, right, expr);
 +        check_to_owned(cx, left, right, true);
 +        check_to_owned(cx, right, left, false);
 +    }
 +    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
 +        if is_allowed(cx, left) || is_allowed(cx, right) {
 +            return;
 +        }
 +
 +        // Allow comparing the results of signum()
 +        if is_signum(cx, left) && is_signum(cx, right) {
 +            return;
 +        }
 +
 +        if let Some(name) = get_item_name(cx, expr) {
 +            let name = name.as_str();
 +            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
 +                return;
 +            }
 +        }
 +        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
 +        let (lint, msg) = get_lint_and_message(
 +            is_named_constant(cx, left) || is_named_constant(cx, right),
 +            is_comparing_arrays,
 +        );
 +        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
 +            let lhs = Sugg::hir(cx, left, "..");
 +            let rhs = Sugg::hir(cx, right, "..");
 +
 +            if !is_comparing_arrays {
 +                diag.span_suggestion(
 +                    expr.span,
 +                    "consider comparing them within some margin of error",
 +                    format!(
 +                        "({}).abs() {} error_margin",
 +                        lhs - rhs,
 +                        if op == BinOpKind::Eq { '<' } else { '>' }
 +                    ),
 +                    Applicability::HasPlaceholders, // snippet
 +                );
 +            }
 +            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
 +        });
 +    } else if op == BinOpKind::Rem {
 +        if is_integer_const(cx, right, 1) {
 +            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
 +        }
 +
 +        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
 +            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
 +                span_lint(
 +                    cx,
 +                    MODULO_ONE,
 +                    expr.span,
 +                    "any number modulo -1 will panic/overflow or result in 0",
 +                );
 +            }
 +        };
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d466d54a6ba5db92c5af6f2f210644cbb73c9b55
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,116 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use rustc_data_structures::fx::FxHashMap;
++use rustc_hir::def::{DefKind, Res};
++use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::GenericParamDefKind;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for type parameters which are positioned inconsistently between
++    /// a type definition and impl block. Specifically, a paramater in an impl
++    /// block which has the same name as a parameter in the type def, but is in
++    /// a different place.
++    ///
++    /// ### Why is this bad?
++    /// Type parameters are determined by their position rather than name.
++    /// Naming type parameters inconsistently may cause you to refer to the
++    /// wrong type parameter.
++    ///
++    /// ### Example
++    /// ```rust
++    /// struct Foo<A, B> {
++    ///     x: A,
++    ///     y: B,
++    /// }
++    /// // inside the impl, B refers to Foo::A
++    /// impl<B, A> Foo<B, A> {}
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// struct Foo<A, B> {
++    ///     x: A,
++    ///     y: B,
++    /// }
++    /// impl<A, B> Foo<A, B> {}
++    /// ```
++    #[clippy::version = "1.62.0"]
++    pub MISMATCHING_TYPE_PARAM_ORDER,
++    pedantic,
++    "type parameter positioned inconsistently between type def and impl block"
++}
++declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
++
++impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        if_chain! {
++            if !item.span.from_expansion();
++            if let ItemKind::Impl(imp) = &item.kind;
++            if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
++            if let Some(segment) = path.segments.iter().next();
++            if let Some(generic_args) = segment.args;
++            if !generic_args.args.is_empty();
++            then {
++                // get the name and span of the generic parameters in the Impl
++                let impl_params = generic_args.args.iter()
++                .filter_map(|p|
++                    match p {
++                        GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
++                            Some((path.segments[0].ident.to_string(), path.span)),
++                        _ => None,
++                    }
++                );
++
++                // find the type that the Impl is for
++                // only lint on struct/enum/union for now
++                let defid = match path.res {
++                    Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
++                    _ => return,
++                };
++
++                // get the names of the generic parameters in the type
++                let type_params = &cx.tcx.generics_of(defid).params;
++                let type_param_names: Vec<_> = type_params.iter()
++                .filter_map(|p|
++                    match p.kind {
++                        GenericParamDefKind::Type {..} => Some(p.name.to_string()),
++                        _ => None,
++                    }
++                ).collect();
++                // hashmap of name -> index for mismatch_param_name
++                let type_param_names_hashmap: FxHashMap<&String, usize> =
++                    type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
++
++                let type_name = segment.ident;
++                for (i, (impl_param_name, impl_param_span)) in impl_params.enumerate() {
++                    if mismatch_param_name(i, &impl_param_name, &type_param_names_hashmap) {
++                        let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
++                                          type_name, impl_param_name);
++                        let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
++                                           type_param_names[i], type_name);
++                        span_lint_and_help(
++                            cx,
++                            MISMATCHING_TYPE_PARAM_ORDER,
++                            impl_param_span,
++                            &msg,
++                            None,
++                            &help
++                        );
++                    }
++                }
++            }
++        }
++    }
++}
++
++// Checks if impl_param_name is the same as one of type_param_names,
++// and is in a different position
++fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
++    if let Some(j) = type_param_names.get(impl_param_name) {
++        if i != *j {
++            return true;
++        }
++    }
++    false
++}
index 405fc23e8de0a5b68cab209b7d34f7bd663cd5f3,0000000000000000000000000000000000000000..024bd0760715e449fffa03f4bcb77f2cae98a79b
mode 100644,000000..100644
--- /dev/null
@@@ -1,348 -1,0 +1,348 @@@
-                     self.maybe_walk_expr(&*arm.body);
 +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;
 +    ///
 +    /// // Bad
 +    /// let a = {
 +    ///     x = 1;
 +    ///     1
 +    /// } + x;
 +    /// // Unclear whether a is 1 or 2.
 +    ///
 +    /// // Good
 +    /// 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 parent_node = match map.find(parent_id) {
 +            Some(parent) => parent,
 +            None => 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_expr<'a, 'tcx>(vis: &mut ReadVisitor<'a, '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<'a, 'tcx>(vis: &mut ReadVisitor<'a, '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 cba54e14212d0d18949213c00db27d684eea1237,0000000000000000000000000000000000000000..7e2531c7ca5f0c6237292bc89245ce108b1d8b2d
mode 100644,000000..100644
--- /dev/null
@@@ -1,175 -1,0 +1,175 @@@
-         check_ty(cx, local.span, cx.typeck_results().pat_ty(&*local.pat));
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::trait_ref_of_method;
 +use rustc_hir as hir;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::TypeFoldable;
 +use rustc_middle::ty::{Adt, Array, Ref, Slice, Tuple, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +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"
 +}
 +
 +declare_lint_pass!(MutableKeyType => [ MUTABLE_KEY_TYPE ]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
 +    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);
 +        }
 +    }
 +
 +    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.def_id).is_none() {
 +                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_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;
 +        }
++        check_ty(cx, local.span, cx.typeck_results().pat_ty(local.pat));
 +    }
 +}
 +
 +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);
 +    }
 +    check_ty(cx, decl.output.span(), cx.tcx.erase_late_bound_regions(fn_sig.output()));
 +}
 +
 +// 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");
 +        }
 +    }
 +}
 +
 +/// 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.at(span), cx.param_env)
 +            }
 +        },
 +        _ => false,
 +    }
 +}
index bb6d820b08cde0891a94ca665ba4cf5d8562866e,0000000000000000000000000000000000000000..44fdf84c6df79d6946f7cb923ff51abfa70fb72d
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,124 @@@
-     /// fn take_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
-     /// debug_assert!(take_a_mut_parameter(&mut 5));
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for function/method calls with a mutable
 +    /// parameter in `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!` macros.
 +    ///
 +    /// ### Why is this bad?
 +    /// In release builds `debug_assert!` macros are optimized out by the
 +    /// compiler.
 +    /// Therefore mutating something in a `debug_assert!` macro results in different behavior
 +    /// between a release and debug build.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// debug_assert_eq!(vec![3].pop(), Some(3));
++    ///
 +    /// // or
++    ///
++    /// # let mut x = 5;
++    /// # fn takes_a_mut_parameter(_: &mut u32) -> bool { unimplemented!() }
++    /// debug_assert!(takes_a_mut_parameter(&mut x));
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub DEBUG_ASSERT_WITH_MUT_CALL,
 +    nursery,
 +    "mutable arguments in `debug_assert{,_ne,_eq}!`"
 +}
 +
 +declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        let Some(macro_call) = root_macro_call_first_node(cx, e) else { return };
 +        let macro_name = cx.tcx.item_name(macro_call.def_id);
 +        if !matches!(
 +            macro_name.as_str(),
 +            "debug_assert" | "debug_assert_eq" | "debug_assert_ne"
 +        ) {
 +            return;
 +        }
 +        let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return };
 +        for arg in [lhs, rhs] {
 +            let mut visitor = MutArgVisitor::new(cx);
 +            visitor.visit_expr(arg);
 +            if let Some(span) = visitor.expr_span() {
 +                span_lint(
 +                    cx,
 +                    DEBUG_ASSERT_WITH_MUT_CALL,
 +                    span,
 +                    &format!(
 +                        "do not call a function with mutable arguments inside of `{}!`",
 +                        macro_name
 +                    ),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +struct MutArgVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    expr_span: Option<Span>,
 +    found: bool,
 +}
 +
 +impl<'a, 'tcx> MutArgVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        Self {
 +            cx,
 +            expr_span: None,
 +            found: false,
 +        }
 +    }
 +
 +    fn expr_span(&self) -> Option<Span> {
 +        if self.found { self.expr_span } else { None }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        match expr.kind {
 +            ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) => {
 +                self.found = true;
 +                return;
 +            },
 +            ExprKind::If(..) => {
 +                self.found = true;
 +                return;
 +            },
 +            ExprKind::Path(_) => {
 +                if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
 +                    if adj
 +                        .iter()
 +                        .any(|a| matches!(a.target.kind(), ty::Ref(_, _, Mutability::Mut)))
 +                    {
 +                        self.found = true;
 +                        return;
 +                    }
 +                }
 +            },
 +            // Don't check await desugars
 +            ExprKind::Match(_, _, MatchSource::AwaitDesugar) => return,
 +            _ if !self.found => self.expr_span = Some(expr.span),
 +            _ => return,
 +        }
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
index b70871b38beab178f63735330657ab8388e2f355,0000000000000000000000000000000000000000..26c694a71feddf4130a78ec63b7a83a3034d0002
mode 100644,000000..100644
--- /dev/null
@@@ -1,389 -1,0 +1,390 @@@
-     let applicability = if suggestions.len() > 1 {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::path_to_local;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::needs_ordered_drop;
 +use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
 +use rustc_errors::{Applicability, MultiSpan};
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{
 +    BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
 +    StmtKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for late initializations that can be replaced by a `let` statement
 +    /// with an initializer.
 +    ///
 +    /// ### Why is this bad?
 +    /// Assigning in the `let` statement is less repetitive.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a;
 +    /// a = 1;
 +    ///
 +    /// let b;
 +    /// match 3 {
 +    ///     0 => b = "zero",
 +    ///     1 => b = "one",
 +    ///     _ => b = "many",
 +    /// }
 +    ///
 +    /// let c;
 +    /// if true {
 +    ///     c = 1;
 +    /// } else {
 +    ///     c = -1;
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = 1;
 +    ///
 +    /// let b = match 3 {
 +    ///     0 => "zero",
 +    ///     1 => "one",
 +    ///     _ => "many",
 +    /// };
 +    ///
 +    /// let c = if true {
 +    ///     1
 +    /// } else {
 +    ///     -1
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub NEEDLESS_LATE_INIT,
 +    style,
 +    "late initializations that can be replaced by a `let` statement with an initializer"
 +}
 +declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
 +
 +fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
 +    let mut seen = false;
 +    expr_visitor(cx, |expr| {
 +        if let ExprKind::Assign(..) = expr.kind {
 +            seen = true;
 +        }
 +
 +        !seen
 +    })
 +    .visit_stmt(stmt);
 +
 +    seen
 +}
 +
 +fn contains_let(cond: &Expr<'_>) -> bool {
 +    let mut seen = false;
 +    expr_visitor_no_bodies(|expr| {
 +        if let ExprKind::Let(_) = expr.kind {
 +            seen = true;
 +        }
 +
 +        !seen
 +    })
 +    .visit_expr(cond);
 +
 +    seen
 +}
 +
 +fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
 +    let StmtKind::Local(local) = stmt.kind else { return false };
 +    !local.pat.walk_short(|pat| {
 +        if let PatKind::Binding(.., None) = pat.kind {
 +            !needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
 +        } else {
 +            true
 +        }
 +    })
 +}
 +
 +#[derive(Debug)]
 +struct LocalAssign {
 +    lhs_id: HirId,
 +    lhs_span: Span,
 +    rhs_span: Span,
 +    span: Span,
 +}
 +
 +impl LocalAssign {
 +    fn from_expr(expr: &Expr<'_>, span: Span) -> Option<Self> {
 +        if let ExprKind::Assign(lhs, rhs, _) = expr.kind {
 +            if lhs.span.from_expansion() {
 +                return None;
 +            }
 +
 +            Some(Self {
 +                lhs_id: path_to_local(lhs)?,
 +                lhs_span: lhs.span,
 +                rhs_span: rhs.span.source_callsite(),
 +                span,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +
 +    fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option<LocalAssign> {
 +        let assign = match expr.kind {
 +            ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span),
 +            ExprKind::Block(block, _) => {
 +                if_chain! {
 +                    if let Some((last, other_stmts)) = block.stmts.split_last();
 +                    if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind;
 +
 +                    let assign = Self::from_expr(expr, last.span)?;
 +
 +                    // avoid visiting if not needed
 +                    if assign.lhs_id == binding_id;
 +                    if other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt));
 +
 +                    then {
 +                        Some(assign)
 +                    } else {
 +                        None
 +                    }
 +                }
 +            },
 +            ExprKind::Assign(..) => Self::from_expr(expr, expr.span),
 +            _ => None,
 +        }?;
 +
 +        if assign.lhs_id == binding_id {
 +            Some(assign)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn assignment_suggestions<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    binding_id: HirId,
 +    exprs: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
 +) -> Option<(Applicability, Vec<(Span, String)>)> {
 +    let mut assignments = Vec::new();
 +
 +    for expr in exprs {
 +        let ty = cx.typeck_results().expr_ty(expr);
 +
 +        if ty.is_never() {
 +            continue;
 +        }
 +        if !ty.is_unit() {
 +            return None;
 +        }
 +
 +        let assign = LocalAssign::new(cx, expr, binding_id)?;
 +
 +        assignments.push(assign);
 +    }
 +
 +    let suggestions = assignments
 +        .iter()
 +        .map(|assignment| Some((assignment.span.until(assignment.rhs_span), String::new())))
 +        .chain(assignments.iter().map(|assignment| {
 +            Some((
 +                assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi()),
 +                String::new(),
 +            ))
 +        }))
 +        .collect::<Option<Vec<(Span, String)>>>()?;
 +
-         Applicability::Unspecified
-     } else {
-         Applicability::MachineApplicable
-     };
-     Some((applicability, suggestions))
++    match suggestions.len() {
++        // All of `exprs` are never types
++        // https://github.com/rust-lang/rust-clippy/issues/8911
++        0 => None,
++        1 => Some((Applicability::MachineApplicable, suggestions)),
 +        // multiple suggestions don't work with rustfix in multipart_suggest
 +        // https://github.com/rust-lang/rustfix/issues/141
++        _ => Some((Applicability::Unspecified, suggestions)),
++    }
 +}
 +
 +struct Usage<'tcx> {
 +    stmt: &'tcx Stmt<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    needs_semi: bool,
 +}
 +
 +fn first_usage<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    binding_id: HirId,
 +    local_stmt_id: HirId,
 +    block: &'tcx Block<'tcx>,
 +) -> Option<Usage<'tcx>> {
 +    let significant_drop = needs_ordered_drop(cx, cx.typeck_results().node_type(binding_id));
 +
 +    block
 +        .stmts
 +        .iter()
 +        .skip_while(|stmt| stmt.hir_id != local_stmt_id)
 +        .skip(1)
 +        .take_while(|stmt| !significant_drop || !stmt_needs_ordered_drop(cx, stmt))
 +        .find(|&stmt| is_local_used(cx, stmt, binding_id))
 +        .and_then(|stmt| match stmt.kind {
 +            StmtKind::Expr(expr) => Some(Usage {
 +                stmt,
 +                expr,
 +                needs_semi: true,
 +            }),
 +            StmtKind::Semi(expr) => Some(Usage {
 +                stmt,
 +                expr,
 +                needs_semi: false,
 +            }),
 +            _ => None,
 +        })
 +}
 +
 +fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &Local<'_>) -> Option<String> {
 +    let span = local.span.with_hi(match local.ty {
 +        // let <pat>: <ty>;
 +        // ~~~~~~~~~~~~~~~
 +        Some(ty) => ty.span.hi(),
 +        // let <pat>;
 +        // ~~~~~~~~~
 +        None => local.pat.span.hi(),
 +    });
 +
 +    snippet_opt(cx, span)
 +}
 +
 +fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    local: &'tcx Local<'tcx>,
 +    local_stmt: &'tcx Stmt<'tcx>,
 +    block: &'tcx Block<'tcx>,
 +    binding_id: HirId,
 +) -> Option<()> {
 +    let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?;
 +    let binding_name = cx.tcx.hir().opt_name(binding_id)?;
 +    let let_snippet = local_snippet_without_semicolon(cx, local)?;
 +
 +    match usage.expr.kind {
 +        ExprKind::Assign(..) => {
 +            let assign = LocalAssign::new(cx, usage.expr, binding_id)?;
 +            let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]);
 +            msg_span.push_span_label(local_stmt.span, "created here");
 +            msg_span.push_span_label(assign.span, "initialised here");
 +
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_LATE_INIT,
 +                msg_span,
 +                "unneeded late initialization",
 +                |diag| {
 +                    diag.tool_only_span_suggestion(
 +                        local_stmt.span,
 +                        "remove the local",
 +                        String::new(),
 +                        Applicability::MachineApplicable,
 +                    );
 +
 +                    diag.span_suggestion(
 +                        assign.lhs_span,
 +                        &format!("declare `{}` here", binding_name),
 +                        let_snippet,
 +                        Applicability::MachineApplicable,
 +                    );
 +                },
 +            );
 +        },
 +        ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
 +            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
 +
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_LATE_INIT,
 +                local_stmt.span,
 +                "unneeded late initialization",
 +                |diag| {
 +                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 +
 +                    diag.span_suggestion_verbose(
 +                        usage.stmt.span.shrink_to_lo(),
 +                        &format!("declare `{}` here", binding_name),
 +                        format!("{} = ", let_snippet),
 +                        applicability,
 +                    );
 +
 +                    diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
 +
 +                    if usage.needs_semi {
 +                        diag.span_suggestion(
 +                            usage.stmt.span.shrink_to_hi(),
 +                            "add a semicolon after the `if` expression",
 +                            ";".to_string(),
 +                            applicability,
 +                        );
 +                    }
 +                },
 +            );
 +        },
 +        ExprKind::Match(_, arms, MatchSource::Normal) => {
 +            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
 +
 +            span_lint_and_then(
 +                cx,
 +                NEEDLESS_LATE_INIT,
 +                local_stmt.span,
 +                "unneeded late initialization",
 +                |diag| {
 +                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
 +
 +                    diag.span_suggestion_verbose(
 +                        usage.stmt.span.shrink_to_lo(),
 +                        &format!("declare `{}` here", binding_name),
 +                        format!("{} = ", let_snippet),
 +                        applicability,
 +                    );
 +
 +                    diag.multipart_suggestion(
 +                        "remove the assignments from the `match` arms",
 +                        suggestions,
 +                        applicability,
 +                    );
 +
 +                    if usage.needs_semi {
 +                        diag.span_suggestion(
 +                            usage.stmt.span.shrink_to_hi(),
 +                            "add a semicolon after the `match` expression",
 +                            ";".to_string(),
 +                            applicability,
 +                        );
 +                    }
 +                },
 +            );
 +        },
 +        _ => {},
 +    };
 +
 +    Some(())
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
 +        let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
 +        if_chain! {
 +            if let Local {
 +                init: None,
 +                pat: &Pat {
 +                    kind: PatKind::Binding(BindingAnnotation::Unannotated, binding_id, _, None),
 +                    ..
 +                },
 +                source: LocalSource::Normal,
 +                ..
 +            } = local;
 +            if let Some((_, Node::Stmt(local_stmt))) = parents.next();
 +            if let Some((_, Node::Block(block))) = parents.next();
 +
 +            then {
 +                check(cx, local, local_stmt, block, binding_id);
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5c4de3381496cd839567ba17a5b2de1eef2e2b99
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,170 @@@
++use clippy_utils::consts::constant_simple;
++use clippy_utils::diagnostics::span_lint;
++use rustc_hir as hir;
++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 integer arithmetic operations which could overflow or panic.
++    ///
++    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
++    /// of overflowing according to the [Rust
++    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
++    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
++    /// attempted.
++    ///
++    /// ### Why is this bad?
++    /// Integer overflow will trigger a panic in debug builds or will wrap in
++    /// release mode. Division by zero will cause a panic in either mode. In some applications one
++    /// wants explicitly checked, wrapping or saturating arithmetic.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let a = 0;
++    /// a + 1;
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub INTEGER_ARITHMETIC,
++    restriction,
++    "any integer arithmetic expression which could overflow or panic"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for float arithmetic.
++    ///
++    /// ### Why is this bad?
++    /// For some embedded systems or kernel development, it
++    /// can be useful to rule out floating-point numbers.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let a = 0.0;
++    /// a + 1.0;
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub FLOAT_ARITHMETIC,
++    restriction,
++    "any floating-point arithmetic statement"
++}
++
++#[derive(Copy, Clone, Default)]
++pub struct NumericArithmetic {
++    expr_span: Option<Span>,
++    /// This field is used to check whether expressions are constants, such as in enum discriminants
++    /// and consts
++    const_span: Option<Span>,
++}
++
++impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
++
++impl<'tcx> LateLintPass<'tcx> for NumericArithmetic {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if self.expr_span.is_some() {
++            return;
++        }
++
++        if let Some(span) = self.const_span {
++            if span.contains(expr.span) {
++                return;
++            }
++        }
++        match &expr.kind {
++            hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
++                match op.node {
++                    hir::BinOpKind::And
++                    | hir::BinOpKind::Or
++                    | hir::BinOpKind::BitAnd
++                    | hir::BinOpKind::BitOr
++                    | hir::BinOpKind::BitXor
++                    | hir::BinOpKind::Eq
++                    | hir::BinOpKind::Lt
++                    | hir::BinOpKind::Le
++                    | hir::BinOpKind::Ne
++                    | hir::BinOpKind::Ge
++                    | hir::BinOpKind::Gt => return,
++                    _ => (),
++                }
++
++                let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
++                if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
++                    match op.node {
++                        hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
++                            hir::ExprKind::Lit(_lit) => (),
++                            hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
++                                if let hir::ExprKind::Lit(lit) = &expr.kind {
++                                    if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
++                                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++                                        self.expr_span = Some(expr.span);
++                                    }
++                                }
++                            },
++                            _ => {
++                                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++                                self.expr_span = Some(expr.span);
++                            },
++                        },
++                        _ => {
++                            span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++                            self.expr_span = Some(expr.span);
++                        },
++                    }
++                } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
++                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
++                    self.expr_span = Some(expr.span);
++                }
++            },
++            hir::ExprKind::Unary(hir::UnOp::Neg, arg) => {
++                let ty = cx.typeck_results().expr_ty(arg);
++                if constant_simple(cx, cx.typeck_results(), expr).is_none() {
++                    if ty.is_integral() {
++                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
++                        self.expr_span = Some(expr.span);
++                    } else if ty.is_floating_point() {
++                        span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
++                        self.expr_span = Some(expr.span);
++                    }
++                }
++            },
++            _ => (),
++        }
++    }
++
++    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
++        if Some(expr.span) == self.expr_span {
++            self.expr_span = None;
++        }
++    }
++
++    fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
++        let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
++
++        match cx.tcx.hir().body_owner_kind(body_owner) {
++            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
++                let body_span = cx.tcx.def_span(body_owner);
++
++                if let Some(span) = self.const_span {
++                    if span.contains(body_span) {
++                        return;
++                    }
++                }
++                self.const_span = Some(body_span);
++            },
++            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
++        }
++    }
++
++    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 {
++            if span.contains(body_span) {
++                return;
++            }
++        }
++        self.const_span = None;
++    }
++}
index b7a56970b335520aac10736b4ca9f9f660a2fe93,0000000000000000000000000000000000000000..21acf003d92b2a775d4068bcbcde535de7e7b531
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,87 @@@
-             &*cx.tcx.item_name(macro_call.def_id).as_str(),
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::return_ty;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::visitors::expr_visitor_no_bodies;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{FnKind, Visitor};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `panic!`, `unimplemented!`, `todo!`, `unreachable!` or assertions in a function of type result.
 +    ///
 +    /// ### Why is this bad?
 +    /// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
 +    ///
 +    /// ### Known problems
 +    /// Functions called from a function returning a `Result` may invoke a panicking macro. This is not checked.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn result_with_panic() -> Result<bool, String>
 +    /// {
 +    ///     panic!("error");
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn result_without_panic() -> Result<bool, String> {
 +    ///     Err(String::from("error"))
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub PANIC_IN_RESULT_FN,
 +    restriction,
 +    "functions of type `Result<..>` that contain `panic!()`, `todo!()`, `unreachable()`, `unimplemented()` or assertion"
 +}
 +
 +declare_lint_pass!(PanicInResultFn  => [PANIC_IN_RESULT_FN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PanicInResultFn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: FnKind<'tcx>,
 +        _: &'tcx hir::FnDecl<'tcx>,
 +        body: &'tcx hir::Body<'tcx>,
 +        span: Span,
 +        hir_id: hir::HirId,
 +    ) {
 +        if !matches!(fn_kind, FnKind::Closure) && is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
 +            lint_impl_body(cx, span, body);
 +        }
 +    }
 +}
 +
 +fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
 +    let mut panics = Vec::new();
 +    expr_visitor_no_bodies(|expr| {
 +        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
 +        if matches!(
++            cx.tcx.item_name(macro_call.def_id).as_str(),
 +            "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
 +        ) {
 +            panics.push(macro_call.span);
 +            return false;
 +        }
 +        true
 +    })
 +    .visit_expr(&body.value);
 +    if !panics.is_empty() {
 +        span_lint_and_then(
 +            cx,
 +            PANIC_IN_RESULT_FN,
 +            impl_span,
 +            "used `unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertion in a function that returns `Result`",
 +            move |diag| {
 +                diag.help(
 +                    "`unimplemented!()`, `unreachable!()`, `todo!()`, `panic!()` or assertions should not be used in a function that returns `Result` as `Result` is expected to return an error instead of crashing",
 +                );
 +                diag.span_note(panics, "return Err() instead of panicking");
 +            },
 +        );
 +    }
 +}
index e3ded716341f633028736d387076ceae8a39f31f,0000000000000000000000000000000000000000..5a93431f25a981f2db07e84f2da182814fbb8c13
mode 100644,000000..100644
--- /dev/null
@@@ -1,285 -1,0 +1,285 @@@
-             self.check_poly_fn(cx, item.def_id, &*method_sig.decl, None);
 +use std::cmp;
 +use std::iter;
 +
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::is_copy;
 +use clippy_utils::{is_self, is_self_ty};
 +use if_chain::if_chain;
 +use rustc_ast::attr;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::{sym, Span};
 +use rustc_target::spec::abi::Abi;
 +use rustc_target::spec::Target;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions taking arguments by reference, where
 +    /// the argument type is `Copy` and small enough to be more efficient to always
 +    /// pass by value.
 +    ///
 +    /// ### Why is this bad?
 +    /// In many calling conventions instances of structs will
 +    /// be passed through registers if they fit into two or less general purpose
 +    /// registers.
 +    ///
 +    /// ### Known problems
 +    /// This lint is target register size dependent, it is
 +    /// limited to 32-bit to try and reduce portability problems between 32 and
 +    /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
 +    /// will be different.
 +    ///
 +    /// The configuration option `trivial_copy_size_limit` can be set to override
 +    /// this limit for a project.
 +    ///
 +    /// This lint attempts to allow passing arguments by reference if a reference
 +    /// to that argument is returned. This is implemented by comparing the lifetime
 +    /// of the argument and return value for equality. However, this can cause
 +    /// false positives in cases involving multiple lifetimes that are bounded by
 +    /// each other.
 +    ///
 +    /// Also, it does not take account of other similar cases where getting memory addresses
 +    /// matters; namely, returning the pointer to the argument in question,
 +    /// and passing the argument, as both references and pointers,
 +    /// to a function that needs the memory address. For further details, refer to
 +    /// [this issue](https://github.com/rust-lang/rust-clippy/issues/5953)
 +    /// that explains a real case in which this false positive
 +    /// led to an **undefined behavior** introduced with unsafe code.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// // Bad
 +    /// fn foo(v: &u32) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Better
 +    /// fn foo(v: u32) {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRIVIALLY_COPY_PASS_BY_REF,
 +    pedantic,
 +    "functions taking small copyable arguments by reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions taking arguments by value, where
 +    /// the argument type is `Copy` and large enough to be worth considering
 +    /// passing by reference. Does not trigger if the function is being exported,
 +    /// because that might induce API breakage, if the parameter is declared as mutable,
 +    /// or if the argument is a `self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Arguments passed by value might result in an unnecessary
 +    /// shallow copy, taking up more space in the stack and requiring a call to
 +    /// `memcpy`, which can be expensive.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[derive(Clone, Copy)]
 +    /// struct TooLarge([u8; 2048]);
 +    ///
 +    /// // Bad
 +    /// fn foo(v: TooLarge) {}
 +    /// ```
 +    /// ```rust
 +    /// #[derive(Clone, Copy)]
 +    /// struct TooLarge([u8; 2048]);
 +    ///
 +    /// // Good
 +    /// fn foo(v: &TooLarge) {}
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub LARGE_TYPES_PASSED_BY_VALUE,
 +    pedantic,
 +    "functions taking large arguments by value"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct PassByRefOrValue {
 +    ref_min_size: u64,
 +    value_max_size: u64,
 +    avoid_breaking_exported_api: bool,
 +}
 +
 +impl<'tcx> PassByRefOrValue {
 +    pub fn new(
 +        ref_min_size: Option<u64>,
 +        value_max_size: u64,
 +        avoid_breaking_exported_api: bool,
 +        target: &Target,
 +    ) -> Self {
 +        let ref_min_size = ref_min_size.unwrap_or_else(|| {
 +            let bit_width = u64::from(target.pointer_width);
 +            // Cap the calculated bit width at 32-bits to reduce
 +            // portability problems between 32 and 64-bit targets
 +            let bit_width = cmp::min(bit_width, 32);
 +            #[expect(clippy::integer_division)]
 +            let byte_width = bit_width / 8;
 +            // Use a limit of 2 times the register byte width
 +            byte_width * 2
 +        });
 +
 +        Self {
 +            ref_min_size,
 +            value_max_size,
 +            avoid_breaking_exported_api,
 +        }
 +    }
 +
 +    fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option<Span>) {
 +        if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) {
 +            return;
 +        }
 +
 +        let fn_sig = cx.tcx.fn_sig(def_id);
 +        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
 +
 +        let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
 +
 +        for (index, (input, &ty)) in iter::zip(decl.inputs, fn_sig.inputs()).enumerate() {
 +            // All spans generated from a proc-macro invocation are the same...
 +            match span {
 +                Some(s) if s == input.span => return,
 +                _ => (),
 +            }
 +
 +            match ty.kind() {
 +                ty::Ref(input_lt, ty, Mutability::Not) => {
 +                    // Use lifetimes to determine if we're returning a reference to the
 +                    // argument. In that case we can't switch to pass-by-value as the
 +                    // argument will not live long enough.
 +                    let output_lts = match *fn_sig.output().kind() {
 +                        ty::Ref(output_lt, _, _) => vec![output_lt],
 +                        ty::Adt(_, substs) => substs.regions().collect(),
 +                        _ => vec![],
 +                    };
 +
 +                    if_chain! {
 +                        if !output_lts.contains(input_lt);
 +                        if is_copy(cx, *ty);
 +                        if let Some(size) = cx.layout_of(*ty).ok().map(|l| l.size.bytes());
 +                        if size <= self.ref_min_size;
 +                        if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind;
 +                        then {
 +                            let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
 +                                "self".into()
 +                            } else {
 +                                snippet(cx, decl_ty.span, "_").into()
 +                            };
 +                            span_lint_and_sugg(
 +                                cx,
 +                                TRIVIALLY_COPY_PASS_BY_REF,
 +                                input.span,
 +                                &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
 +                                "consider passing by value instead",
 +                                value_type,
 +                                Applicability::Unspecified,
 +                            );
 +                        }
 +                    }
 +                },
 +
 +                ty::Adt(_, _) | ty::Array(_, _) | ty::Tuple(_) => {
 +                    // if function has a body and parameter is annotated with mut, ignore
 +                    if let Some(param) = fn_body.and_then(|body| body.params.get(index)) {
 +                        match param.pat.kind {
 +                            PatKind::Binding(BindingAnnotation::Unannotated, _, _, _) => {},
 +                            _ => continue,
 +                        }
 +                    }
 +
 +                    if_chain! {
 +                        if is_copy(cx, ty);
 +                        if !is_self_ty(input);
 +                        if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
 +                        if size > self.value_max_size;
 +                        then {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                LARGE_TYPES_PASSED_BY_VALUE,
 +                                input.span,
 +                                &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size),
 +                                "consider passing by reference instead",
 +                                format!("&{}", snippet(cx, input.span, "_")),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        }
 +                    }
 +                },
 +
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        if item.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
++            self.check_poly_fn(cx, item.def_id, method_sig.decl, None);
 +        }
 +    }
 +
 +    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) => {
 +                if header.abi != Abi::Rust {
 +                    return;
 +                }
 +                let attrs = cx.tcx.hir().attrs(hir_id);
 +                for a in attrs {
 +                    if let Some(meta_items) = a.meta_item_list() {
 +                        if a.has_name(sym::proc_macro_derive)
 +                            || (a.has_name(sym::inline) && attr::list_contains_name(&meta_items, sym::always))
 +                        {
 +                            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;
 +            }
 +        }
 +
 +        self.check_poly_fn(cx, cx.tcx.hir().local_def_id(hir_id), decl, Some(span));
 +    }
 +}
index 26dc88a406e0294719c8fdc2d2f68d966c77b7fe,0000000000000000000000000000000000000000..eea036178b83764cc5a4d163413ec78ee3aba2cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,565 -1,0 +1,579 @@@
-                     check_possible_range_contains(cx, op.node, l, r, expr);
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
 +use clippy_utils::{higher, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast::RangeLimits;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::{Span, Spanned};
 +use rustc_span::sym;
 +use std::cmp::Ordering;
 +
 +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];
 +    /// x.iter().zip(0..x.len());
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let x = vec![1];
 +    /// 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 exclusive ranges where 1 is added to the
 +    /// upper bound, e.g., `x..(y+1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an inclusive range
 +    /// like `x..=y`.
 +    ///
 +    /// ### Known problems
 +    /// Will add unnecessary pair of parentheses when the
 +    /// expression is not wrapped in a pair but starts with an opening parenthesis
 +    /// and ends with a closing one.
 +    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
 +    ///
 +    /// Also in many cases, inclusive ranges are still slower to run than
 +    /// exclusive ranges, because they essentially add an extra branch that
 +    /// LLVM may fail to hoist out of the loop.
 +    ///
 +    /// This will cause a warning that cannot be fixed if the consumer of the
 +    /// range only accepts a specific range type, instead of the generic
 +    /// `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// for x..(y+1) { .. }
 +    /// ```
 +    /// Could be written as
 +    /// ```rust,ignore
 +    /// for x..=y { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_PLUS_ONE,
 +    pedantic,
 +    "`x..(y+1)` reads better as `x..=y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for inclusive ranges where 1 is subtracted from
 +    /// the upper bound, e.g., `x..=(y-1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an exclusive range
 +    /// like `x..y`.
 +    ///
 +    /// ### Known problems
 +    /// This will cause a warning that cannot be fixed if
 +    /// the consumer of the range only accepts a specific range type, instead of
 +    /// the generic `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// for x..=(y-1) { .. }
 +    /// ```
 +    /// Could be written as
 +    /// ```rust,ignore
 +    /// for x..y { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_MINUS_ONE,
 +    pedantic,
 +    "`x..=(y-1)` reads better as `x..y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for range expressions `x..y` where both `x` and `y`
 +    /// are constant and `x` is greater or equal to `y`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Empty ranges yield no values so iterating them is a no-op.
 +    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// fn main() {
 +    ///     (10..=0).for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[3..1];
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     (0..=10).rev().for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[1..3];
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub REVERSED_EMPTY_RANGES,
 +    correctness,
 +    "reversing the limits of range expressions, resulting in empty ranges"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions like `x >= 3 && x < 8` that could
 +    /// be more readably expressed as `(3..8).contains(x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `contains` expresses the intent better and has less
 +    /// failure modes (such as fencepost errors or using `||` instead of `&&`).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // given
 +    /// let x = 6;
 +    ///
 +    /// assert!(x >= 3 && x < 8);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    ///# let x = 6;
 +    /// assert!((3..8).contains(&x));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_RANGE_CONTAINS,
 +    style,
 +    "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 +}
 +
 +pub struct Ranges {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Ranges {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Ranges => [
 +    RANGE_ZIP_WITH_LEN,
 +    RANGE_PLUS_ONE,
 +    RANGE_MINUS_ONE,
 +    REVERSED_EMPTY_RANGES,
 +    MANUAL_RANGE_CONTAINS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ranges {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        match expr.kind {
 +            ExprKind::MethodCall(path, args, _) => {
 +                check_range_zip_with_len(cx, path, args, expr.span);
 +            },
 +            ExprKind::Binary(ref op, l, r) => {
 +                if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
-     let span = expr.span;
++                    check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        check_exclusive_range_plus_one(cx, expr);
 +        check_inclusive_range_minus_one(cx, expr);
 +        check_reversed_empty_range(cx, expr);
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_possible_range_contains(
 +    cx: &LateContext<'_>,
 +    op: BinOpKind,
 +    left: &Expr<'_>,
 +    right: &Expr<'_>,
 +    expr: &Expr<'_>,
++    span: Span,
 +) {
 +    if in_constant(cx, expr.hir_id) {
 +        return;
 +    }
 +
 +    let combine_and = match op {
 +        BinOpKind::And | BinOpKind::BitAnd => true,
 +        BinOpKind::Or | BinOpKind::BitOr => false,
 +        _ => return,
 +    };
 +    // value, name, order (higher/lower), inclusiveness
 +    if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) {
 +        // we only lint comparisons on the same name and with different
 +        // direction
 +        if l.id != r.id || l.ord == r.ord {
 +            return;
 +        }
 +        let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val);
 +        if combine_and && ord == Some(r.ord) {
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            // we only lint inclusive lower bounds
 +            if !l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("RangeInclusive", "..=")
 +            } else {
 +                ("Range", "..")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `{}::contains` implementation", range_type),
 +                "use",
 +                format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        } else if !combine_and && ord == Some(l.ord) {
 +            // `!_.contains(_)`
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            if l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("Range", "..")
 +            } else {
 +                ("RangeInclusive", "..=")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `!{}::contains` implementation", range_type),
 +                "use",
 +                format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        }
 +    }
++
++    // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have
++    // the same operator precedence
++    if_chain! {
++        if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind;
++        if op == lhs_op.node;
++        let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent());
++        if let Some(snip) = &snippet_opt(cx, new_span);
++        // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
++        if snip.matches('(').count() == snip.matches(')').count();
++        then {
++            check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
++        }
++    }
 +}
 +
 +struct RangeBounds<'a> {
 +    val: Constant,
 +    expr: &'a Expr<'a>,
 +    id: HirId,
 +    name_span: Span,
 +    val_span: Span,
 +    ord: Ordering,
 +    inc: bool,
 +}
 +
 +// Takes a binary expression such as x <= 2 as input
 +// Breaks apart into various pieces, such as the value of the number,
 +// hir id of the variable, and direction/inclusiveness of the operator
 +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
 +    if let ExprKind::Binary(ref op, l, r) = ex.kind {
 +        let (inclusive, ordering) = match op.node {
 +            BinOpKind::Gt => (false, Ordering::Greater),
 +            BinOpKind::Ge => (true, Ordering::Greater),
 +            BinOpKind::Lt => (false, Ordering::Less),
 +            BinOpKind::Le => (true, Ordering::Less),
 +            _ => return None,
 +        };
 +        if let Some(id) = path_to_local(l) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: r,
 +                    id,
 +                    name_span: l.span,
 +                    val_span: r.span,
 +                    ord: ordering,
 +                    inc: inclusive,
 +                });
 +            }
 +        } else if let Some(id) = path_to_local(r) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: l,
 +                    id,
 +                    name_span: r.span,
 +                    val_span: l.span,
 +                    ord: ordering.reverse(),
 +                    inc: inclusive,
 +                });
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
 +    if_chain! {
 +        if path.ident.as_str() == "zip";
 +        if let [iter, zip_arg] = args;
 +        // `.iter()` call
 +        if let ExprKind::MethodCall(iter_path, iter_args, _) = iter.kind;
 +        if iter_path.ident.name == sym::iter;
 +        // range expression in `.zip()` call: `0..x.len()`
 +        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
 +        if is_integer_const(cx, start, 0);
 +        // `.len()` call
 +        if let ExprKind::MethodCall(len_path, len_args, _) = end.kind;
 +        if len_path.ident.name == sym::len && len_args.len() == 1;
 +        // `.iter()` and `.len()` called on same `Path`
 +        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
 +        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
 +        if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
 +        then {
 +            span_lint(cx,
 +                RANGE_ZIP_WITH_LEN,
 +                span,
 +                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
 +                    snippet(cx, iter_args[0].span, "_"))
 +            );
 +        }
 +    }
 +}
 +
 +// exclusive range plus one: `x..(y+1)`
 +fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range {
 +            start,
 +            end: Some(end),
 +            limits: RangeLimits::HalfOpen
 +        }) = higher::Range::hir(expr);
 +        if let Some(y) = y_plus_one(cx, end);
 +        then {
 +            let span = if expr.span.from_expansion() {
 +                expr.span
 +                    .ctxt()
 +                    .outer_expn_data()
 +                    .call_site
 +            } else {
 +                expr.span
 +            };
 +            span_lint_and_then(
 +                cx,
 +                RANGE_PLUS_ONE,
 +                span,
 +                "an inclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
 +                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("({}..={})", start, end),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        } else {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("{}..={}", start, end),
 +                                Applicability::MachineApplicable, // snippet
 +                            );
 +                        }
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +// inclusive range minus one: `x..=(y-1)`
 +fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
 +        if let Some(y) = y_minus_one(cx, end);
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                RANGE_MINUS_ONE,
 +                expr.span,
 +                "an exclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "use",
 +                        format!("{}..{}", start, end),
 +                        Applicability::MachineApplicable, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        matches!(
 +            get_parent_expr(cx, expr),
 +            Some(Expr {
 +                kind: ExprKind::Index(..),
 +                ..
 +            })
 +        )
 +    }
 +
 +    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        let mut cur_expr = expr;
 +        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
 +            match higher::ForLoop::hir(parent_expr) {
 +                Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true,
 +                _ => cur_expr = parent_expr,
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
 +        match limits {
 +            RangeLimits::HalfOpen => ordering != Ordering::Less,
 +            RangeLimits::Closed => ordering == Ordering::Greater,
 +        }
 +    }
 +
 +    if_chain! {
 +        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
 +        let ty = cx.typeck_results().expr_ty(start);
 +        if let ty::Int(_) | ty::Uint(_) = ty.kind();
 +        if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
 +        if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
 +        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
 +        if is_empty_range(limits, ordering);
 +        then {
 +            if inside_indexing_expr(cx, expr) {
 +                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
 +                if ordering != Ordering::Equal {
 +                    span_lint(
 +                        cx,
 +                        REVERSED_EMPTY_RANGES,
 +                        expr.span,
 +                        "this range is reversed and using it to index a slice will panic at run-time",
 +                    );
 +                }
 +            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
 +            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
 +                span_lint_and_then(
 +                    cx,
 +                    REVERSED_EMPTY_RANGES,
 +                    expr.span,
 +                    "this range is empty so it will yield no values",
 +                    |diag| {
 +                        if ordering != Ordering::Equal {
 +                            let start_snippet = snippet(cx, start.span, "_");
 +                            let end_snippet = snippet(cx, end.span, "_");
 +                            let dots = match limits {
 +                                RangeLimits::HalfOpen => "..",
 +                                RangeLimits::Closed => "..="
 +                            };
 +
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using the following if you are attempting to iterate over this \
 +                                 range in reverse",
 +                                format!("({}{}{}).rev()", end_snippet, dots, start_snippet),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) => {
 +            if is_integer_const(cx, lhs, 1) {
 +                Some(rhs)
 +            } else if is_integer_const(cx, rhs, 1) {
 +                Some(lhs)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Sub, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
 +        _ => None,
 +    }
 +}
index 110f58f3734dfc348e9d00f72cd7dfce3cf4d464,0000000000000000000000000000000000000000..8db8c4e9b7870117d71022f7baa10809ab1c6bbf
mode 100644,000000..100644
--- /dev/null
@@@ -1,141 -1,0 +1,139 @@@
-     /// Checks for `Arc::new` or `Rc::new` in `vec![elem; len]`
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::VecArgs;
 +use clippy_utils::last_path_segment;
 +use clippy_utils::macros::root_macro_call_first_node;
++use clippy_utils::paths;
 +use clippy_utils::source::{indent_of, snippet};
++use clippy_utils::ty::match_type;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, QPath, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// This will create `elem` once and clone it `len` times - doing so with `Arc` or `Rc`
++    /// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`)
++    /// in `vec![elem; len]`
 +    ///
 +    /// ### Why is this bad?
-     ///
++    /// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak`
 +    /// is a bit misleading, as it will create references to the same pointer, rather
 +    /// than different instances.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let v = vec![std::sync::Arc::new("some data".to_string()); 100];
 +    /// // or
 +    /// let v = vec![std::rc::Rc::new("some data".to_string()); 100];
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
-     "initializing `Arc` or `Rc` in `vec![elem; len]`"
 +    /// // Initialize each value separately:
 +    /// let mut data = Vec::with_capacity(100);
 +    /// for _ in 0..100 {
 +    ///     data.push(std::rc::Rc::new("some data".to_string()));
 +    /// }
 +    ///
 +    /// // Or if you want clones of the same reference,
 +    /// // Create the reference beforehand to clarify that
 +    /// // it should be cloned for each value
 +    /// let data = std::rc::Rc::new("some data".to_string());
 +    /// let v = vec![data; 100];
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub RC_CLONE_IN_VEC_INIT,
 +    suspicious,
-         let Some(symbol) = new_reference_call(cx, elem) else { return; };
++    "initializing reference-counted pointer in `vec![elem; len]`"
 +}
 +declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]);
 +
 +impl LateLintPass<'_> for RcCloneInVecInit {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; };
 +        let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; };
-         emit_lint(cx, symbol, macro_call.span, elem, len);
++        let Some((symbol, func_span)) = ref_init(cx, elem) else { return; };
 +
- fn elem_snippet(cx: &LateContext<'_>, elem: &Expr<'_>, symbol_name: &str) -> String {
-     let elem_snippet = snippet(cx, elem.span, "..").to_string();
-     if elem_snippet.contains('\n') {
-         // This string must be found in `elem_snippet`, otherwise we won't be constructing
-         // the snippet in the first place.
-         let reference_creation = format!("{symbol_name}::new");
-         let (code_until_reference_creation, _right) = elem_snippet.split_once(&reference_creation).unwrap();
-         return format!("{code_until_reference_creation}{reference_creation}(..)");
-     }
-     elem_snippet
- }
++        emit_lint(cx, symbol, macro_call.span, elem, len, func_span);
 +    }
 +}
 +
- fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>) {
 +fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String {
 +    format!(
 +        r#"{{
 +{indent}    let mut v = Vec::with_capacity({len});
 +{indent}    (0..{len}).for_each(|_| v.push({elem}));
 +{indent}    v
 +{indent}}}"#
 +    )
 +}
 +
 +fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String {
 +    format!(
 +        "{{
 +{indent}    let data = {elem};
 +{indent}    vec![data; {len}]
 +{indent}}}"
 +    )
 +}
 +
-         &format!("calling `{symbol_name}::new` in `vec![elem; len]`"),
++fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) {
 +    let symbol_name = symbol.as_str();
 +
 +    span_lint_and_then(
 +        cx,
 +        RC_CLONE_IN_VEC_INIT,
 +        lint_span,
-             let elem_snippet = elem_snippet(cx, elem, symbol_name);
++        "initializing a reference-counted pointer in `vec![elem; len]`",
 +        |diag| {
 +            let len_snippet = snippet(cx, len.span, "..");
-                 Applicability::Unspecified,
++            let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), ".."));
 +            let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0));
 +            let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
 +            let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
 +
 +            diag.note(format!("each element will point to the same `{symbol_name}` instance"));
 +            diag.span_suggestion(
 +                lint_span,
 +                format!("consider initializing each `{symbol_name}` element individually"),
 +                loop_init_suggestion,
-                 Applicability::Unspecified,
++                Applicability::HasPlaceholders,
 +            );
 +            diag.span_suggestion(
 +                lint_span,
 +                format!(
 +                    "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
 +                ),
 +                extract_suggestion,
- /// Checks whether the given `expr` is a call to `Arc::new` or `Rc::new`
- fn new_reference_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
++                Applicability::HasPlaceholders,
 +            );
 +        },
 +    );
 +}
 +
-         if last_path_segment(func_path).ident.name == sym::new;
++/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak`
++fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> {
 +    if_chain! {
 +        if let ExprKind::Call(func, _args) = expr.kind;
 +        if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind;
 +        if let TyKind::Path(ref ty_path) = ty.kind;
 +        if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id();
-             return cx.tcx.get_diagnostic_name(def_id).filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc);
 +
 +        then {
++            if last_path_segment(func_path).ident.name == sym::new
++                && let Some(symbol) = cx
++                    .tcx
++                    .get_diagnostic_name(def_id)
++                    .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) {
++                return Some((symbol, func.span));
++            }
++
++            let ty_path = cx.typeck_results().expr_ty(expr);
++            if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) {
++                return Some((Symbol::intern("Weak"), func.span));
++            }
 +        }
 +    }
 +
 +    None
 +}
index ab16fe47d4dfbe536844123b4b742c5cbb3d26e3,0000000000000000000000000000000000000000..249f11f985082f7c55f7b6d3f37c28f15732661b
mode 100644,000000..100644
--- /dev/null
@@@ -1,776 -1,0 +1,776 @@@
-         if let ty::FnDef(def_id, _) = *func.ty(&*mir, cx.tcx).kind();
-         if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(&*mir, cx.tcx));
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
 +use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{def_id, Body, FnDecl, HirId};
 +use rustc_index::bit_set::{BitSet, HybridBitSet};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::{
 +    self, traversal,
 +    visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor as _},
 +    Mutability,
 +};
 +use rustc_middle::ty::{self, fold::TypeVisitor, Ty};
 +use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::{BytePos, Span};
 +use rustc_span::sym;
 +use std::ops::ControlFlow;
 +
 +macro_rules! unwrap_or_continue {
 +    ($x:expr) => {
 +        match $x {
 +            Some(x) => x,
 +            None => continue,
 +        }
 +    };
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a redundant `clone()` (and its relatives) which clones an owned
 +    /// value that is going to be dropped without further use.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not always possible for the compiler to eliminate useless
 +    /// allocations and deallocations generated by redundant `clone()`s.
 +    ///
 +    /// ### Known problems
 +    /// False-negatives: analysis performed by this lint is conservative and limited.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::path::Path;
 +    /// # #[derive(Clone)]
 +    /// # struct Foo;
 +    /// # impl Foo {
 +    /// #     fn new() -> Self { Foo {} }
 +    /// # }
 +    /// # fn call(x: Foo) {}
 +    /// {
 +    ///     let x = Foo::new();
 +    ///     call(x.clone());
 +    ///     call(x.clone()); // this can just pass `x`
 +    /// }
 +    ///
 +    /// ["lorem", "ipsum"].join(" ").to_string();
 +    ///
 +    /// Path::new("/a/b").join("c").to_path_buf();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub REDUNDANT_CLONE,
 +    perf,
 +    "`clone()` of an owned value that is going to be dropped immediately"
 +}
 +
 +declare_lint_pass!(RedundantClone => [REDUNDANT_CLONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantClone {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        _: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        let def_id = cx.tcx.hir().body_owner_def_id(body.id());
 +
 +        // Building MIR for `fn`s with unsatisfiable preds results in ICE.
 +        if fn_has_unsatisfiable_preds(cx, def_id.to_def_id()) {
 +            return;
 +        }
 +
 +        let mir = cx.tcx.optimized_mir(def_id.to_def_id());
 +
 +        let possible_origin = {
 +            let mut vis = PossibleOriginVisitor::new(mir);
 +            vis.visit_body(mir);
 +            vis.into_map(cx)
 +        };
 +        let maybe_storage_live_result = MaybeStorageLive
 +            .into_engine(cx.tcx, mir)
 +            .pass_name("redundant_clone")
 +            .iterate_to_fixpoint()
 +            .into_results_cursor(mir);
 +        let mut possible_borrower = {
 +            let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin);
 +            vis.visit_body(mir);
 +            vis.into_map(cx, maybe_storage_live_result)
 +        };
 +
 +        for (bb, bbdata) in mir.basic_blocks().iter_enumerated() {
 +            let terminator = bbdata.terminator();
 +
 +            if terminator.source_info.span.from_expansion() {
 +                continue;
 +            }
 +
 +            // Give up on loops
 +            if terminator.successors().any(|s| s == bb) {
 +                continue;
 +            }
 +
 +            let (fn_def_id, arg, arg_ty, clone_ret) =
 +                unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 +
 +            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
 +                || match_def_path(cx, fn_def_id, &paths::TO_OWNED_METHOD)
 +                || (match_def_path(cx, fn_def_id, &paths::TO_STRING_METHOD)
 +                    && is_type_diagnostic_item(cx, arg_ty, sym::String));
 +
 +            let from_deref = !from_borrow
 +                && (match_def_path(cx, fn_def_id, &paths::PATH_TO_PATH_BUF)
 +                    || match_def_path(cx, fn_def_id, &paths::OS_STR_TO_OS_STRING));
 +
 +            if !from_borrow && !from_deref {
 +                continue;
 +            }
 +
 +            if let ty::Adt(def, _) = arg_ty.kind() {
 +                if def.is_manually_drop() {
 +                    continue;
 +                }
 +            }
 +
 +            // `{ arg = &cloned; clone(move arg); }` or `{ arg = &cloned; to_path_buf(arg); }`
 +            let (cloned, cannot_move_out) = unwrap_or_continue!(find_stmt_assigns_to(cx, mir, arg, from_borrow, bb));
 +
 +            let loc = mir::Location {
 +                block: bb,
 +                statement_index: bbdata.statements.len(),
 +            };
 +
 +            // `Local` to be cloned, and a local of `clone` call's destination
 +            let (local, ret_local) = if from_borrow {
 +                // `res = clone(arg)` can be turned into `res = move arg;`
 +                // if `arg` is the only borrow of `cloned` at this point.
 +
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg], cloned, loc) {
 +                    continue;
 +                }
 +
 +                (cloned, clone_ret)
 +            } else {
 +                // `arg` is a reference as it is `.deref()`ed in the previous block.
 +                // Look into the predecessor block and find out the source of deref.
 +
 +                let ps = &mir.predecessors()[bb];
 +                if ps.len() != 1 {
 +                    continue;
 +                }
 +                let pred_terminator = mir[ps[0]].terminator();
 +
 +                // receiver of the `deref()` call
 +                let (pred_arg, deref_clone_ret) = if_chain! {
 +                    if let Some((pred_fn_def_id, pred_arg, pred_arg_ty, res)) =
 +                        is_call_with_ref_arg(cx, mir, &pred_terminator.kind);
 +                    if res == cloned;
 +                    if cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id);
 +                    if is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
 +                        || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString);
 +                    then {
 +                        (pred_arg, res)
 +                    } else {
 +                        continue;
 +                    }
 +                };
 +
 +                let (local, cannot_move_out) =
 +                    unwrap_or_continue!(find_stmt_assigns_to(cx, mir, pred_arg, true, ps[0]));
 +                let loc = mir::Location {
 +                    block: bb,
 +                    statement_index: mir.basic_blocks()[bb].statements.len(),
 +                };
 +
 +                // This can be turned into `res = move local` if `arg` and `cloned` are not borrowed
 +                // at the last statement:
 +                //
 +                // ```
 +                // pred_arg = &local;
 +                // cloned = deref(pred_arg);
 +                // arg = &cloned;
 +                // StorageDead(pred_arg);
 +                // res = to_path_buf(cloned);
 +                // ```
 +                if cannot_move_out || !possible_borrower.only_borrowers(&[arg, cloned], local, loc) {
 +                    continue;
 +                }
 +
 +                (local, deref_clone_ret)
 +            };
 +
 +            let clone_usage = if local == ret_local {
 +                CloneUsage {
 +                    cloned_used: false,
 +                    cloned_consume_or_mutate_loc: None,
 +                    clone_consumed_or_mutated: true,
 +                }
 +            } else {
 +                let clone_usage = visit_clone_usage(local, ret_local, mir, bb);
 +                if clone_usage.cloned_used && clone_usage.clone_consumed_or_mutated {
 +                    // cloned value is used, and the clone is modified or moved
 +                    continue;
 +                } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc {
 +                    // cloned value is mutated, and the clone is alive.
 +                    if possible_borrower.local_is_alive_at(ret_local, loc) {
 +                        continue;
 +                    }
 +                }
 +                clone_usage
 +            };
 +
 +            let span = terminator.source_info.span;
 +            let scope = terminator.source_info.scope;
 +            let node = mir.source_scopes[scope]
 +                .local_data
 +                .as_ref()
 +                .assert_crate_local()
 +                .lint_root;
 +
 +            if_chain! {
 +                if let Some(snip) = snippet_opt(cx, span);
 +                if let Some(dot) = snip.rfind('.');
 +                then {
 +                    let sugg_span = span.with_lo(
 +                        span.lo() + BytePos(u32::try_from(dot).unwrap())
 +                    );
 +                    let mut app = Applicability::MaybeIncorrect;
 +
 +                    let call_snip = &snip[dot + 1..];
 +                    // Machine applicable when `call_snip` looks like `foobar()`
 +                    if let Some(call_snip) = call_snip.strip_suffix("()").map(str::trim) {
 +                        if call_snip.as_bytes().iter().all(|b| b.is_ascii_alphabetic() || *b == b'_') {
 +                            app = Applicability::MachineApplicable;
 +                        }
 +                    }
 +
 +                    span_lint_hir_and_then(cx, REDUNDANT_CLONE, node, sugg_span, "redundant clone", |diag| {
 +                        diag.span_suggestion(
 +                            sugg_span,
 +                            "remove this",
 +                            String::new(),
 +                            app,
 +                        );
 +                        if clone_usage.cloned_used {
 +                            diag.span_note(
 +                                span,
 +                                "cloned value is neither consumed nor mutated",
 +                            );
 +                        } else {
 +                            diag.span_note(
 +                                span.with_hi(span.lo() + BytePos(u32::try_from(dot).unwrap())),
 +                                "this value is dropped without further use",
 +                            );
 +                        }
 +                    });
 +                } else {
 +                    span_lint_hir(cx, REDUNDANT_CLONE, node, span, "redundant clone");
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// If `kind` is `y = func(x: &T)` where `T: !Copy`, returns `(DefId of func, x, T, y)`.
 +fn is_call_with_ref_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &'tcx mir::Body<'tcx>,
 +    kind: &'tcx mir::TerminatorKind<'tcx>,
 +) -> Option<(def_id::DefId, mir::Local, Ty<'tcx>, mir::Local)> {
 +    if_chain! {
 +        if let mir::TerminatorKind::Call { func, args, destination, .. } = kind;
 +        if args.len() == 1;
 +        if let mir::Operand::Move(mir::Place { local, .. }) = &args[0];
-     match (by_ref, &*rvalue) {
++        if let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind();
++        if let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].ty(mir, cx.tcx));
 +        if !is_copy(cx, inner_ty);
 +        then {
 +            Some((def_id, *local, inner_ty, destination.as_local()?))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +type CannotMoveOut = bool;
 +
 +/// Finds the first `to = (&)from`, and returns
 +/// ``Some((from, whether `from` cannot be moved out))``.
 +fn find_stmt_assigns_to<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    to_local: mir::Local,
 +    by_ref: bool,
 +    bb: mir::BasicBlock,
 +) -> Option<(mir::Local, CannotMoveOut)> {
 +    let rvalue = mir.basic_blocks()[bb].statements.iter().rev().find_map(|stmt| {
 +        if let mir::StatementKind::Assign(box (mir::Place { local, .. }, v)) = &stmt.kind {
 +            return if *local == to_local { Some(v) } else { None };
 +        }
 +
 +        None
 +    })?;
 +
++    match (by_ref, rvalue) {
 +        (true, mir::Rvalue::Ref(_, _, place)) | (false, mir::Rvalue::Use(mir::Operand::Copy(place))) => {
 +            Some(base_local_and_movability(cx, mir, *place))
 +        },
 +        (false, mir::Rvalue::Ref(_, _, place)) => {
 +            if let [mir::ProjectionElem::Deref] = place.as_ref().projection {
 +                Some(base_local_and_movability(cx, mir, *place))
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Extracts and returns the undermost base `Local` of given `place`. Returns `place` itself
 +/// if it is already a `Local`.
 +///
 +/// Also reports whether given `place` cannot be moved out.
 +fn base_local_and_movability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mir: &mir::Body<'tcx>,
 +    place: mir::Place<'tcx>,
 +) -> (mir::Local, CannotMoveOut) {
 +    use rustc_middle::mir::PlaceRef;
 +
 +    // Dereference. You cannot move things out from a borrowed value.
 +    let mut deref = false;
 +    // Accessing a field of an ADT that has `Drop`. Moving the field out will cause E0509.
 +    let mut field = false;
 +    // If projection is a slice index then clone can be removed only if the
 +    // underlying type implements Copy
 +    let mut slice = false;
 +
 +    let PlaceRef { local, mut projection } = place.as_ref();
 +    while let [base @ .., elem] = projection {
 +        projection = base;
 +        deref |= matches!(elem, mir::ProjectionElem::Deref);
 +        field |= matches!(elem, mir::ProjectionElem::Field(..))
 +            && has_drop(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +        slice |= matches!(elem, mir::ProjectionElem::Index(..))
 +            && !is_copy(cx, mir::Place::ty_from(local, projection, &mir.local_decls, cx.tcx).ty);
 +    }
 +
 +    (local, deref || field || slice)
 +}
 +
 +#[derive(Default)]
 +struct CloneUsage {
 +    /// Whether the cloned value is used after the clone.
 +    cloned_used: bool,
 +    /// The first location where the cloned value is consumed or mutated, if any.
 +    cloned_consume_or_mutate_loc: Option<mir::Location>,
 +    /// Whether the clone value is mutated.
 +    clone_consumed_or_mutated: bool,
 +}
 +fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, bb: mir::BasicBlock) -> CloneUsage {
 +    struct V {
 +        cloned: mir::Local,
 +        clone: mir::Local,
 +        result: CloneUsage,
 +    }
 +    impl<'tcx> mir::visit::Visitor<'tcx> for V {
 +        fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
 +            let statements = &data.statements;
 +            for (statement_index, statement) in statements.iter().enumerate() {
 +                self.visit_statement(statement, mir::Location { block, statement_index });
 +            }
 +
 +            self.visit_terminator(
 +                data.terminator(),
 +                mir::Location {
 +                    block,
 +                    statement_index: statements.len(),
 +                },
 +            );
 +        }
 +
 +        fn visit_place(&mut self, place: &mir::Place<'tcx>, ctx: PlaceContext, loc: mir::Location) {
 +            let local = place.local;
 +
 +            if local == self.cloned
 +                && !matches!(
 +                    ctx,
 +                    PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
 +                )
 +            {
 +                self.result.cloned_used = true;
 +                self.result.cloned_consume_or_mutate_loc = self.result.cloned_consume_or_mutate_loc.or_else(|| {
 +                    matches!(
 +                        ctx,
 +                        PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
 +                            | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
 +                    )
 +                    .then(|| loc)
 +                });
 +            } else if local == self.clone {
 +                match ctx {
 +                    PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
 +                    | PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
 +                        self.result.clone_consumed_or_mutated = true;
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +    }
 +
 +    let init = CloneUsage {
 +        cloned_used: false,
 +        cloned_consume_or_mutate_loc: None,
 +        // Consider non-temporary clones consumed.
 +        // TODO: Actually check for mutation of non-temporaries.
 +        clone_consumed_or_mutated: mir.local_kind(clone) != mir::LocalKind::Temp,
 +    };
 +    traversal::ReversePostorder::new(mir, bb)
 +        .skip(1)
 +        .fold(init, |usage, (tbb, tdata)| {
 +            // Short-circuit
 +            if (usage.cloned_used && usage.clone_consumed_or_mutated) ||
 +                // Give up on loops
 +                tdata.terminator().successors().any(|s| s == bb)
 +            {
 +                return CloneUsage {
 +                    cloned_used: true,
 +                    clone_consumed_or_mutated: true,
 +                    ..usage
 +                };
 +            }
 +
 +            let mut v = V {
 +                cloned,
 +                clone,
 +                result: usage,
 +            };
 +            v.visit_basic_block_data(tbb, tdata);
 +            v.result
 +        })
 +}
 +
 +/// Determines liveness of each local purely based on `StorageLive`/`Dead`.
 +#[derive(Copy, Clone)]
 +struct MaybeStorageLive;
 +
 +impl<'tcx> AnalysisDomain<'tcx> for MaybeStorageLive {
 +    type Domain = BitSet<mir::Local>;
 +    const NAME: &'static str = "maybe_storage_live";
 +
 +    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
 +        // bottom = dead
 +        BitSet::new_empty(body.local_decls.len())
 +    }
 +
 +    fn initialize_start_block(&self, body: &mir::Body<'tcx>, state: &mut Self::Domain) {
 +        for arg in body.args_iter() {
 +            state.insert(arg);
 +        }
 +    }
 +}
 +
 +impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
 +    type Idx = mir::Local;
 +
 +    fn statement_effect(&self, trans: &mut impl GenKill<Self::Idx>, stmt: &mir::Statement<'tcx>, _: mir::Location) {
 +        match stmt.kind {
 +            mir::StatementKind::StorageLive(l) => trans.gen(l),
 +            mir::StatementKind::StorageDead(l) => trans.kill(l),
 +            _ => (),
 +        }
 +    }
 +
 +    fn terminator_effect(
 +        &self,
 +        _trans: &mut impl GenKill<Self::Idx>,
 +        _terminator: &mir::Terminator<'tcx>,
 +        _loc: mir::Location,
 +    ) {
 +    }
 +
 +    fn call_return_effect(
 +        &self,
 +        _trans: &mut impl GenKill<Self::Idx>,
 +        _block: mir::BasicBlock,
 +        _return_places: CallReturnPlaces<'_, 'tcx>,
 +    ) {
 +        // Nothing to do when a call returns successfully
 +    }
 +}
 +
 +/// Collects the possible borrowers of each local.
 +/// For example, `b = &a; c = &a;` will make `b` and (transitively) `c`
 +/// possible borrowers of `a`.
 +struct PossibleBorrowerVisitor<'a, 'tcx> {
 +    possible_borrower: TransitiveRelation,
 +    body: &'a mir::Body<'tcx>,
 +    cx: &'a LateContext<'tcx>,
 +    possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +}
 +
 +impl<'a, 'tcx> PossibleBorrowerVisitor<'a, 'tcx> {
 +    fn new(
 +        cx: &'a LateContext<'tcx>,
 +        body: &'a mir::Body<'tcx>,
 +        possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +    ) -> Self {
 +        Self {
 +            possible_borrower: TransitiveRelation::default(),
 +            cx,
 +            body,
 +            possible_origin,
 +        }
 +    }
 +
 +    fn into_map(
 +        self,
 +        cx: &LateContext<'tcx>,
 +        maybe_live: ResultsCursor<'tcx, 'tcx, MaybeStorageLive>,
 +    ) -> PossibleBorrowerMap<'a, 'tcx> {
 +        let mut map = FxHashMap::default();
 +        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
 +            if is_copy(cx, self.body.local_decls[row].ty) {
 +                continue;
 +            }
 +
 +            let mut borrowers = self.possible_borrower.reachable_from(row, self.body.local_decls.len());
 +            borrowers.remove(mir::Local::from_usize(0));
 +            if !borrowers.is_empty() {
 +                map.insert(row, borrowers);
 +            }
 +        }
 +
 +        let bs = BitSet::new_empty(self.body.local_decls.len());
 +        PossibleBorrowerMap {
 +            map,
 +            maybe_live,
 +            bitset: (bs.clone(), bs),
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'a, 'tcx> {
 +    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
 +        let lhs = place.local;
 +        match rvalue {
 +            mir::Rvalue::Ref(_, _, borrowed) => {
 +                self.possible_borrower.add(borrowed.local, lhs);
 +            },
 +            other => {
 +                if ContainsRegion
 +                    .visit_ty(place.ty(&self.body.local_decls, self.cx.tcx).ty)
 +                    .is_continue()
 +                {
 +                    return;
 +                }
 +                rvalue_locals(other, |rhs| {
 +                    if lhs != rhs {
 +                        self.possible_borrower.add(rhs, lhs);
 +                    }
 +                });
 +            },
 +        }
 +    }
 +
 +    fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Location) {
 +        if let mir::TerminatorKind::Call {
 +            args,
 +            destination: mir::Place { local: dest, .. },
 +            ..
 +        } = &terminator.kind
 +        {
 +            // TODO add doc
 +            // If the call returns something with lifetimes,
 +            // let's conservatively assume the returned value contains lifetime of all the arguments.
 +            // For example, given `let y: Foo<'a> = foo(x)`, `y` is considered to be a possible borrower of `x`.
 +
 +            let mut immutable_borrowers = vec![];
 +            let mut mutable_borrowers = vec![];
 +
 +            for op in args {
 +                match op {
 +                    mir::Operand::Copy(p) | mir::Operand::Move(p) => {
 +                        if let ty::Ref(_, _, Mutability::Mut) = self.body.local_decls[p.local].ty.kind() {
 +                            mutable_borrowers.push(p.local);
 +                        } else {
 +                            immutable_borrowers.push(p.local);
 +                        }
 +                    },
 +                    mir::Operand::Constant(..) => (),
 +                }
 +            }
 +
 +            let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
 +                .iter()
 +                .filter_map(|r| self.possible_origin.get(r))
 +                .flat_map(HybridBitSet::iter)
 +                .collect();
 +
 +            if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
 +                mutable_variables.push(*dest);
 +            }
 +
 +            for y in mutable_variables {
 +                for x in &immutable_borrowers {
 +                    self.possible_borrower.add(*x, y);
 +                }
 +                for x in &mutable_borrowers {
 +                    self.possible_borrower.add(*x, y);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Collect possible borrowed for every `&mut` local.
 +/// For example, `_1 = &mut _2` generate _1: {_2,...}
 +/// Known Problems: not sure all borrowed are tracked
 +struct PossibleOriginVisitor<'a, 'tcx> {
 +    possible_origin: TransitiveRelation,
 +    body: &'a mir::Body<'tcx>,
 +}
 +
 +impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
 +    fn new(body: &'a mir::Body<'tcx>) -> Self {
 +        Self {
 +            possible_origin: TransitiveRelation::default(),
 +            body,
 +        }
 +    }
 +
 +    fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
 +        let mut map = FxHashMap::default();
 +        for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
 +            if is_copy(cx, self.body.local_decls[row].ty) {
 +                continue;
 +            }
 +
 +            let mut borrowers = self.possible_origin.reachable_from(row, self.body.local_decls.len());
 +            borrowers.remove(mir::Local::from_usize(0));
 +            if !borrowers.is_empty() {
 +                map.insert(row, borrowers);
 +            }
 +        }
 +        map
 +    }
 +}
 +
 +impl<'a, 'tcx> mir::visit::Visitor<'tcx> for PossibleOriginVisitor<'a, 'tcx> {
 +    fn visit_assign(&mut self, place: &mir::Place<'tcx>, rvalue: &mir::Rvalue<'_>, _location: mir::Location) {
 +        let lhs = place.local;
 +        match rvalue {
 +            // Only consider `&mut`, which can modify origin place
 +            mir::Rvalue::Ref(_, rustc_middle::mir::BorrowKind::Mut { .. }, borrowed) |
 +            // _2: &mut _;
 +            // _3 = move _2
 +            mir::Rvalue::Use(mir::Operand::Move(borrowed))  |
 +            // _3 = move _2 as &mut _;
 +            mir::Rvalue::Cast(_, mir::Operand::Move(borrowed), _)
 +                => {
 +                self.possible_origin.add(lhs, borrowed.local);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +struct ContainsRegion;
 +
 +impl TypeVisitor<'_> for ContainsRegion {
 +    type BreakTy = ();
 +
 +    fn visit_region(&mut self, _: ty::Region<'_>) -> ControlFlow<Self::BreakTy> {
 +        ControlFlow::BREAK
 +    }
 +}
 +
 +fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
 +    use rustc_middle::mir::Rvalue::{Aggregate, BinaryOp, Cast, CheckedBinaryOp, Repeat, UnaryOp, Use};
 +
 +    let mut visit_op = |op: &mir::Operand<'_>| match op {
 +        mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
 +        mir::Operand::Constant(..) => (),
 +    };
 +
 +    match rvalue {
 +        Use(op) | Repeat(op, _) | Cast(_, op, _) | UnaryOp(_, op) => visit_op(op),
 +        Aggregate(_, ops) => ops.iter().for_each(visit_op),
 +        BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
 +            visit_op(lhs);
 +            visit_op(rhs);
 +        },
 +        _ => (),
 +    }
 +}
 +
 +/// Result of `PossibleBorrowerVisitor`.
 +struct PossibleBorrowerMap<'a, 'tcx> {
 +    /// Mapping `Local -> its possible borrowers`
 +    map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
 +    maybe_live: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
 +    // Caches to avoid allocation of `BitSet` on every query
 +    bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
 +}
 +
 +impl PossibleBorrowerMap<'_, '_> {
 +    /// Returns true if the set of borrowers of `borrowed` living at `at` matches with `borrowers`.
 +    fn only_borrowers(&mut self, borrowers: &[mir::Local], borrowed: mir::Local, at: mir::Location) -> bool {
 +        self.maybe_live.seek_after_primary_effect(at);
 +
 +        self.bitset.0.clear();
 +        let maybe_live = &mut self.maybe_live;
 +        if let Some(bitset) = self.map.get(&borrowed) {
 +            for b in bitset.iter().filter(move |b| maybe_live.contains(*b)) {
 +                self.bitset.0.insert(b);
 +            }
 +        } else {
 +            return false;
 +        }
 +
 +        self.bitset.1.clear();
 +        for b in borrowers {
 +            self.bitset.1.insert(*b);
 +        }
 +
 +        self.bitset.0 == self.bitset.1
 +    }
 +
 +    fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool {
 +        self.maybe_live.seek_after_primary_effect(at);
 +        self.maybe_live.contains(local)
 +    }
 +}
 +
 +#[derive(Default)]
 +struct TransitiveRelation {
 +    relations: FxHashMap<mir::Local, Vec<mir::Local>>,
 +}
 +impl TransitiveRelation {
 +    fn add(&mut self, a: mir::Local, b: mir::Local) {
 +        self.relations.entry(a).or_default().push(b);
 +    }
 +
 +    fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet<mir::Local> {
 +        let mut seen = HybridBitSet::new_empty(domain_size);
 +        let mut stack = vec![a];
 +        while let Some(u) = stack.pop() {
 +            if let Some(edges) = self.relations.get(&u) {
 +                for &v in edges {
 +                    if seen.insert(v) {
 +                        stack.push(v);
 +                    }
 +                }
 +            }
 +        }
 +        seen
 +    }
 +}
index 2d26c49252fa5d9db9231d70ecb6cda32c80c966,0000000000000000000000000000000000000000..0825f00f421c5b9a9762b2f66d5213bd55b506fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,120 -1,0 +1,117 @@@
-             TyKind::Array(ref ty, _) => {
-                 self.visit_type(&*ty, cx, reason);
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{meets_msrv, msrvs};
 +use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for constants and statics with an explicit `'static` lifetime.
 +    ///
 +    /// ### Why is this bad?
 +    /// Adding `'static` to every reference can create very
 +    /// complicated types.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
 +    /// &[...]
 +    /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
 +    /// &[...]
 +    /// ```
 +    /// This code can be rewritten as
 +    /// ```ignore
 +    ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
 +    ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub REDUNDANT_STATIC_LIFETIMES,
 +    style,
 +    "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
 +}
 +
 +pub struct RedundantStaticLifetimes {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl RedundantStaticLifetimes {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
 +
 +impl RedundantStaticLifetimes {
 +    // Recursively visit types
 +    fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
 +        match ty.kind {
 +            // Be careful of nested structures (arrays and tuples)
-                     self.visit_type(&*tup_ty, cx, reason);
++            TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
++                self.visit_type(ty, cx, reason);
 +            },
 +            TyKind::Tup(ref tup) => {
 +                for tup_ty in tup {
-             TyKind::Slice(ref ty) => {
-                 self.visit_type(ty, cx, reason);
-             },
++                    self.visit_type(tup_ty, cx, reason);
 +                }
 +            },
 +            // This is what we are looking for !
 +            TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
 +                // Match the 'static lifetime
 +                if let Some(lifetime) = *optional_lifetime {
 +                    match borrow_type.ty.kind {
 +                        TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
 +                            if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
 +                                let snip = snippet(cx, borrow_type.ty.span, "<type>");
 +                                let sugg = format!("&{}", snip);
 +                                span_lint_and_then(
 +                                    cx,
 +                                    REDUNDANT_STATIC_LIFETIMES,
 +                                    lifetime.ident.span,
 +                                    reason,
 +                                    |diag| {
 +                                        diag.span_suggestion(
 +                                            ty.span,
 +                                            "consider removing `'static`",
 +                                            sugg,
 +                                            Applicability::MachineApplicable, //snippet
 +                                        );
 +                                    },
 +                                );
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                }
 +                self.visit_type(&*borrow_type.ty, cx, reason);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl EarlyLintPass for RedundantStaticLifetimes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
 +            return;
 +        }
 +
 +        if !item.span.from_expansion() {
 +            if let ItemKind::Const(_, ref var_type, _) = item.kind {
 +                self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
 +                // Don't check associated consts because `'static` cannot be elided on those (issue
 +                // #2438)
 +            }
 +
 +            if let ItemKind::Static(ref var_type, _, _) = item.kind {
 +                self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
index 8068fa22d9ccf8eea0fb4801d0d9b3f92193184a,0000000000000000000000000000000000000000..e525eba53e2aa6332852ad0d91ae7b4c67869b72
mode 100644,000000..100644
--- /dev/null
@@@ -1,307 -1,0 +1,303 @@@
- use clippy_utils::source::snippet_opt;
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-             if in_external_macro(cx.tcx.sess, inner_span) || inner_span.from_expansion() {
-                 return;
-             }
++use clippy_utils::source::{snippet_opt, snippet_with_context};
 +use clippy_utils::{fn_def_id, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_ast::ast::Attribute;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::hygiene::DesugaringKind;
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let`-bindings, which are subsequently
 +    /// returned.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is just extraneous code. Remove it to make your code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo() -> String {
 +    ///     let x = String::new();
 +    ///     x
 +    /// }
 +    /// ```
 +    /// instead, use
 +    /// ```
 +    /// fn foo() -> String {
 +    ///     String::new()
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LET_AND_RETURN,
 +    style,
 +    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for return statements at the end of a block.
 +    ///
 +    /// ### Why is this bad?
 +    /// Removing the `return` and semicolon will make the code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    /// simplify to
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_RETURN,
 +    style,
 +    "using a return statement like `return expr;` where an expression would suffice"
 +}
 +
 +#[derive(PartialEq, Eq, Copy, Clone)]
 +enum RetReplacement {
 +    Empty,
 +    Block,
 +    Unit,
 +}
 +
 +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Return {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
 +        // we need both a let-binding stmt and an expr
 +        if_chain! {
 +            if let Some(retexpr) = block.expr;
 +            if let Some(stmt) = block.stmts.iter().last();
 +            if let StmtKind::Local(local) = &stmt.kind;
 +            if local.ty.is_none();
 +            if cx.tcx.hir().attrs(local.hir_id).is_empty();
 +            if let Some(initexpr) = &local.init;
 +            if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
 +            if path_to_local_id(retexpr, local_id);
 +            if !last_statement_borrows(cx, initexpr);
 +            if !in_external_macro(cx.sess(), initexpr.span);
 +            if !in_external_macro(cx.sess(), retexpr.span);
 +            if !local.span.from_expansion();
 +            then {
 +                span_lint_and_then(
 +                    cx,
 +                    LET_AND_RETURN,
 +                    retexpr.span,
 +                    "returning the result of a `let` binding from a block",
 +                    |err| {
 +                        err.span_label(local.span, "unnecessary `let` binding");
 +
 +                        if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
 +                            if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
 +                                snippet.push_str(" as _");
 +                            }
 +                            err.multipart_suggestion(
 +                                "return the expression directly",
 +                                vec![
 +                                    (local.span, String::new()),
 +                                    (retexpr.span, snippet),
 +                                ],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        } else {
 +                            err.span_help(initexpr.span, "this expression can be directly returned");
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'tcx>,
 +        body: &'tcx Body<'tcx>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        match kind {
 +            FnKind::Closure => {
 +                // when returning without value in closure, replace this `return`
 +                // with an empty block to prevent invalid suggestion (see #6501)
 +                let replacement = if let ExprKind::Ret(None) = &body.value.kind {
 +                    RetReplacement::Block
 +                } else {
 +                    RetReplacement::Empty
 +                };
 +                check_final_expr(cx, &body.value, Some(body.value.span), replacement);
 +            },
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                if let ExprKind::Block(block, _) = body.value.kind {
 +                    check_block_return(cx, block);
 +                }
 +            },
 +        }
 +    }
 +}
 +
 +fn attr_is_cfg(attr: &Attribute) -> bool {
 +    attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
 +}
 +
 +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +    if let Some(expr) = block.expr {
 +        check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
 +    } else if let Some(stmt) = block.stmts.iter().last() {
 +        match stmt.kind {
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
 +
 +fn check_final_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    match expr.kind {
 +        // simple return is always "bad"
 +        ExprKind::Ret(ref inner) => {
 +            // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
 +            let attrs = cx.tcx.hir().attrs(expr.hir_id);
 +            if !attrs.iter().any(attr_is_cfg) {
 +                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
 +                if !borrows {
 +                    emit_return_lint(
 +                        cx,
 +                        span.expect("`else return` is not possible"),
 +                        inner.as_ref().map(|i| i.span),
 +                        replacement,
 +                    );
 +                }
 +            }
 +        },
 +        // a whole block? check it!
 +        ExprKind::Block(block, _) => {
 +            check_block_return(cx, block);
 +        },
 +        ExprKind::If(_, then, else_clause_opt) => {
 +            if let ExprKind::Block(ifblock, _) = then.kind {
 +                check_block_return(cx, ifblock);
 +            }
 +            if let Some(else_clause) = else_clause_opt {
 +                if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) {
 +                    check_final_expr(cx, else_clause, None, RetReplacement::Empty);
 +                }
 +            }
 +        },
 +        // a match expr, check all arms
 +        // an if/if let expr, check both exprs
 +        // note, if without else is going to be a type checking error anyways
 +        // (except for unit type functions) so we don't match it
 +        ExprKind::Match(_, arms, MatchSource::Normal) => {
 +            for arm in arms.iter() {
 +                check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit);
 +            }
 +        },
 +        ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
 +        _ => (),
 +    }
 +}
 +
 +fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
 +    if ret_span.from_expansion() {
 +        return;
 +    }
 +    match inner_span {
 +        Some(inner_span) => {
-                 if let Some(snippet) = snippet_opt(cx, inner_span) {
-                     diag.span_suggestion(ret_span, "remove `return`", snippet, Applicability::MachineApplicable);
-                 }
++            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
-         if self.borrows {
++                let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
++                diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
 +            });
 +        },
 +        None => match replacement {
 +            RetReplacement::Empty => {
 +                span_lint_and_sugg(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    "remove `return`",
 +                    String::new(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +            RetReplacement::Block => {
 +                span_lint_and_sugg(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    "replace `return` with an empty block",
 +                    "{}".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +            RetReplacement::Unit => {
 +                span_lint_and_sugg(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    "replace `return` with a unit value",
 +                    "()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            },
 +        },
 +    }
 +}
 +
 +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    let mut visitor = BorrowVisitor { cx, borrows: false };
 +    walk_expr(&mut visitor, expr);
 +    visitor.borrows
 +}
 +
 +struct BorrowVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    borrows: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
++        if self.borrows || expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let Some(def_id) = fn_def_id(self.cx, expr) {
 +            self.borrows = self
 +                .cx
 +                .tcx
 +                .fn_sig(def_id)
 +                .output()
 +                .skip_binder()
 +                .walk()
 +                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index 2a80e6f918de2c9979a39ee0709db510a197b7ea,0000000000000000000000000000000000000000..4f74c1e44c26d54c9d6cd0a2a9587dfee623820c
mode 100644,000000..100644
--- /dev/null
@@@ -1,242 -1,0 +1,249 @@@
- use rustc_hir::{
-     Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp,
- };
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::source::snippet;
 +use clippy_utils::visitors::is_local_used;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def::Res;
 +use rustc_hir::def_id::LocalDefId;
 +use rustc_hir::hir_id::ItemLocalId;
-         if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure)
-         {
++use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Let, Node, Pat, PatKind, QPath, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, while just changing reference level or mutability.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not much, in fact it's a very common pattern in Rust
 +    /// code. Still, some may opt to avoid it in their code base, they can set this
 +    /// lint to `Warn`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// // Bad
 +    /// let x = &x;
 +    ///
 +    /// // Good
 +    /// let y = &x; // use different variable name
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHADOW_SAME,
 +    restriction,
 +    "rebinding a name to itself, e.g., `let mut x = &mut x`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, while reusing the original value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not too much, in fact it's a common pattern in Rust
 +    /// code. Still, some argue that name shadowing like this hurts readability,
 +    /// because a value may be bound to different things depending on position in
 +    /// the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 2;
 +    /// let x = x + 1;
 +    /// ```
 +    /// use different variable name:
 +    /// ```rust
 +    /// let x = 2;
 +    /// let y = x + 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHADOW_REUSE,
 +    restriction,
 +    "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, either without an initialization or with one that does not even use
 +    /// the original value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Name shadowing can hurt readability, especially in
 +    /// large code bases, because it is easy to lose track of the active binding at
 +    /// any place in the code. This can be alleviated by either giving more specific
 +    /// names to bindings or introducing more scopes to contain the bindings.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y = 1;
 +    /// # let z = 2;
 +    /// let x = y;
 +    ///
 +    /// // Bad
 +    /// let x = z; // shadows the earlier binding
 +    ///
 +    /// // Good
 +    /// let w = z; // use different variable name
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHADOW_UNRELATED,
 +    restriction,
 +    "rebinding a name without even using the original value"
 +}
 +
 +#[derive(Default)]
 +pub(crate) struct Shadow {
 +    bindings: Vec<FxHashMap<Symbol, Vec<ItemLocalId>>>,
 +}
 +
 +impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Shadow {
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        let (id, ident) = match pat.kind {
 +            PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
 +            _ => return,
 +        };
 +
 +        if pat.span.desugaring_kind().is_some() {
 +            return;
 +        }
 +
 +        if ident.span.from_expansion() || ident.span.is_dummy() {
 +            return;
 +        }
 +
 +        let HirId { owner, local_id } = id;
 +        // get (or insert) the list of items for this owner and symbol
 +        let data = self.bindings.last_mut().unwrap();
 +        let items_with_name = data.entry(ident.name).or_default();
 +
 +        // check other bindings with the same name, most recently seen first
 +        for &prev in items_with_name.iter().rev() {
 +            if prev == local_id {
 +                // repeated binding in an `Or` pattern
 +                return;
 +            }
 +
 +            if is_shadow(cx, owner, prev, local_id) {
 +                let prev_hir_id = HirId { owner, local_id: prev };
 +                lint_shadow(cx, pat, prev_hir_id, ident.span);
 +                // only lint against the "nearest" shadowed binding
 +                break;
 +            }
 +        }
 +        // store the binding
 +        items_with_name.push(local_id);
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
 +        let hir = cx.tcx.hir();
-         if !matches!(hir.body_owner_kind(hir.body_owner_def_id(body.id())), BodyOwnerKind::Closure)
-         {
++        if !matches!(
++            hir.body_owner_kind(hir.body_owner_def_id(body.id())),
++            BodyOwnerKind::Closure
++        ) {
 +            self.bindings.push(FxHashMap::default());
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
 +        let hir = cx.tcx.hir();
- fn is_shadow(
-     cx: &LateContext<'_>,
-     owner: LocalDefId,
-     first: ItemLocalId,
-     second: ItemLocalId,
- ) -> bool {
-     let scope_tree = cx.tcx.region_scope_tree(owner);
-     let first_scope = scope_tree.var_scope(first).unwrap();
-     let second_scope = scope_tree.var_scope(second).unwrap();
-     scope_tree.is_subscope_of(second_scope, first_scope)
++        if !matches!(
++            hir.body_owner_kind(hir.body_owner_def_id(body.id())),
++            BodyOwnerKind::Closure
++        ) {
 +            self.bindings.pop();
 +        }
 +    }
 +}
 +
-         }
++fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool {
++    let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id());
++    if let Some(first_scope) = scope_tree.var_scope(first) {
++        if let Some(second_scope) = scope_tree.var_scope(second) {
++            return scope_tree.is_subscope_of(second_scope, first_scope);
++        }
++    }
++
++    false
 +}
 +
 +fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) {
 +    let (lint, msg) = match find_init(cx, pat.hir_id) {
 +        Some(expr) if is_self_shadow(cx, pat, expr, shadowed) => {
 +            let msg = format!(
 +                "`{}` is shadowed by itself in `{}`",
 +                snippet(cx, pat.span, "_"),
 +                snippet(cx, expr.span, "..")
 +            );
 +            (SHADOW_SAME, msg)
-         }
++        },
 +        Some(expr) if is_local_used(cx, expr, shadowed) => {
 +            let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_"));
 +            (SHADOW_REUSE, msg)
-             let msg =
-                 format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
++        },
 +        _ => {
-         }
++            let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
 +            (SHADOW_UNRELATED, msg)
-             | ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _)
++        },
 +    };
 +    span_lint_and_note(
 +        cx,
 +        lint,
 +        span,
 +        &msg,
 +        Some(cx.tcx.hir().span(shadowed)),
 +        "previous binding is here",
 +    );
 +}
 +
 +/// Returns true if the expression is a simple transformation of a local binding such as `&x`
 +fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_id: HirId) -> bool {
 +    let hir = cx.tcx.hir();
 +    let is_direct_binding = hir
 +        .parent_iter(pat.hir_id)
 +        .map_while(|(_id, node)| match node {
 +            Node::Pat(pat) => Some(pat),
 +            _ => None,
 +        })
 +        .all(|pat| matches!(pat.kind, PatKind::Ref(..) | PatKind::Or(_)));
 +    if !is_direct_binding {
 +        return false;
 +    }
 +    loop {
 +        expr = match expr.kind {
 +            ExprKind::Box(e)
 +            | ExprKind::AddrOf(_, _, e)
++            | ExprKind::Block(
++                &Block {
++                    stmts: [],
++                    expr: Some(e),
++                    ..
++                },
++                _,
++            )
 +            | ExprKind::Unary(UnOp::Deref, e) => e,
 +            ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id),
 +            _ => break false,
 +        }
 +    }
 +}
 +
 +/// Finds the "init" expression for a pattern: `let <pat> = <init>;` (or `if let`) or
 +/// `match <init> { .., <pat> => .., .. }`
 +fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(hir_id) {
 +        let init = match node {
 +            Node::Arm(_) | Node::Pat(_) => continue,
 +            Node::Expr(expr) => match expr.kind {
 +                ExprKind::Match(e, _, _) | ExprKind::Let(&Let { init: e, .. }) => Some(e),
 +                _ => None,
 +            },
 +            Node::Local(local) => local.init,
 +            _ => None,
 +        };
 +        return init;
 +    }
 +    None
 +}
index 3d7dc49b406a6cec8f294dbfb9ef99332279c5ad,0000000000000000000000000000000000000000..bfb9f0d01e1dc88a717b3b1317a2cae03f557228
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,154 @@@
-         if METHODS.iter().any(|m| *m == &*method_ident);
 +//! Lint on use of `size_of` or `size_of_val` of T in an expression
 +//! expecting a count of T
 +
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::{match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_hir::BinOpKind;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, Ty, TypeAndMut};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects expressions where
 +    /// `size_of::<T>` or `size_of_val::<T>` is used as a
 +    /// count of elements of type `T`
 +    ///
 +    /// ### Why is this bad?
 +    /// These functions expect a count
 +    /// of `T` and not a number of bytes
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// # use std::ptr::copy_nonoverlapping;
 +    /// # use std::mem::size_of;
 +    /// const SIZE: usize = 128;
 +    /// let x = [2u8; SIZE];
 +    /// let mut y = [2u8; SIZE];
 +    /// unsafe { copy_nonoverlapping(x.as_ptr(), y.as_mut_ptr(), size_of::<u8>() * SIZE) };
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub SIZE_OF_IN_ELEMENT_COUNT,
 +    correctness,
 +    "using `size_of::<T>` or `size_of_val::<T>` where a count of elements of `T` is expected"
 +}
 +
 +declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]);
 +
 +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> {
 +    match expr.kind {
 +        ExprKind::Call(count_func, _func_args) => {
 +            if_chain! {
 +                if !inverted;
 +                if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
 +                if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
 +                if matches!(cx.tcx.get_diagnostic_name(def_id), Some(sym::mem_size_of | sym::mem_size_of_val));
 +                then {
 +                    cx.typeck_results().node_substs(count_func.hir_id).types().next()
 +                } else {
 +                    None
 +                }
 +            }
 +        },
 +        ExprKind::Binary(op, left, right) if BinOpKind::Mul == op.node => {
 +            get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, inverted))
 +        },
 +        ExprKind::Binary(op, left, right) if BinOpKind::Div == op.node => {
 +            get_size_of_ty(cx, left, inverted).or_else(|| get_size_of_ty(cx, right, !inverted))
 +        },
 +        ExprKind::Cast(expr, _) => get_size_of_ty(cx, expr, inverted),
 +        _ => None,
 +    }
 +}
 +
 +fn get_pointee_ty_and_count_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> {
 +    const FUNCTIONS: [&[&str]; 8] = [
 +        &paths::PTR_COPY_NONOVERLAPPING,
 +        &paths::PTR_COPY,
 +        &paths::PTR_WRITE_BYTES,
 +        &paths::PTR_SWAP_NONOVERLAPPING,
 +        &paths::PTR_SLICE_FROM_RAW_PARTS,
 +        &paths::PTR_SLICE_FROM_RAW_PARTS_MUT,
 +        &paths::SLICE_FROM_RAW_PARTS,
 +        &paths::SLICE_FROM_RAW_PARTS_MUT,
 +    ];
 +    const METHODS: [&str; 11] = [
 +        "write_bytes",
 +        "copy_to",
 +        "copy_from",
 +        "copy_to_nonoverlapping",
 +        "copy_from_nonoverlapping",
 +        "add",
 +        "wrapping_add",
 +        "sub",
 +        "wrapping_sub",
 +        "offset",
 +        "wrapping_offset",
 +    ];
 +
 +    if_chain! {
 +        // Find calls to ptr::{copy, copy_nonoverlapping}
 +        // and ptr::{swap_nonoverlapping, write_bytes},
 +        if let ExprKind::Call(func, [.., count]) = expr.kind;
 +        if let ExprKind::Path(ref func_qpath) = func.kind;
 +        if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
 +        if FUNCTIONS.iter().any(|func_path| match_def_path(cx, def_id, func_path));
 +
 +        // Get the pointee type
 +        if let Some(pointee_ty) = cx.typeck_results().node_substs(func.hir_id).types().next();
 +        then {
 +            return Some((pointee_ty, count));
 +        }
 +    };
 +    if_chain! {
 +        // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods
 +        if let ExprKind::MethodCall(method_path, [ptr_self, .., count], _) = expr.kind;
 +        let method_ident = method_path.ident.as_str();
++        if METHODS.iter().any(|m| *m == method_ident);
 +
 +        // Get the pointee type
 +        if let ty::RawPtr(TypeAndMut { ty: pointee_ty, .. }) =
 +            cx.typeck_results().expr_ty(ptr_self).kind();
 +        then {
 +            return Some((*pointee_ty, count));
 +        }
 +    };
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        const HELP_MSG: &str = "use a count of elements instead of a count of bytes\
 +            , it already gets multiplied by the size of the type";
 +
 +        const LINT_MSG: &str = "found a count of bytes \
 +             instead of a count of elements of `T`";
 +
 +        if_chain! {
 +            // Find calls to functions with an element count parameter and get
 +            // the pointee type and count parameter expression
 +            if let Some((pointee_ty, count_expr)) = get_pointee_ty_and_count_expr(cx, expr);
 +
 +            // Find a size_of call in the count parameter expression and
 +            // check that it's the same type
 +            if let Some(ty_used_for_size_of) = get_size_of_ty(cx, count_expr, false);
 +            if pointee_ty == ty_used_for_size_of;
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    SIZE_OF_IN_ELEMENT_COUNT,
 +                    count_expr.span,
 +                    LINT_MSG,
 +                    None,
 +                    HELP_MSG
 +                );
 +            }
 +        };
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..75d3b040c968f9b3f47889d9a164203e4357a1b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,80 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::source::snippet_with_context;
++use clippy_utils::{match_def_path, path_def_id, paths};
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{Span, SyntaxContext};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for calls to `core::mem::swap` where either parameter is derived from a pointer
++    ///
++    /// ### Why is this bad?
++    /// When at least one parameter to `swap` is derived from a pointer it may overlap with the
++    /// other. This would then lead to undefined behavior.
++    ///
++    /// ### Example
++    /// ```rust
++    /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
++    ///     for (&x, &y) in x.iter().zip(y) {
++    ///         core::mem::swap(&mut *x, &mut *y);
++    ///     }
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// unsafe fn swap(x: &[*mut u32], y: &[*mut u32]) {
++    ///     for (&x, &y) in x.iter().zip(y) {
++    ///         core::ptr::swap(x, y);
++    ///     }
++    /// }
++    /// ```
++    #[clippy::version = "1.63.0"]
++    pub SWAP_PTR_TO_REF,
++    suspicious,
++    "call to `mem::swap` using pointer derived references"
++}
++declare_lint_pass!(SwapPtrToRef => [SWAP_PTR_TO_REF]);
++
++impl LateLintPass<'_> for SwapPtrToRef {
++    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
++        if let ExprKind::Call(fn_expr, [arg1, arg2]) = e.kind
++            && let Some(fn_id) = path_def_id(cx, fn_expr)
++            && match_def_path(cx, fn_id, &paths::MEM_SWAP)
++            && let ctxt = e.span.ctxt()
++            && let (from_ptr1, arg1_span) = is_ptr_to_ref(cx, arg1, ctxt)
++            && let (from_ptr2, arg2_span) = is_ptr_to_ref(cx, arg2, ctxt)
++            && (from_ptr1 || from_ptr2)
++        {
++            span_lint_and_then(
++                cx,
++                SWAP_PTR_TO_REF,
++                e.span,
++                "call to `core::mem::swap` with a parameter derived from a raw pointer",
++                |diag| {
++                    if !((from_ptr1 && arg1_span.is_none()) || (from_ptr2 && arg2_span.is_none())) {
++                        let mut app = Applicability::MachineApplicable;
++                        let snip1 = snippet_with_context(cx, arg1_span.unwrap_or(arg1.span), ctxt, "..", &mut app).0;
++                        let snip2 = snippet_with_context(cx, arg2_span.unwrap_or(arg2.span), ctxt, "..", &mut app).0;
++                        diag.span_suggestion(e.span, "use ptr::swap", format!("core::ptr::swap({}, {})", snip1, snip2), app);
++                    }
++                }
++            );
++        }
++    }
++}
++
++/// Checks if the expression converts a mutable pointer to a mutable reference. If it is, also
++/// returns the span of the pointer expression if it's suitable for making a suggestion.
++fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bool, Option<Span>) {
++    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, borrowed_expr) = e.kind
++        && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
++        && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
++    {
++        (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span))
++    } else {
++        (false, None)
++    }
++}
index 911da3997ae451160af7106915e89e3a1243c093,0000000000000000000000000000000000000000..1e5b646f5f055b4404c4b29411fc1fde3488bf9f
mode 100644,000000..100644
--- /dev/null
@@@ -1,263 -1,0 +1,263 @@@
-     /// Could be written as:
-     ///
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::{SpanlessEq, SpanlessHash};
 +use core::hash::{Hash, Hasher};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{
 +    GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind,
 +    WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +use std::fmt::Write as _;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about unnecessary type repetitions in trait bounds
 +    ///
 +    /// ### Why is this bad?
 +    /// Repeating the type for every bound makes the code
 +    /// less readable than combining the bounds
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    ///
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
 +    /// ```
 +    #[clippy::version = "1.38.0"]
 +    pub TYPE_REPETITION_IN_BOUNDS,
 +    pedantic,
 +    "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases where generics are being used and multiple
 +    /// syntax specifications for trait bounds are used simultaneously.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate bounds makes the code
 +    /// less readable than specifying them only once.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    ///
-     /// ```
-     /// or
++    /// Use instead:
 +    /// ```rust
++    /// # mod hidden {
 +    /// fn func<T: Clone + Default>(arg: T) {}
-     /// ```rust
++    /// # }
++    ///
++    /// // or
 +    ///
 +    /// fn func<T>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRAIT_DUPLICATION_IN_BOUNDS,
 +    pedantic,
 +    "Check if the same trait bounds are specified twice during a function declaration"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct TraitBounds {
 +    max_trait_bounds: u64,
 +}
 +
 +impl TraitBounds {
 +    #[must_use]
 +    pub fn new(max_trait_bounds: u64) -> Self {
 +        Self { max_trait_bounds }
 +    }
 +}
 +
 +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 +    fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        self.check_type_repetition(cx, gen);
 +        check_trait_bound_duplication(cx, gen);
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
 +        let mut self_bounds_map = FxHashMap::default();
 +
 +        for predicate in item.generics.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +                if bound_predicate.origin != PredicateOrigin::ImplTrait;
 +                if !bound_predicate.span.from_expansion();
 +                if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
 +                if let Some(PathSegment {
 +                    res: Some(Res::SelfTy{ trait_: Some(def_id), alias_to: _ }), ..
 +                }) = segments.first();
 +                if let Some(
 +                    Node::Item(
 +                        Item {
 +                            kind: ItemKind::Trait(_, _, _, self_bounds, _),
 +                            .. }
 +                        )
 +                    ) = cx.tcx.hir().get_if_local(*def_id);
 +                then {
 +                    if self_bounds_map.is_empty() {
 +                        for bound in self_bounds.iter() {
 +                            let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue };
 +                            self_bounds_map.insert(self_res, self_segments);
 +                        }
 +                    }
 +
 +                    bound_predicate
 +                        .bounds
 +                        .iter()
 +                        .filter_map(get_trait_info_from_bound)
 +                        .for_each(|(trait_item_res, trait_item_segments, span)| {
 +                            if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
 +                                if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
 +                                    span_lint_and_help(
 +                                        cx,
 +                                        TRAIT_DUPLICATION_IN_BOUNDS,
 +                                        span,
 +                                        "this trait bound is already specified in trait declaration",
 +                                        None,
 +                                        "consider removing this trait bound",
 +                                    );
 +                                }
 +                            }
 +                        });
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl TraitBounds {
 +    fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        struct SpanlessTy<'cx, 'tcx> {
 +            ty: &'tcx Ty<'tcx>,
 +            cx: &'cx LateContext<'tcx>,
 +        }
 +        impl PartialEq for SpanlessTy<'_, '_> {
 +            fn eq(&self, other: &Self) -> bool {
 +                let mut eq = SpanlessEq::new(self.cx);
 +                eq.inter_expr().eq_ty(self.ty, other.ty)
 +            }
 +        }
 +        impl Hash for SpanlessTy<'_, '_> {
 +            fn hash<H: Hasher>(&self, h: &mut H) {
 +                let mut t = SpanlessHash::new(self.cx);
 +                t.hash_ty(self.ty);
 +                h.write_u64(t.finish());
 +            }
 +        }
 +        impl Eq for SpanlessTy<'_, '_> {}
 +
 +        if gen.span.from_expansion() {
 +            return;
 +        }
 +        let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
 +        let mut applicability = Applicability::MaybeIncorrect;
 +        for bound in gen.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref p) = bound;
 +                if p.origin != PredicateOrigin::ImplTrait;
 +                if p.bounds.len() as u64 <= self.max_trait_bounds;
 +                if !p.span.from_expansion();
 +                if let Some(ref v) = map.insert(
 +                    SpanlessTy { ty: p.bounded_ty, cx },
 +                    p.bounds.iter().collect::<Vec<_>>()
 +                );
 +
 +                then {
 +                    let mut hint_string = format!(
 +                        "consider combining the bounds: `{}:",
 +                        snippet(cx, p.bounded_ty.span, "_")
 +                    );
 +                    for b in v.iter() {
 +                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
 +                            let path = &poly_trait_ref.trait_ref.path;
 +                            let _ = write!(hint_string,
 +                                " {} +",
 +                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
 +                            );
 +                        }
 +                    }
 +                    for b in p.bounds.iter() {
 +                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
 +                            let path = &poly_trait_ref.trait_ref.path;
 +                            let _ = write!(hint_string,
 +                                " {} +",
 +                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
 +                            );
 +                        }
 +                    }
 +                    hint_string.truncate(hint_string.len() - 2);
 +                    hint_string.push('`');
 +                    span_lint_and_help(
 +                        cx,
 +                        TYPE_REPETITION_IN_BOUNDS,
 +                        p.span,
 +                        "this type has already been used as a bound predicate",
 +                        None,
 +                        &hint_string,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
 +    if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
 +        return;
 +    }
 +
 +    let mut map = FxHashMap::<_, Vec<_>>::default();
 +    for predicate in gen.predicates {
 +        if_chain! {
 +            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +            if bound_predicate.origin != PredicateOrigin::ImplTrait;
 +            if !bound_predicate.span.from_expansion();
 +            if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
 +            if let Some(segment) = segments.first();
 +            then {
 +                for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
 +                    let trait_resolutions_direct = map.entry(segment.ident).or_default();
 +                    if let Some((_, span_direct)) = trait_resolutions_direct
 +                                                .iter()
 +                                                .find(|(res_direct, _)| *res_direct == res_where) {
 +                        span_lint_and_help(
 +                            cx,
 +                            TRAIT_DUPLICATION_IN_BOUNDS,
 +                            *span_direct,
 +                            "this trait bound is already specified in the where clause",
 +                            None,
 +                            "consider removing this trait bound",
 +                        );
 +                    }
 +                    else {
 +                        trait_resolutions_direct.push((res_where, span_where));
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
 +    if let GenericBound::Trait(t, _) = bound {
 +        Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span))
 +    } else {
 +        None
 +    }
 +}
index d2a040beb0cf70e67c866b24a8dab247043b8b04,0000000000000000000000000000000000000000..cbe1406728bc7d61dd6c87b0d216cdaae85e5221
mode 100644,000000..100644
--- /dev/null
@@@ -1,449 -1,0 +1,449 @@@
-     nursery,
 +mod crosspointer_transmute;
 +mod transmute_float_to_int;
 +mod transmute_int_to_bool;
 +mod transmute_int_to_char;
 +mod transmute_int_to_float;
 +mod transmute_num_to_bytes;
 +mod transmute_ptr_to_ptr;
 +mod transmute_ptr_to_ref;
 +mod transmute_ref_to_ref;
 +mod transmute_undefined_repr;
 +mod transmutes_expressible_as_ptr_casts;
 +mod unsound_collection_transmute;
 +mod useless_transmute;
 +mod utils;
 +mod wrong_transmute;
 +
 +use clippy_utils::in_constant;
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind};
 +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 for transmutes that can't ever be correct on any
 +    /// architecture.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's basically guaranteed to be undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// When accessing C, users might want to store pointer
 +    /// sized objects in `extradata` arguments to save an allocation.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let ptr: *const T = core::intrinsics::transmute('x')
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_TRANSMUTE,
 +    correctness,
 +    "transmutes that are confusing at best, undefined behavior at worst and always useless"
 +}
 +
 +// FIXME: Move this to `complexity` again, after #5343 is fixed
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes to the original type of the object
 +    /// and transmutes that could be a cast.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. The code tricks people into thinking that
 +    /// something complex is going on.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_TRANSMUTE,
++    complexity,
 +    "transmutes that have the same to and from types or could be a cast/coercion"
 +}
 +
 +// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///Checks for transmutes that could be a pointer cast.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. The code tricks people into thinking that
 +    /// something complex is going on.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// # let p: *const [i32] = &[];
 +    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let p: *const [i32] = &[];
 +    /// p as *const [u16];
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    complexity,
 +    "transmutes that could be a pointer cast"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between a type `T` and `*T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easy to mistakenly transmute between a type and a
 +    /// pointer to that type.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// core::intrinsics::transmute(t) // where the result type is the same as
 +    ///                                // `*t` or `&t`'s
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CROSSPOINTER_TRANSMUTE,
 +    complexity,
 +    "transmutes that have to or from types that are a pointer to the other"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a pointer to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// This can always be rewritten with `&` and `*`.
 +    ///
 +    /// ### Known problems
 +    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
 +    /// while dereferencing raw pointer is not stable yet.
 +    /// If you need to do this in those places,
 +    /// you would have to use `transmute` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// unsafe {
 +    ///     let _: &T = std::mem::transmute(p); // where p: *const T
 +    /// }
 +    ///
 +    /// // can be written:
 +    /// let _: &T = &*p;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_PTR_TO_REF,
 +    complexity,
 +    "transmutes from a pointer to a reference type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a `char`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not every integer is a Unicode scalar value.
 +    ///
 +    /// ### Known problems
 +    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
 +    /// as it needs to validate the input.
 +    /// If you are certain that the input is always a valid Unicode scalar value,
 +    /// use [`from_u32_unchecked`] which is as fast as `transmute`
 +    /// but has a semantically meaningful name.
 +    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
 +    ///
 +    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
 +    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1_u32;
 +    /// unsafe {
 +    ///     let _: char = std::mem::transmute(x); // where x: u32
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _ = std::char::from_u32(x).unwrap();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_CHAR,
 +    complexity,
 +    "transmutes from an integer to a `char`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a `&[u8]` to a `&str`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not every byte slice is a valid UTF-8 string.
 +    ///
 +    /// ### Known problems
 +    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
 +    /// as it needs to validate the input.
 +    /// If you are certain that the input is always a valid UTF-8,
 +    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
 +    /// but has a semantically meaningful name.
 +    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
 +    ///
 +    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
 +    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let b: &[u8] = &[1_u8, 2_u8];
 +    /// unsafe {
 +    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _ = std::str::from_utf8(b).unwrap();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_BYTES_TO_STR,
 +    complexity,
 +    "transmutes from a `&[u8]` to a `&str`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a `bool`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This might result in an invalid in-memory representation of a `bool`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1_u8;
 +    /// unsafe {
 +    ///     let _: bool = std::mem::transmute(x); // where x: u8
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: bool = x != 0;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_BOOL,
 +    complexity,
 +    "transmutes from an integer to a `bool`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a float.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
 +    /// and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: f32 = f32::from_bits(1_u32);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_FLOAT,
 +    complexity,
 +    "transmutes from an integer to a float"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a float to an integer.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
 +    /// and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let _: u32 = std::mem::transmute(1f32);
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: u32 = 1f32.to_bits();
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub TRANSMUTE_FLOAT_TO_INT,
 +    complexity,
 +    "transmutes from a float to an integer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a number to an array of `u8`
 +    ///
 +    /// ### Why this is bad?
 +    /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
 +    /// is intuitive and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let x: [u8; 8] = std::mem::transmute(1i64);
 +    /// }
 +    ///
 +    /// // should be
 +    /// let x: [u8; 8] = 0i64.to_ne_bytes();
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TRANSMUTE_NUM_TO_BYTES,
 +    complexity,
 +    "transmutes from a number to an array of `u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a pointer to a pointer, or
 +    /// from a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous, and these can instead be
 +    /// written as casts.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr = &1u32 as *const u32;
 +    /// unsafe {
 +    ///     // pointer-to-pointer transmute
 +    ///     let _: *const f32 = std::mem::transmute(ptr);
 +    ///     // ref-ref transmute
 +    ///     let _: &f32 = std::mem::transmute(&1u32);
 +    /// }
 +    /// // These can be respectively written:
 +    /// let _ = ptr as *const f32;
 +    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_PTR_TO_PTR,
 +    pedantic,
 +    "transmutes from a pointer to a pointer / a reference to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between collections whose
 +    /// types have different ABI, size or alignment.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// Currently, we cannot know whether a type is a
 +    /// collection, so we just lint the ones that come with `std`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // different size, therefore likely out-of-bounds memory access
 +    /// // You absolutely do not want this in your code!
 +    /// unsafe {
 +    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
 +    /// };
 +    /// ```
 +    ///
 +    /// You must always iterate, map and collect the values:
 +    ///
 +    /// ```rust
 +    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub UNSOUND_COLLECTION_TRANSMUTE,
 +    correctness,
 +    "transmute between collections of layout-incompatible types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between types which do not have a representation defined relative to
 +    /// each other.
 +    ///
 +    /// ### Why is this bad?
 +    /// The results of such a transmute are not defined.
 +    ///
 +    /// ### Known problems
 +    /// This lint has had multiple problems in the past and was moved to `nursery`. See issue
 +    /// [#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo<T>(u32, T);
 +    /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[repr(C)]
 +    /// struct Foo<T>(u32, T);
 +    /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub TRANSMUTE_UNDEFINED_REPR,
 +    nursery,
 +    "transmute to or from a type with an undefined representation"
 +}
 +
 +declare_lint_pass!(Transmute => [
 +    CROSSPOINTER_TRANSMUTE,
 +    TRANSMUTE_PTR_TO_REF,
 +    TRANSMUTE_PTR_TO_PTR,
 +    USELESS_TRANSMUTE,
 +    WRONG_TRANSMUTE,
 +    TRANSMUTE_INT_TO_CHAR,
 +    TRANSMUTE_BYTES_TO_STR,
 +    TRANSMUTE_INT_TO_BOOL,
 +    TRANSMUTE_INT_TO_FLOAT,
 +    TRANSMUTE_FLOAT_TO_INT,
 +    TRANSMUTE_NUM_TO_BYTES,
 +    UNSOUND_COLLECTION_TRANSMUTE,
 +    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    TRANSMUTE_UNDEFINED_REPR,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Transmute {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(path_expr, [arg]) = e.kind;
 +            if let ExprKind::Path(ref qpath) = path_expr.kind;
 +            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
 +            if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
 +            then {
 +                // Avoid suggesting non-const operations in const contexts:
 +                // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
 +                // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
 +                // - char conversions (https://github.com/rust-lang/rust/issues/89259)
 +                let const_context = in_constant(cx, e.hir_id);
 +
 +                let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
 +                // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
 +                let to_ty = cx.typeck_results().expr_ty(e);
 +
 +                // If useless_transmute is triggered, the other lints can be skipped.
 +                if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
 +                    return;
 +                }
 +
 +                let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
 +                    | crosspointer_transmute::check(cx, e, from_ty, to_ty)
 +                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
 +                    | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
 +                    | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
 +                    | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | (
 +                        unsound_collection_transmute::check(cx, e, from_ty, to_ty)
 +                        || transmute_undefined_repr::check(cx, e, from_ty, to_ty)
 +                    );
 +
 +                if !linted {
 +                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
 +                }
 +            }
 +        }
 +    }
 +}
index 3cc3d40a143dc1c96ca8d535e866968187b0eb48,0000000000000000000000000000000000000000..a0d104e239042e6ce4b091a1c7b9e16a9151f359
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,77 @@@
- use rustc_middle::ty::{self, Ty};
 +use super::USELESS_TRANSMUTE;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 +use clippy_utils::sugg;
 +use rustc_errors::Applicability;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
-         _ if from_ty == to_ty => {
++use rustc_middle::ty::{self, Ty, TypeFoldable};
 +
 +/// Checks for `useless_transmute` lint.
 +/// Returns `true` if it's triggered, otherwise returns `false`.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +    arg: &'tcx Expr<'_>,
 +) -> bool {
 +    match (&from_ty.kind(), &to_ty.kind()) {
-             span_lint_and_then(
-                 cx,
-                 USELESS_TRANSMUTE,
-                 e.span,
-                 "transmute from a reference to a pointer",
-                 |diag| {
-                     if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
-                         let rty_and_mut = ty::TypeAndMut {
-                             ty: *rty,
-                             mutbl: *rty_mutbl,
-                         };
++        _ if from_ty == to_ty && !from_ty.has_erased_regions() => {
 +            span_lint(
 +                cx,
 +                USELESS_TRANSMUTE,
 +                e.span,
 +                &format!("transmute from a type (`{}`) to itself", from_ty),
 +            );
 +            true
 +        },
 +        (ty::Ref(_, rty, rty_mutbl), ty::RawPtr(ptr_ty)) => {
-                         let sugg = if *ptr_ty == rty_and_mut {
-                             arg.as_ty(to_ty)
-                         } else {
-                             arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
-                         };
++            // No way to give the correct suggestion here. Avoid linting for now.
++            if !rty.has_erased_regions() {
++                span_lint_and_then(
++                    cx,
++                    USELESS_TRANSMUTE,
++                    e.span,
++                    "transmute from a reference to a pointer",
++                    |diag| {
++                        if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
++                            let rty_and_mut = ty::TypeAndMut {
++                                ty: *rty,
++                                mutbl: *rty_mutbl,
++                            };
 +
-                         diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
-                     }
-                 },
-             );
++                            let sugg = if *ptr_ty == rty_and_mut {
++                                arg.as_ty(to_ty)
++                            } else {
++                                arg.as_ty(cx.tcx.mk_ptr(rty_and_mut)).as_ty(to_ty)
++                            };
 +
++                            diag.span_suggestion(e.span, "try", sugg.to_string(), Applicability::Unspecified);
++                        }
++                    },
++                );
++            }
 +            true
 +        },
 +        (ty::Int(_) | ty::Uint(_), ty::RawPtr(_)) => {
 +            span_lint_and_then(
 +                cx,
 +                USELESS_TRANSMUTE,
 +                e.span,
 +                "transmute from an integer to a pointer",
 +                |diag| {
 +                    if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
 +                        diag.span_suggestion(
 +                            e.span,
 +                            "try",
 +                            arg.as_ty(&to_ty.to_string()).to_string(),
 +                            Applicability::Unspecified,
 +                        );
 +                    }
 +                },
 +            );
 +            true
 +        },
 +        _ => false,
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..306afe441484755d025289dee2a7992329ab2d9d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,69 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind};
++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.62.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)> {
++    if let ExprKind::MethodCall(name_ident, args, _) = &expr.kind
++        && let method_name = name_ident.ident.name.as_str()
++        && (method_name == "ceil" || method_name == "round" || method_name == "floor")
++        && !args.is_empty()
++        && let ExprKind::Lit(spanned) = &args[0].kind
++        && let LitKind::Float(symbol, ty) = spanned.kind {
++            let f = symbol.as_str().parse::<f64>().unwrap();
++            let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty {
++                ty.name_str()
++            } else {
++                ""
++            };
++            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 with a whole number float", method_name),
++                &format!("remove the `{}` method call", method_name),
++                float,
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
index 66f7748e9e0898bef526b132360aed58781ed396,0000000000000000000000000000000000000000..486ea5e5ccfa90c54386b6e7da4971136f9717fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,312 -1,0 +1,320 @@@
-             if let PatKind::Path(QPath::Resolved(_, path)) = pat.kind;
-             if !matches!(path.res, Res::SelfTy { .. } | Res::Def(DefKind::TyParam, _));
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::ty::same_type_and_consts;
 +use clippy_utils::{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_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +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
 +    /// - False positive with associated types in traits (#4140)
 +    ///
 +    /// ### 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<'_>, item: &Item<'_>) {
 +        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(_)))
 +            });
 +            then {
 +                StackItem::Check {
 +                    impl_id: item.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.def_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::SelfTy { .. } | 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));
 +            let hir = cx.tcx.hir();
 +            // prevents false positive on `#[derive(serde::Deserialize)]`
 +            if !hir.span(hir.get_parent_node(hir_ty.hir_id)).in_derive_expansion();
 +            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 {
 +            ExprKind::Struct(QPath::Resolved(_, path), ..) => match path.res {
 +                Res::SelfTy { .. } => (),
 +                Res::Def(DefKind::Variant, _) => lint_path_to_variant(cx, path),
 +                _ => span_lint(cx, path.span),
 +            },
 +            // tuple struct instantiation (`Foo(arg)` or `Enum::Foo(arg)`)
 +            ExprKind::Call(fun, _) => {
 +                if let ExprKind::Path(QPath::Resolved(_, path)) = fun.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),
 +                        }
 +                    }
 +                }
 +            },
 +            // unit enum variants (`Enum::A`)
 +            ExprKind::Path(QPath::Resolved(_, path)) => lint_path_to_variant(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();
-             if let [first, ..] = path.segments;
-             if let Some(hir_id) = first.hir_id;
++            // 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);
-                 span_lint(cx, cx.tcx.hir().span(hir_id));
 +            then {
++                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),
++                    _ => ()
++                }
 +            }
 +        }
 +    }
 +
 +    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 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 cd4d16fe95f75822965c9091d7b1999a4aeaeceb,0000000000000000000000000000000000000000..b5c5d35135f90e2ec74a139ecb6cf70d59710842
mode 100644,000000..100644
--- /dev/null
@@@ -1,500 -1,0 +1,504 @@@
 +//! 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};
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedMethod {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +impl DisallowedMethod {
 +    pub fn path(&self) -> &str {
 +        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
 +
 +        path
 +    }
 +}
 +
 +/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedType {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<Box<dyn Error>>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error + 'static) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![Box::new(error)],
 +        }
 +    }
 +}
 +
 +#[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: String) -> Box<dyn Error> {
 +    Box::new(ConfError(s))
 +}
 +
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal)])?
 +        ($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 $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 => {
 +                            $(errors.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),
 +                                }
 +                            }
 +                        })*
 +                        // 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 })
 +            }
 +        }
 +
 +        #[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: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// Lint: BLACKLISTED_NAME.
 +    ///
 +    /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
 +    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
 +    /// 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")]
 +    (cyclomatic_complexity_threshold: Option<u64> = None),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks
 +    (doc_valid_idents: Vec<String> = [
 +        "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",
 +    ].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_METHODS.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPES.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
 +    (disallowed_types: Vec<crate::utils::conf::DisallowedType> = 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: _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> = ["Latin"].iter().map(ToString::to_string).collect()),
 +    /// 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::DisallowedType> = Vec::new()),
 +    /// Lint: LARGE_INCLUDE_FILE.
 +    ///
 +    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
 +    (max_include_file_size: u64 = 1_000_000),
 +    /// Lint: EXPECT_USED.
 +    ///
 +    /// Whether `expect` should be allowed in test functions
 +    (allow_expect_in_tests: bool = false),
 +    /// Lint: UNWRAP_USED.
 +    ///
 +    /// Whether `unwrap` should be allowed in test functions
 +    (allow_unwrap_in_tests: bool = false),
++    /// Lint: DBG_MACRO.
++    ///
++    /// Whether `dbg!` should be allowed in test functions
++    (allow_dbg_in_tests: bool = false),
 +}
 +
 +/// Search for the configuration 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,
 +    };
 +    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 +}
 +
 +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 {
 +                write!(msg, "\n").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_width$}",
 +                        " ",
 +                        field,
 +                        separator_width = SEPARATOR_WIDTH,
 +                        field_width = 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 0e8f40e92101a053ab1d5210b761838ca2447b87,0000000000000000000000000000000000000000..60f9887699498c2482f3f21c8284c40d4a6b7ed8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1377 -1,0 +1,1377 @@@
-                 &*cx.tcx.item_name(macro_call.def_id).as_str(),
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
 +    method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 +use rustc_ast::visit::FnKind;
 +use rustc_data_structures::fx::{FxHashMap, 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::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{
 +    BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
 +    UnOp,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "internal")]
 +pub mod metadata_collector;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for various things we like to keep tidy in clippy.
 +    ///
 +    /// ### Why is this bad?
 +    /// We like to pretend we're an example of tidy code.
 +    ///
 +    /// ### Example
 +    /// Wrong ordering of the util::paths constants.
 +    pub CLIPPY_LINTS_INTERNAL,
 +    internal,
 +    "various things that will negatively affect your clippy experience"
 +}
 +
 +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 calls to `cx.span_lint*` and suggests to use the `utils::*`
 +    /// variant of the function.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `utils::*` variants also add a link to the Clippy documentation to the
 +    /// warning/error messages.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// cx.span_lint(LINT_NAME, "message");
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// utils::span_lint(cx, LINT_NAME, "message");
 +    /// ```
 +    pub COMPILER_LINT_FUNCTIONS,
 +    internal,
 +    "usage of the lint functions of the compiler instead of the utils::* variant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.outer().expn_data()` and suggests to use
 +    /// the `cx.outer_expn_data()`
 +    ///
 +    /// ### Why is this bad?
 +    /// `cx.outer_expn_data()` is faster and more concise.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer().expn_data()
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer_expn_data()
 +    /// ```
 +    pub OUTER_EXPN_EXPN_DATA,
 +    internal,
 +    "using `cx.outer_expn().expn_data()` instead of `cx.outer_expn_data()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Not an actual lint. This lint is only meant for testing our customized internal compiler
 +    /// error message by calling `panic`.
 +    ///
 +    /// ### Why is this bad?
 +    /// ICE in large quantities can damage your teeth
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// 🍦🍦🍦🍦🍦
 +    /// ```
 +    pub PRODUCE_ICE,
 +    internal,
 +    "this message should not appear anywhere as we ICE before and don't emit the lint"
 +}
 +
 +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
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```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
 +    /// Lints `span_lint_and_then` function calls, where the
 +    /// closure argument has only one statement and that statement is a method
 +    /// call to `span_suggestion`, `span_help`, `span_note` (using the same
 +    /// span), `help` or `note`.
 +    ///
 +    /// These usages of `span_lint_and_then` should be replaced with one of the
 +    /// wrapper functions `span_lint_and_sugg`, span_lint_and_help`, or
 +    /// `span_lint_and_note`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the wrapper `span_lint_and_*` functions, is more
 +    /// convenient, readable and less error prone.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_suggestion(
 +    ///         expr.span,
 +    ///         help_msg,
 +    ///         sugg.to_string(),
 +    ///         Applicability::MachineApplicable,
 +    ///     );
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_help(expr.span, help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.help(help_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.span_note(expr.span, note_msg);
 +    /// });
 +    /// span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |diag| {
 +    ///     diag.note(note_msg);
 +    /// });
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     TEST_LINT,
 +    ///     expr.span,
 +    ///     lint_msg,
 +    ///     help_msg,
 +    ///     sugg.to_string(),
 +    ///     Applicability::MachineApplicable,
 +    /// );
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), help_msg);
 +    /// span_lint_and_help(cx, TEST_LINT, expr.span, lint_msg, None, help_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, Some(expr.span), note_msg);
 +    /// span_lint_and_note(cx, TEST_LINT, expr.span, lint_msg, None, note_msg);
 +    /// ```
 +    pub COLLAPSIBLE_SPAN_LINT_CALLS,
 +    internal,
 +    "found collapsible `span_lint_and_then` calls"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `utils::match_type()` on a type diagnostic item
 +    /// and suggests to use `utils::is_type_diagnostic_item()` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `utils::is_type_diagnostic_item()` does not require hardcoded paths.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
 +    /// ```
 +    pub MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    internal,
 +    "using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
 +}
 +
 +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_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
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```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
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +declare_clippy_lint! {
 +    /// Finds unidiomatic usage of `if_chain!`
 +    pub IF_CHAIN_STYLE,
 +    internal,
 +    "non-idiomatic `if_chain!` usage"
 +}
 +
 +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
 +    /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
 +    ///
 +    pub MISSING_MSRV_ATTR_IMPL,
 +    internal,
 +    "checking if all necessary steps were taken when adding a MSRV to a lint"
 +}
 +
 +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 +
 +impl EarlyLintPass for ClippyLintsInternal {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
 +        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
 +            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
 +                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
 +                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
 +                        let mut last_name: Option<&str> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(last_name) = last_name {
 +                                if *last_name > *name {
 +                                    span_lint(
 +                                        cx,
 +                                        CLIPPY_LINTS_INTERNAL,
 +                                        item.span,
 +                                        "this constant should be before the previous constant due to lexical \
 +                                         ordering",
 +                                    );
 +                                }
 +                            }
 +                            last_name = Some(name);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[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]);
 +
 +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()) {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            if is_lint_ref_type(cx, ty) {
 +                check_invalid_clippy_version_attribute(cx, item);
 +
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                if_chain! {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
 +                    if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
 +                    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;
 +                    if sym.as_str() == "default lint description";
 +
 +                    then {
 +                        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 let Some(macro_call) = root_macro_call_first_node(cx, item) {
 +            if !matches!(
-         if RustcVersion::parse(&*value.as_str()).is_err() {
++                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(
 +                    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 `{}` is not added to any `LintPass`", lint_name),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &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 let Some(sugg) = self.map.get(&*fn_name.as_str());
++        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 sematic 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
 +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.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()
 +    }
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct CompilerLintFunctions {
 +    map: FxHashMap<&'static str, &'static str>,
 +}
 +
 +impl CompilerLintFunctions {
 +    #[must_use]
 +    pub fn new() -> Self {
 +        let mut map = FxHashMap::default();
 +        map.insert("span_lint", "utils::span_lint");
 +        map.insert("struct_span_lint", "utils::span_lint");
 +        map.insert("lint", "utils::span_lint");
 +        map.insert("span_lint_note", "utils::span_lint_and_note");
 +        map.insert("span_lint_help", "utils::span_lint_and_help");
 +        Self { map }
 +    }
 +}
 +
 +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +            let fn_name = path.ident;
-                 match &*ps.ident.as_str() {
++            if let Some(sugg) = self.map.get(fn_name.as_str());
 +            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, ty, &paths::EARLY_CONTEXT)
 +                || match_type(cx, ty, &paths::LATE_CONTEXT);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    COMPILER_LINT_FUNCTIONS,
 +                    path.ident.span,
 +                    "usage of a compiler lint function",
 +                    None,
 +                    &format!("please use the Clippy variant of this function: `{}`", sugg),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
 +            return;
 +        }
 +
 +        let (method_names, arg_lists, spans) = method_calls(expr, 2);
 +        let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
 +        if_chain! {
 +            if let ["expn_data", "outer_expn"] = method_names.as_slice();
 +            let args = arg_lists[1];
 +            if args.len() == 1;
 +            let self_arg = &args[0];
 +            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OUTER_EXPN_EXPN_DATA,
 +                    spans[1].with_hi(expr.span.hi()),
 +                    "usage of `outer_expn().expn_data()`",
 +                    "try",
 +                    "outer_expn_data()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
 +
 +impl EarlyLintPass for ProduceIce {
 +    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
 +    }
 +}
 +
 +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
 +        FnKind::Closure(..) => false,
 +    }
 +}
 +
 +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Call(func, and_then_args) = expr.kind;
 +            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
 +            if and_then_args.len() == 5;
 +            if let ExprKind::Closure(_, _, body_id, _, _) = &and_then_args[4].kind;
 +            let body = cx.tcx.hir().body(*body_id);
 +            let only_expr = peel_blocks_with_stmt(&body.value);
 +            if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
 +            then {
 +                let and_then_snippets = get_and_then_snippets(cx, and_then_args);
 +                let mut sle = SpanlessEq::new(cx).deny_side_effects();
++                match ps.ident.as_str() {
 +                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
 +                    },
 +                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
 +                    },
 +                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
 +                    },
 +                    "help" => {
 +                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
 +                    }
 +                    "note" => {
 +                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
 +                    }
 +                    _  => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct AndThenSnippets<'a> {
 +    cx: Cow<'a, str>,
 +    lint: Cow<'a, str>,
 +    span: Cow<'a, str>,
 +    msg: Cow<'a, str>,
 +}
 +
 +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
 +    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
 +    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
 +    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
 +    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
 +
 +    AndThenSnippets {
 +        cx: cx_snippet,
 +        lint: lint_snippet,
 +        span: span_snippet,
 +        msg: msg_snippet,
 +    }
 +}
 +
 +struct SpanSuggestionSnippets<'a> {
 +    help: Cow<'a, str>,
 +    sugg: Cow<'a, str>,
 +    applicability: Cow<'a, str>,
 +}
 +
 +fn span_suggestion_snippets<'a, 'hir>(
 +    cx: &LateContext<'_>,
 +    span_call_args: &'hir [Expr<'hir>],
 +) -> SpanSuggestionSnippets<'a> {
 +    let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +    let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
 +    let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
 +
 +    SpanSuggestionSnippets {
 +        help: help_snippet,
 +        sugg: sugg_snippet,
 +        applicability: applicability_snippet,
 +    }
 +}
 +
 +fn suggest_suggestion(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
 +) {
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            span_suggestion_snippets.help,
 +            span_suggestion_snippets.sugg,
 +            span_suggestion_snippets.applicability
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_help(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    help: &str,
 +    with_span: bool,
 +) {
 +    let option_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_help({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            &option_span,
 +            help
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_note(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    note: &str,
 +    with_span: bool,
 +) {
 +    let note_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_note({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            note_span,
 +            note
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            // Check if this is a call to utils::match_type()
 +            if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
 +            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
 +            // Extract the path to the matched type
 +            if let Some(segments) = path_to_matched_type(cx, ty_path);
 +            let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
 +            if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
 +            // Check if the matched type is a diagnostic item
 +            if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
 +            then {
 +                // TODO: check paths constants from external crates.
 +                let cx_snippet = snippet(cx, context.span, "_");
 +                let ty_snippet = snippet(cx, ty.span, "_");
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +                    expr.span,
 +                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
 +                    "try",
 +                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
 +    use rustc_hir::ItemKind;
 +
 +    match &expr.kind {
 +        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
 +        ExprKind::Path(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)) = cx.tcx.hir().find(parent_id) {
 +                    if let Some(init) = local.init {
 +                        return path_to_matched_type(cx, init);
 +                    }
 +                }
 +            },
 +            Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
 +                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
 +                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
 +                        let body = cx.tcx.hir().body(body_id);
 +                        return path_to_matched_type(cx, &body.value);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        },
 +        ExprKind::Array(exprs) => {
 +            let segments: Vec<Symbol> = exprs
 +                .iter()
 +                .filter_map(|expr| {
 +                    if let ExprKind::Lit(lit) = &expr.kind {
 +                        if let LitKind::Str(sym, _) = lit.node {
 +                            return Some(sym);
 +                        }
 +                    }
 +
 +                    None
 +                })
 +                .collect();
 +
 +            if segments.len() == exprs.len() {
 +                return Some(segments);
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    None
 +}
 +
 +// 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) != Res::Err {
 +        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));
 +    for item_def_id in lang_items.items().iter().flatten().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
 +}
 +
 +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");
 +            }
 +        }
 +    }
 +}
 +
 +#[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] {
 +            if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
 +                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
 +            },
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let (local, after, if_chain_span) = if_chain! {
 +            if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
 +            if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
 +            then { (local, after, if_chain_span) } else { return }
 +        };
 +        if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be above the `if_chain!`",
 +            );
 +        } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be inside `then { .. }`",
 +            );
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
 +            (cond, then, r#else.is_some())
 +        } else {
 +            return;
 +        };
 +        let then_block = match then.kind {
 +            ExprKind::Block(block, _) => block,
 +            _ => return,
 +        };
 +        let if_chain_span = is_expn_of(expr.span, "if_chain");
 +        if !els {
 +            check_nested_if_chains(cx, expr, then_block, if_chain_span);
 +        }
 +        let if_chain_span = match if_chain_span {
 +            None => return,
 +            Some(span) => span,
 +        };
 +        // check for `if a && b;`
 +        if_chain! {
 +            if let ExprKind::Binary(op, _, _) = cond.kind;
 +            if op.node == BinOpKind::And;
 +            if cx.sess().source_map().is_multiline(cond.span);
 +            then {
 +                span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
 +            }
 +        }
 +        if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
 +            && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
 +        {
 +            span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
 +        }
 +    }
 +}
 +
 +fn check_nested_if_chains(
 +    cx: &LateContext<'_>,
 +    if_expr: &Expr<'_>,
 +    then_block: &Block<'_>,
 +    if_chain_span: Option<Span>,
 +) {
 +    #[rustfmt::skip]
 +    let (head, tail) = match *then_block {
 +        Block { stmts, expr: Some(tail), .. } => (stmts, tail),
 +        Block {
 +            stmts: &[
 +                ref head @ ..,
 +                Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
 +            ],
 +            ..
 +        } => (head, tail),
 +        _ => return,
 +    };
 +    if_chain! {
 +        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
 +        let sm = cx.sess().source_map();
 +        if head
 +            .iter()
 +            .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
 +        if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
 +        then {} else { return }
 +    }
 +    let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
 +        (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
 +        (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
 +        (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
 +        _ => return,
 +    };
 +    span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
 +        let (span, msg) = match head {
 +            [] => return,
 +            [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
 +            [a, .., b] => (
 +                a.span.to(b.span),
 +                "these `let` statements can also be in the `if_chain!`",
 +            ),
 +        };
 +        diag.span_help(span, msg);
 +    });
 +}
 +
 +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
 +    cx.tcx
 +        .hir()
 +        .parent_iter(hir_id)
 +        .find(|(_, node)| {
 +            #[rustfmt::skip]
 +            !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
 +        })
 +        .map_or(false, |(id, _)| {
 +            is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
 +        })
 +}
 +
 +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
 +/// of the `then {..}` portion of an `if_chain!`
 +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
 +    let span = if let [stmt, ..] = stmts {
 +        stmt.span
 +    } else if let Some(expr) = expr {
 +        expr.span
 +    } else {
 +        // empty `then {}`
 +        return true;
 +    };
 +    is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
 +}
 +
 +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
 +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
 +    let mut span = local.pat.span;
 +    if let Some(init) = local.init {
 +        span = span.to(init.span);
 +    }
 +    span.adjust(if_chain_span.ctxt().outer_expn());
 +    let sm = cx.sess().source_map();
 +    let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
 +    let span = sm.span_extend_to_next_char(span, ';', false);
 +    Span::new(
 +        span.lo() - BytePos(3),
 +        span.hi() + BytePos(1),
 +        span.ctxt(),
 +        span.parent(),
 +    )
 +}
 +
 +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
 +
 +impl LateLintPass<'_> for MsrvAttrImpl {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if_chain! {
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: Some(lint_pass_trait_ref),
 +                self_ty,
 +                items,
 +                ..
 +            }) = &item.kind;
 +            if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
 +            let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
 +            if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
 +            let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
 +            if let ty::Adt(self_ty_def, _) = self_ty.kind();
 +            if self_ty_def.is_struct();
 +            if self_ty_def.all_fields().any(|f| {
 +                cx.tcx
 +                    .type_of(f.did)
 +                    .walk()
 +                    .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
 +                    .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
 +            });
 +            if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
 +            then {
 +                let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
 +                let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
 +                let span = cx.sess().source_map().span_through_char(item.span, '{');
 +                span_lint_and_sugg(
 +                    cx,
 +                    MISSING_MSRV_ATTR_IMPL,
 +                    span,
 +                    &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
 +                    &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
 +                    format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index 8c1910b3b2af8d37aab0874ad8ac0b6456fc5ab5,0000000000000000000000000000000000000000..cf2de6a42af36633607576db2fc5deecb0654a0a
mode 100644,000000..100644
--- /dev/null
@@@ -1,925 -1,0 +1,973 @@@
-         lints
-             .iter_mut()
-             .for_each(|x| x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default()));
 +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 +//!
 +//! This module and therefore the entire lint is guarded by a feature flag called `internal`
 +//!
 +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 +//! a simple mistake)
 +
++use crate::renamed_lints::RENAMED_LINTS;
 +use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
 +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
++use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 +/// These lints are excluded from the export.
 +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
 +/// These groups will be ignored by the lint group matcher. This is useful for collections like
 +/// `clippy::all`
 +const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
 +/// Lints within this group will be excluded from the collection. These groups
 +/// have to be defined without the `clippy::` prefix.
 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
 +/// Collected deprecated lint will be assigned to this group in the JSON output
 +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
 +/// This is the lint level for deprecated lints that will be displayed in the lint list
 +const DEPRECATED_LINT_LEVEL: &str = "none";
 +/// This array holds Clippy's lint groups with their corresponding default lint level. The
 +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
 +const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
 +    ("correctness", "deny"),
 +    ("suspicious", "warn"),
 +    ("restriction", "allow"),
 +    ("style", "warn"),
 +    ("pedantic", "allow"),
 +    ("complexity", "warn"),
 +    ("perf", "warn"),
 +    ("cargo", "allow"),
 +    ("nursery", "allow"),
 +];
 +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
 +/// to only keep the actual lint group in the output.
 +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
 +
 +/// This template will be used to format the configuration section in the lint documentation.
 +/// The `configurations` parameter will be replaced with one or multiple formatted
 +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations
 +macro_rules! CONFIGURATION_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Configuration
 +This lint has the following configuration variables:
 +
 +{configurations}
 +"#
 +    };
 +}
 +/// This template will be used to format an individual `ClippyConfiguration` instance in the
 +/// lint documentation.
 +///
 +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and
 +/// `default`
 +macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n"
 +    };
 +}
 +
++macro_rules! RENAMES_SECTION_TEMPLATE {
++    () => {
++        r#"
++### Past names
++
++{names}
++"#
++    };
++}
++macro_rules! RENAME_VALUE_TEMPLATE {
++    () => {
++        "* `{name}`\n"
++    };
++}
++
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg_for_edges"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +const APPLICABILITY_NAME_INDEX: usize = 2;
 +/// This applicability will be set for unresolved applicability values.
 +const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 +/// The version that will be displayed if none has been defined
 +const VERSION_DEFAULT_STR: &str = "Unknown";
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Collects metadata about clippy lints for the website.
 +    ///
 +    /// This lint will be used to report problems of syntax parsing. You should hopefully never
 +    /// see this but never say never I guess ^^
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not a bad thing but definitely a hacky way to do it. See
 +    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
 +    /// about the implementation.
 +    ///
 +    /// ### Known problems
 +    /// Hopefully none. It would be pretty uncool to have a problem here :)
 +    ///
 +    /// ### Example output
 +    /// ```json,ignore
 +    /// {
 +    ///     "id": "internal_metadata_collector",
 +    ///     "id_span": {
 +    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
 +    ///         "line": 1
 +    ///     },
 +    ///     "group": "clippy::internal",
 +    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub INTERNAL_METADATA_COLLECTOR,
 +    internal_warn,
 +    "A busy bee collection metadata about lints"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
-     let mut docs = String::from(&*lines.next()?.as_str());
++        collect_renames(&mut lints);
++        for x in &mut lints {
++            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
++        }
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    group: String,
 +    level: String,
 +    docs: String,
 +    version: String,
 +    /// This field is only used in the output and will only be
 +    /// mapped shortly before the actual output.
 +    applicability: Option<ApplicabilityInfo>,
 +}
 +
 +impl LintMetadata {
 +    fn new(
 +        id: String,
 +        id_span: SerializableSpan,
 +        group: String,
 +        level: &'static str,
 +        version: String,
 +        docs: String,
 +    ) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            version,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl std::fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    #[allow(dead_code)]
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        docs.push_str(&configuration_section);
 +                    }
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        group,
 +                        level,
 +                        version,
 +                        docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // blacklist check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        DEPRECATED_LINT_GROUP_STR.to_string(),
 +                        DEPRECATED_LINT_LEVEL,
 +                        version,
 +                        docs,
 +                    ));
 +                }
 +            }
 +        }
 +    }
 +
 +    /// Collecting constant applicability from the actual lint emissions
 +    ///
 +    /// Example:
 +    /// ```rust, ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     SOME_LINT,
 +    ///     item.span,
 +    ///     "Le lint message",
 +    ///     "Here comes help:",
 +    ///     "#![allow(clippy::all)]",
 +    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
 +    /// );
 +    /// ```
 +    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
 +        if let Some(args) = match_lint_emission(cx, expr) {
 +            let emission_info = extract_emission_info(cx, args);
 +            if emission_info.is_empty() {
 +                // See:
 +                // - src/misc.rs:734:9
 +                // - src/methods/mod.rs:3545:13
 +                // - src/methods/mod.rs:3496:13
 +                // We are basically unable to resolve the lint name itself.
 +                return;
 +            }
 +
 +            for (lint_name, applicability, is_multi_part) in emission_info {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +///
 +/// ---
 +///
 +/// This function may modify the doc comment to ensure that the string can be displayed using a
 +/// markdown viewer in Clippy's lint list. The following modifications could be applied:
 +/// * Removal of leading space after a new line. (Important to display tables)
 +/// * Ensures that code blocks only contain language information
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
-         let line = &*line;
++    let mut docs = String::from(lines.next()?.as_str());
 +    let mut in_code_block = false;
 +    let mut is_code_block_rust = false;
 +    for line in lines {
 +        let line = line.as_str();
 +
 +        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
 +        if is_code_block_rust && line.trim_start().starts_with("# ") {
 +            continue;
 +        }
 +
 +        // The line should be represented in the lint list, even if it's just an empty line
 +        docs.push('\n');
 +        if let Some(info) = line.trim_start().strip_prefix("```") {
 +            in_code_block = !in_code_block;
 +            is_code_block_rust = false;
 +            if in_code_block {
 +                let lang = info
 +                    .trim()
 +                    .split(',')
 +                    // remove rustdoc directives
 +                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
 +                    // if no language is present, fill in "rust"
 +                    .unwrap_or("rust");
 +                docs.push_str("```");
 +                docs.push_str(lang);
 +
 +                is_code_block_rust = lang == "rust";
 +                continue;
 +            }
 +        }
 +        // This removes the leading space that the macro translation introduces
 +        if let Some(stripped_doc) = line.strip_prefix(' ') {
 +            docs.push_str(stripped_doc);
 +        } else if !line.is_empty() {
 +            docs.push_str(line);
 +        }
 +    }
 +    Some(docs)
 +}
 +
 +fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
 +    extract_clippy_version_value(cx, item).map_or_else(
 +        || VERSION_DEFAULT_STR.to_string(),
 +        |version| version.as_str().to_string(),
 +    )
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx.lint_store.check_lint_name(
 +        lint_name,
 +        Some(sym::clippy),
 +        &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
 +    );
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(&group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
 +}
 +
 +fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
++fn collect_renames(lints: &mut Vec<LintMetadata>) {
++    for lint in lints {
++        let mut collected = String::new();
++        let mut names = vec![lint.id.clone()];
++
++        loop {
++            if let Some(lint_name) = names.pop() {
++                for (k, v) in RENAMED_LINTS {
++                    if_chain! {
++                        if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
++                        if name == lint_name;
++                        if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
++                        then {
++                            write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap();
++                            names.push(past_name.to_string());
++                        }
++                    }
++                }
++
++                continue;
++            }
++
++            break;
++        }
++
++        if !collected.is_empty() {
++            write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap();
++        }
++    }
++}
++
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .into_iter()
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This returns the parent local node if the expression is a reference one
 +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
 +    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
 +        if let hir::def::Res::Local(local_hir) = path.res {
 +            return get_parent_local_hir_id(cx, local_hir);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
 +    let map = cx.tcx.hir();
 +
 +    match map.find(map.get_parent_node(hir_id)) {
 +        Some(hir::Node::Local(local)) => Some(local),
 +        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
 +        _ => None,
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct IsMultiSpanScanner<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    suggestion_count: usize,
 +}
 +
 +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            suggestion_count: 0,
 +        }
 +    }
 +
 +    /// Add a new single expression suggestion to the counter
 +    fn add_single_span_suggestion(&mut self) {
 +        self.suggestion_count += 1;
 +    }
 +
 +    /// Signals that a suggestion with possible multiple spans was found
 +    fn add_multi_part_suggestion(&mut self) {
 +        self.suggestion_count += 2;
 +    }
 +
 +    /// Checks if the suggestions include multiple spans
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        // Early return if the lint is already multi span
 +        if self.is_multi_part() {
 +            return;
 +        }
 +
 +        match &expr.kind {
 +            ExprKind::Call(fn_expr, _args) => {
 +                let found_function = SUGGESTION_FUNCTIONS
 +                    .iter()
 +                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
 +                if found_function {
 +                    // These functions are all multi part suggestions
 +                    self.add_single_span_suggestion();
 +                }
 +            },
 +            ExprKind::MethodCall(path, arg, _arg_span) => {
 +                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
 +                if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
 +                    let called_method = path.ident.name.as_str().to_string();
 +                    for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
 +                        if *method_name == called_method {
 +                            if *is_multi_part {
 +                                self.add_multi_part_suggestion();
 +                            } else {
 +                                self.add_single_span_suggestion();
 +                            }
 +                            break;
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index 9f162a117b2d103cc12309f33029f24203ff4a3b,0000000000000000000000000000000000000000..d487868cafe50e42b682aa56969453d4db69979d
mode 100644,000000..100644
--- /dev/null
@@@ -1,659 -1,0 +1,659 @@@
-                 self.expr(&*then)
 +#![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::interpret::Scalar;
 +use rustc_middle::ty::subst::{Subst, 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(Symbol),
 +}
 +
 +impl PartialEq for Constant {
 +    fn eq(&self, other: &Self) -> bool {
 +        match (self, other) {
 +            (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
 +            (&Self::Binary(ref l), &Self::Binary(ref 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::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,
 +            // 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(ref s) => {
 +                s.hash(state);
 +            },
 +        }
 +    }
 +}
 +
 +impl Constant {
 +    pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
 +        match (left, right) {
 +            (&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::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::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
 +            (&Self::Tuple(ref l), &Self::Tuple(ref r)) | (&Self::Vec(ref l), &Self::Vec(ref r)) => 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::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
 +                match Self::partial_cmp(tcx, cmp_type, lv, rv) {
 +                    Some(Equal) => Some(ls.cmp(rs)),
 +                    x => x,
 +                }
 +            },
 +            (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(tcx, cmp_type, 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(s) => Constant::Err(s),
 +    }
 +}
 +
 +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 ity = match *ty.kind() {
 +                    ty::Int(ity) => ity,
 +                    _ => 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,
 +                        ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs),
 +                        None,
 +                    )
 +                    .ok()
 +                    .map(|val| rustc_middle::ty::Const::from_value(self.lcx.tcx, val, ty))?;
 +                let result = miri_to_const(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().expect("invalid shift")).map(zext),
 +                        BinOpKind::Shl => l.checked_shl(r.try_into().expect("invalid shift")).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().expect("shift too large")).map(Constant::Int),
 +                    BinOpKind::Shl => l.checked_shl(r.try_into().expect("shift too large")).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(result: ty::Const<'_>) -> Option<Constant> {
 +    use rustc_middle::mir::interpret::ConstValue;
 +    match result.val() {
 +        ty::ConstKind::Value(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,
 +            }
 +        },
 +        ty::ConstKind::Value(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,
 +        },
 +        ty::ConstKind::Value(ConstValue::ByRef { alloc, offset: _ }) => match result.ty().kind() {
 +            ty::Array(sub_type, len) => match sub_type.kind() {
 +                ty::Float(FloatTy::F32) => match miri_to_const(*len) {
 +                    Some(Constant::Int(len)) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * len as usize))
 +                        .to_owned()
 +                        .chunks(4)
 +                        .map(|chunk| {
 +                            Some(Constant::F32(f32::from_le_bytes(
 +                                chunk.try_into().expect("this shouldn't happen"),
 +                            )))
 +                        })
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                ty::Float(FloatTy::F64) => match miri_to_const(*len) {
 +                    Some(Constant::Int(len)) => alloc
 +                        .inner()
 +                        .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * len as usize))
 +                        .to_owned()
 +                        .chunks(8)
 +                        .map(|chunk| {
 +                            Some(Constant::F64(f64::from_le_bytes(
 +                                chunk.try_into().expect("this shouldn't happen"),
 +                            )))
 +                        })
 +                        .collect::<Option<Vec<Constant>>>()
 +                        .map(Constant::Vec),
 +                    _ => None,
 +                },
 +                // FIXME: implement other array type conversions.
 +                _ => None,
 +            },
 +            _ => None,
 +        },
 +        // FIXME: implement other conversions.
 +        _ => None,
 +    }
 +}
index 1e0fc789af24306beaea24a7823acac6ca2dca8a,0000000000000000000000000000000000000000..4604ae5c2c7f0486bbfda4424a5a6070a6534a6d
mode 100644,000000..100644
--- /dev/null
@@@ -1,469 -1,0 +1,469 @@@
-             if let [stmt] = &*block.stmts;
 +//! This module contains functions that retrieve specific elements.
 +
 +#![deny(clippy::missing_docs_in_private_items)]
 +
 +use crate::consts::{constant_simple, Constant};
 +use crate::ty::is_type_diagnostic_item;
 +use crate::{is_expn_of, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast::ast;
 +use rustc_hir as hir;
 +use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::{sym, symbol, Span};
 +
 +/// The essential nodes of a desugared for loop as well as the entire span:
 +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
 +pub struct ForLoop<'tcx> {
 +    /// `for` loop item
 +    pub pat: &'tcx hir::Pat<'tcx>,
 +    /// `IntoIterator` argument
 +    pub arg: &'tcx hir::Expr<'tcx>,
 +    /// `for` loop body
 +    pub body: &'tcx hir::Expr<'tcx>,
 +    /// Compare this against `hir::Destination.target`
 +    pub loop_id: HirId,
 +    /// entire `for` loop span
 +    pub span: Span,
 +}
 +
 +impl<'tcx> ForLoop<'tcx> {
 +    /// Parses a desugared `for` loop
 +    pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let hir::ExprKind::DropTemps(e) = expr.kind;
 +            if let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind;
 +            if let hir::ExprKind::Call(_, [arg]) = iterexpr.kind;
 +            if let hir::ExprKind::Loop(block, ..) = arm.body.kind;
++            if let [stmt] = block.stmts;
 +            if let hir::StmtKind::Expr(e) = stmt.kind;
 +            if let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind;
 +            if let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind;
 +            then {
 +                return Some(Self {
 +                    pat: field.pat,
 +                    arg,
 +                    body: some_arm.body,
 +                    loop_id: arm.body.hir_id,
 +                    span: expr.span.ctxt().outer_expn_data().call_site,
 +                });
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +/// An `if` expression without `DropTemps`
 +pub struct If<'hir> {
 +    /// `if` condition
 +    pub cond: &'hir Expr<'hir>,
 +    /// `if` then expression
 +    pub then: &'hir Expr<'hir>,
 +    /// `else` expression
 +    pub r#else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> If<'hir> {
 +    #[inline]
 +    /// Parses an `if` expression
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(
 +            Expr {
 +                kind: ExprKind::DropTemps(cond),
 +                ..
 +            },
 +            then,
 +            r#else,
 +        ) = expr.kind
 +        {
 +            Some(Self { cond, then, r#else })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// An `if let` expression
 +pub struct IfLet<'hir> {
 +    /// `if let` pattern
 +    pub let_pat: &'hir Pat<'hir>,
 +    /// `if let` scrutinee
 +    pub let_expr: &'hir Expr<'hir>,
 +    /// `if let` then expression
 +    pub if_then: &'hir Expr<'hir>,
 +    /// `if let` else expression
 +    pub if_else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> IfLet<'hir> {
 +    /// Parses an `if let` expression
 +    pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(
 +            Expr {
 +                kind:
 +                    ExprKind::Let(hir::Let {
 +                        pat: let_pat,
 +                        init: let_expr,
 +                        ..
 +                    }),
 +                ..
 +            },
 +            if_then,
 +            if_else,
 +        ) = expr.kind
 +        {
 +            let mut iter = cx.tcx.hir().parent_iter(expr.hir_id);
 +            if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
 +                if let Some((
 +                    _,
 +                    Node::Expr(Expr {
 +                        kind: ExprKind::Loop(_, _, LoopSource::While, _),
 +                        ..
 +                    }),
 +                )) = iter.next()
 +                {
 +                    // while loop desugar
 +                    return None;
 +                }
 +            }
 +            return Some(Self {
 +                let_pat,
 +                let_expr,
 +                if_then,
 +                if_else,
 +            });
 +        }
 +        None
 +    }
 +}
 +
 +/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
 +pub enum IfLetOrMatch<'hir> {
 +    /// Any `match` expression
 +    Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
 +    /// scrutinee, pattern, then block, else block
 +    IfLet(
 +        &'hir Expr<'hir>,
 +        &'hir Pat<'hir>,
 +        &'hir Expr<'hir>,
 +        Option<&'hir Expr<'hir>>,
 +    ),
 +}
 +
 +impl<'hir> IfLetOrMatch<'hir> {
 +    /// Parses an `if let` or `match` expression
 +    pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
 +        match expr.kind {
 +            ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
 +            _ => IfLet::hir(cx, expr).map(
 +                |IfLet {
 +                     let_expr,
 +                     let_pat,
 +                     if_then,
 +                     if_else,
 +                 }| { Self::IfLet(let_expr, let_pat, if_then, if_else) },
 +            ),
 +        }
 +    }
 +}
 +
 +/// An `if` or `if let` expression
 +pub struct IfOrIfLet<'hir> {
 +    /// `if` condition that is maybe a `let` expression
 +    pub cond: &'hir Expr<'hir>,
 +    /// `if` then expression
 +    pub then: &'hir Expr<'hir>,
 +    /// `else` expression
 +    pub r#else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> IfOrIfLet<'hir> {
 +    #[inline]
 +    /// Parses an `if` or `if let` expression
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(cond, then, r#else) = expr.kind {
 +            if let ExprKind::DropTemps(new_cond) = cond.kind {
 +                return Some(Self {
 +                    cond: new_cond,
 +                    r#else,
 +                    then,
 +                });
 +            }
 +            if let ExprKind::Let(..) = cond.kind {
 +                return Some(Self { cond, then, r#else });
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +/// Represent a range akin to `ast::ExprKind::Range`.
 +#[derive(Debug, Copy, Clone)]
 +pub struct Range<'a> {
 +    /// The lower bound of the range, or `None` for ranges such as `..X`.
 +    pub start: Option<&'a hir::Expr<'a>>,
 +    /// The upper bound of the range, or `None` for ranges such as `X..`.
 +    pub end: Option<&'a hir::Expr<'a>>,
 +    /// Whether the interval is open or closed.
 +    pub limits: ast::RangeLimits,
 +}
 +
 +impl<'a> Range<'a> {
 +    /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
 +    pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
 +        /// Finds the field named `name` in the field. Always return `Some` for
 +        /// convenience.
 +        fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
 +            let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
 +            Some(expr)
 +        }
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(path, args)
 +                if matches!(
 +                    path.kind,
 +                    hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, ..))
 +                ) =>
 +            {
 +                Some(Range {
 +                    start: Some(&args[0]),
 +                    end: Some(&args[1]),
 +                    limits: ast::RangeLimits::Closed,
 +                })
 +            },
 +            hir::ExprKind::Struct(path, fields, None) => match &path {
 +                hir::QPath::LangItem(hir::LangItem::RangeFull, ..) => Some(Range {
 +                    start: None,
 +                    end: None,
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeFrom, ..) => Some(Range {
 +                    start: Some(get_field("start", fields)?),
 +                    end: None,
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::Range, ..) => Some(Range {
 +                    start: Some(get_field("start", fields)?),
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeToInclusive, ..) => Some(Range {
 +                    start: None,
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::Closed,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeTo, ..) => Some(Range {
 +                    start: None,
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Represent the pre-expansion arguments of a `vec!` invocation.
 +pub enum VecArgs<'a> {
 +    /// `vec![elem; len]`
 +    Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
 +    /// `vec![a, b, c]`
 +    Vec(&'a [hir::Expr<'a>]),
 +}
 +
 +impl<'a> VecArgs<'a> {
 +    /// Returns the arguments of the `vec!` macro if this expression was expanded
 +    /// from `vec!`.
 +    pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
 +        if_chain! {
 +            if let hir::ExprKind::Call(fun, args) = expr.kind;
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            if is_expn_of(fun.span, "vec").is_some();
 +            if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +            then {
 +                return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
 +                    // `vec![elem; size]` case
 +                    Some(VecArgs::Repeat(&args[0], &args[1]))
 +                } else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
 +                    // `vec![a, b, c]` case
 +                    if_chain! {
 +                        if let hir::ExprKind::Box(boxed) = args[0].kind;
 +                        if let hir::ExprKind::Array(args) = boxed.kind;
 +                        then {
 +                            return Some(VecArgs::Vec(args));
 +                        }
 +                    }
 +
 +                    None
 +                } else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
 +                    Some(VecArgs::Vec(&[]))
 +                } else {
 +                    None
 +                };
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
 +/// A desugared `while` loop
 +pub struct While<'hir> {
 +    /// `while` loop condition
 +    pub condition: &'hir Expr<'hir>,
 +    /// `while` loop body
 +    pub body: &'hir Expr<'hir>,
 +}
 +
 +impl<'hir> While<'hir> {
 +    #[inline]
 +    /// Parses a desugared `while` loop
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::Loop(
 +            Block {
 +                expr:
 +                    Some(Expr {
 +                        kind:
 +                            ExprKind::If(
 +                                Expr {
 +                                    kind: ExprKind::DropTemps(condition),
 +                                    ..
 +                                },
 +                                body,
 +                                _,
 +                            ),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +            LoopSource::While,
 +            _,
 +        ) = expr.kind
 +        {
 +            return Some(Self { condition, body });
 +        }
 +        None
 +    }
 +}
 +
 +/// A desugared `while let` loop
 +pub struct WhileLet<'hir> {
 +    /// `while let` loop item pattern
 +    pub let_pat: &'hir Pat<'hir>,
 +    /// `while let` loop scrutinee
 +    pub let_expr: &'hir Expr<'hir>,
 +    /// `while let` loop body
 +    pub if_then: &'hir Expr<'hir>,
 +}
 +
 +impl<'hir> WhileLet<'hir> {
 +    #[inline]
 +    /// Parses a desugared `while let` loop
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::Loop(
 +            Block {
 +                expr:
 +                    Some(Expr {
 +                        kind:
 +                            ExprKind::If(
 +                                Expr {
 +                                    kind:
 +                                        ExprKind::Let(hir::Let {
 +                                            pat: let_pat,
 +                                            init: let_expr,
 +                                            ..
 +                                        }),
 +                                    ..
 +                                },
 +                                if_then,
 +                                _,
 +                            ),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +            LoopSource::While,
 +            _,
 +        ) = expr.kind
 +        {
 +            return Some(Self {
 +                let_pat,
 +                let_expr,
 +                if_then,
 +            });
 +        }
 +        None
 +    }
 +}
 +
 +/// Converts a hir binary operator to the corresponding `ast` type.
 +#[must_use]
 +pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
 +    match op {
 +        hir::BinOpKind::Eq => ast::BinOpKind::Eq,
 +        hir::BinOpKind::Ge => ast::BinOpKind::Ge,
 +        hir::BinOpKind::Gt => ast::BinOpKind::Gt,
 +        hir::BinOpKind::Le => ast::BinOpKind::Le,
 +        hir::BinOpKind::Lt => ast::BinOpKind::Lt,
 +        hir::BinOpKind::Ne => ast::BinOpKind::Ne,
 +        hir::BinOpKind::Or => ast::BinOpKind::Or,
 +        hir::BinOpKind::Add => ast::BinOpKind::Add,
 +        hir::BinOpKind::And => ast::BinOpKind::And,
 +        hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
 +        hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
 +        hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
 +        hir::BinOpKind::Div => ast::BinOpKind::Div,
 +        hir::BinOpKind::Mul => ast::BinOpKind::Mul,
 +        hir::BinOpKind::Rem => ast::BinOpKind::Rem,
 +        hir::BinOpKind::Shl => ast::BinOpKind::Shl,
 +        hir::BinOpKind::Shr => ast::BinOpKind::Shr,
 +        hir::BinOpKind::Sub => ast::BinOpKind::Sub,
 +    }
 +}
 +
 +/// A parsed `Vec` initialization expression
 +#[derive(Clone, Copy)]
 +pub enum VecInitKind {
 +    /// `Vec::new()`
 +    New,
 +    /// `Vec::default()` or `Default::default()`
 +    Default,
 +    /// `Vec::with_capacity(123)`
 +    WithConstCapacity(u128),
 +    /// `Vec::with_capacity(slice.len())`
 +    WithExprCapacity(HirId),
 +}
 +
 +/// Checks if given expression is an initialization of `Vec` and returns its kind.
 +pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
 +    if let ExprKind::Call(func, args) = expr.kind {
 +        match func.kind {
 +            ExprKind::Path(QPath::TypeRelative(ty, name))
 +                if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
 +            {
 +                if name.ident.name == sym::new {
 +                    return Some(VecInitKind::New);
 +                } else if name.ident.name == symbol::kw::Default {
 +                    return Some(VecInitKind::Default);
 +                } else if name.ident.name.as_str() == "with_capacity" {
 +                    let arg = args.get(0)?;
 +                    return match constant_simple(cx, cx.typeck_results(), arg) {
 +                        Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
 +                        _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
 +                    };
 +                };
 +            },
 +            ExprKind::Path(QPath::Resolved(_, path))
 +                if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
 +                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
 +            {
 +                return Some(VecInitKind::Default);
 +            },
 +            _ => (),
 +        }
 +    }
 +    None
 +}
index fc1a4e1f6025555bb7c472d6c4c0968676bf3a9b,0000000000000000000000000000000000000000..0603471c3431be2750b086d459c9b3a24dadce20
mode 100644,000000..100644
--- /dev/null
@@@ -1,998 -1,0 +1,998 @@@
-                 l_mut.mutbl == r_mut.mutbl && self.eq_ty(&*l_mut.ty, &*r_mut.ty)
 +use crate::consts::constant_simple;
 +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, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
 +    InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
 +    StmtKind, Ty, TyKind, TypeBinding,
 +};
 +use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::TypeckResults;
 +use rustc_span::Symbol;
 +use std::hash::{Hash, Hasher};
 +
 +/// 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<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + '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`.
 +    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))
 +                    && 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.
 +                let (left, right) = match (
 +                    snippet_opt(self.inner.cx, left.span),
 +                    snippet_opt(self.inner.cx, right.span),
 +                ) {
 +                    (Some(left), Some(right)) => (left, right),
 +                    _ => return true,
 +                };
 +                let mut left_pos = 0;
 +                let left = tokenize(&left)
 +                    .map(|t| {
 +                        let end = left_pos + t.len;
 +                        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;
 +                        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))
 +            },
 +        }
 +    }
 +
 +    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::Lit(ref l), &ExprKind::Lit(ref 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_args, _), &ExprKind::MethodCall(r_path, r_args, _)) => {
 +                self.inner.allow_side_effects && self.eq_path_segment(l_path, r_path) && 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)
 +            },
 +            (&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::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.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
 +            },
 +            (&PatKind::Path(ref l), &PatKind::Path(ref 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::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
-                 l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(&*l_rmut.ty, &*r_rmut.ty)
++                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)) => {
-                     self.hash_expr(&*j);
++                l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
 +            },
 +            (&TyKind::Path(ref l), &TyKind::Path(ref 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(cap, _, eid, _, _) => {
 +                std::mem::discriminant(&cap).hash(&mut self.s);
 +                // closures inherit TypeckResults
 +                self.hash_expr(&self.cx.tcx.hir().body(eid).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, args, ref _fn_span) => {
 +                self.hash_name(path.ident.name);
 +                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(ann, _, _, pat) => {
 +                std::mem::discriminant(&ann).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);
 +                }
 +            },
 +            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) => {
 +                self.hash_generic_args(arg_list);
 +            },
 +            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(ref 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()),
 +            }
 +        }
 +    }
 +}
index 833f8cde63abaa94d35849a0121ea85bd2f2460b,0000000000000000000000000000000000000000..5f051e3f444c0eac2906760ae8729241c6e47f94
mode 100644,000000..100644
--- /dev/null
@@@ -1,2187 -1,0 +1,2198 @@@
-             is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| &*field.pat))
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_else)]
 +#![feature(let_chains)]
 +#![feature(lint_reasons)]
 +#![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_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
 +extern crate rustc_session;
 +extern crate rustc_span;
 +extern crate rustc_target;
 +extern crate rustc_trait_selection;
 +extern crate rustc_typeck;
 +
 +#[macro_use]
 +pub mod sym_helper;
 +
 +pub mod ast_utils;
 +pub mod attrs;
 +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 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::hir_utils::{both, count_eq, eq_expr_value, over, SpanlessEq, SpanlessHash};
 +
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +use std::lazy::SyncOnceCell;
 +use std::sync::{Mutex, MutexGuard};
 +
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_ast::Attribute;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
 +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::{
 +    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
 +    HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node,
 +    Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
 +    UnOp,
 +};
 +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, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
 +use rustc_middle::ty::{FloatTy, IntTy, UintTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::source_map::original_sp;
 +use rustc_span::sym;
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{Span, DUMMY_SP};
 +use rustc_target::abi::Integer;
 +
 +use crate::consts::{constant, Constant};
 +use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +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!("`{}` is not a valid Rust version", msrv));
 +        }
 +    }
 +    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::Binding(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
 +        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);
 +    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 `QPath` resolves to a constructor of a `LangItem`.
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
 +                return cx.tcx.parent(ctor_id) == item_id;
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +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_expr_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 the expression is a path, resolves it to a `DefId` and checks if it matches the given
 +/// diagnostic item.
 +pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    path_def_id(cx, expr).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()
 +}
 +
 +/// Resolves a def path like `std::vec::Vec`.
 +/// This function is expensive and should be used sparingly.
 +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
 +    fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
 +        match tcx.def_kind(def_id) {
 +            DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
 +                .module_children(def_id)
 +                .iter()
 +                .find(|item| item.ident.name.as_str() == name)
 +                .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)),
 +            _ => None,
 +        }
 +    }
 +    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),
 +            // FIXME: rustdoc documents these two using just `pointer`.
 +            //
 +            // Maybe this is something we should do here too.
 +            "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(),
 +        }
 +    }
 +    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        tcx.crates(())
 +            .iter()
 +            .copied()
 +            .find(|&num| tcx.crate_name(num).as_str() == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
 +    let (base, first, path) = match *path {
 +        [base, first, ref path @ ..] => (base, first, path),
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let starts = find_primitive(tcx, base)
 +        .chain(find_crate(tcx, base))
 +        .filter_map(|id| item_child_by_name(tcx, id, first));
 +
 +    for first in starts {
 +        let last = path
 +            .iter()
 +            .copied()
 +            // for each segment, find the child item
 +            .try_fold(first, |res, segment| {
 +                let def_id = res.def_id();
 +                if let Some(item) = item_child_by_name(tcx, def_id, segment) {
 +                    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))
 +                } else {
 +                    None
 +                }
 +            });
 +
 +        if let Some(last) = last {
 +            return last;
 +        }
 +    }
 +
 +    Res::Err
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    match def_path_res(cx, path) {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    }
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != CRATE_DEF_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
 +        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::String,
 +        sym::Vec,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::HashMap,
 +        sym::BTreeMap,
 +        sym::HashSet,
 +        sym::BTreeSet,
 +        sym::BinaryHeap,
 +    ];
 +
 +    if let QPath::TypeRelative(_, method) = path {
 +        if method.ident.name == sym::new {
 +            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +                    return std_types_symbols
 +                        .iter()
 +                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
 +                }
 +            }
 +        }
 +    }
 +    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::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the top level expression can be moved into a closure as is.
 +/// Currently checks for:
 +/// * Break/Continue outside the given loop HIR ids.
 +/// * Yield/Return statements.
 +/// * Inline assembly.
 +/// * Usages of a field of a local where the type of the local can be partially moved.
 +///
 +/// For example, given the following function:
 +///
 +/// ```
 +/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
 +///     for item in iter {
 +///         let s = item.1;
 +///         if item.0 > 10 {
 +///             continue;
 +///         } else {
 +///             s.clear();
 +///         }
 +///     }
 +/// }
 +/// ```
 +///
 +/// When called on the expression `item.0` this will return false unless the local `item` is in the
 +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
 +/// isn't always safe to move into a closure when only a single field is needed.
 +///
 +/// When called on the `continue` expression this will return false unless the outer loop expression
 +/// is in the `loop_ids` set.
 +///
 +/// Note that this check is not recursive, so passing the `if` expression will always return true
 +/// even though sub-expressions might return false.
 +pub fn can_move_expr_to_closure_no_visit<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    loop_ids: &[HirId],
 +    ignore_locals: &HirIdSet,
 +) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
 +    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
 +        let mut capture = CaptureKind::Ref(Mutability::Not);
 +        pat.each_binding_or_first(&mut |_, id, span, _| match cx
 +            .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::Assign(_, 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).to_def_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(|| v.captures)
 +}
 +
 +/// 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>, Vec<&'tcx [Expr<'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, args, _) = &current.kind {
 +            if args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push(&**args);
 +            spans.push(path.ident.span);
 +            current = &args[0];
 +        } 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>]>> {
 +    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, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push(args); // build up `matched` backwards
 +                current = &args[0]; // 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);
 +    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, _: Span, 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 {
 +    let mut found = false;
 +    expr_visitor_no_bodies(|expr| {
 +        if !found {
 +            if let hir::ExprKind::Ret(..) = &expr.kind {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +/// 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)
 +}
 +
 +/// 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_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(
 +                e @ Expr {
 +                    kind: ExprKind::Loop(..) | ExprKind::Closure(..),
 +                    ..
 +                },
 +            ) => return Some(e),
 +            Node::Expr(_) | 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().local_def_id(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_typeck::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, _) => {
-         conds.push(&*cond);
++            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::SelfTy { .. } = 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, None) = arm.pat.kind;
 +            if is_lang_ctor(cx, path, 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_lang_ctor(cx, path, 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
 +///
 +/// Useful for skipping long running code when it's unnecessary
 +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 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.local_def_id_to_hir_id(map.get_parent_item(enclosing_node));
 +    }
 +
 +    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);
 +/// ```
 +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
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, 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) {
-         ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
++        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 {
 +    matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// 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(_, _, 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(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
 +        _ => 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 attr, _) = attr.kind {
 +            attr.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 attr, _) = attr.kind {
 +            attr.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,
 +                ..
 +            },
 +            ..,
-     match map.entry(module) {
++        ) => {
++            // 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)
 +}
 +
 +/// 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: SyncOnceCell<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = SyncOnceCell::new();
 +
 +fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
 +    let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
 +    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
++    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.def_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")
 +}
 +
 +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 134fd1ce505a092b22022b6c840a8d722cffc2e7,0000000000000000000000000000000000000000..b9ec2c19fdd312a98bdfb289e9a41b7c1317ed8b
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,37 @@@
 +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,53,0 { OR_PATTERNS, MANUAL_BITS }
 +    1,52,0 { STR_SPLIT_ONCE }
 +    1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
 +    1,50,0 { BOOL_THEN }
 +    1,47,0 { TAU }
 +    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 }
 +    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 }
 +    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
 +    1,16,0 { STR_REPEAT }
 +    1,24,0 { IS_ASCII_DIGIT }
 +}
index 9b9cbff2d146238a55b7a8d666d3cc3a0ceba645,0000000000000000000000000000000000000000..89789c3d8513538179011a8f850d0ed021f1c7eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,186 @@@
 +//! 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 ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +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"];
 +/// Preferably use the diagnostic item `sym::deref_method` where possible
 +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
 +pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 +#[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 FILE: [&str; 3] = ["std", "fs", "File"];
 +pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
 +pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 +#[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_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +#[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 INDEX: [&str; 3] = ["core", "ops", "Index"];
 +pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 +pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal")]
 +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 MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 +/// Preferably use the diagnostic item `sym::Option` where possible
 +pub const OPTION: [&str; 3] = ["core", "option", "Option"];
 +pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
 +pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 +pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 +pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
 +pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 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 PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 +#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 +pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
 +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 +pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
 +pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
 +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"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
 +/// Preferably use the diagnostic item `sym::Result` where possible
 +pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 +pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 +pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 +#[cfg(feature = "internal")]
 +pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 +pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
 +pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
 +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_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 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_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal")]
 +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_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"];
index 58abef38ea8be9e1cc7505536de596ac62e8d76f,0000000000000000000000000000000000000000..498dcbb89006d3fae3a62607e30e80f95bfc00ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,373 -1,0 +1,370 @@@
-         Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => check_operand(tcx, operand, span, body),
 +// 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, 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>)>;
 +
 +pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
 +    let def_id = body.source.def_id();
 +    let mut current = def_id;
 +    loop {
 +        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() {
 +        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::Cast(CastKind::PointerExposeAddress, _, _) => {
-             Err((span, "casting pointers to ints is unstable in const fn".into()))
-         },
-         Rvalue::Cast(CastKind::Misc, operand, _) => {
-             check_operand(tcx, operand, span, body)
-         },
-         Rvalue::Cast(
 +        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
 +            check_place(tcx, *place, span, body)
 +        },
-             _
-         ) => {
-             check_operand(tcx, operand, span, body)
-         },
++        Rvalue::Repeat(operand, _)
++        | Rvalue::Use(operand)
++        | Rvalue::Cast(
 +            CastKind::PointerFromExposedAddress
++            | CastKind::Misc
 +            | 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()))
++        },
 +        // 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::CopyNonOverlapping(box 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::Downcast(..)
 +            | ProjectionElem::Subslice { .. }
 +            | ProjectionElem::Deref
 +            | ProjectionElem::Index(_) => {},
 +        }
 +    }
 +
 +    Ok(())
 +}
 +
 +fn check_terminator<'a, 'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    body: &'a 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 `{:?}` is not stable as `const fn`",
 +                            func,
 +                        )
 +                        .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.
 +                crate::meets_msrv(
 +                    msrv,
 +                    RustcVersion::parse(since.as_str())
 +                        .expect("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted"),
 +                )
 +            } else {
 +                // Unstable const fn with the feature enabled.
 +                msrv.is_none()
 +            }
 +        })
 +}
index 04ef2f57447c6da80ad7b726e43cfba015f10cf0,0000000000000000000000000000000000000000..f88a92fb11c111146851e4213a6508c0ec26b2ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,487 -1,0 +1,508 @@@
- use rustc_span::{BytePos, Pos, Span, SyntaxContext};
 +//! Utils for extracting, inspecting or transforming source code
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use crate::line_span;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_span::hygiene;
 +use rustc_span::source_map::SourceMap;
++use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext};
 +use std::borrow::Cow;
 +
 +/// Checks if the span starts with the given text. This will return false if the span crosses
 +/// multiple files or if source is not available.
 +///
 +/// This is used to check for proc macros giving unhelpful spans to things.
 +pub fn span_starts_with<T: LintContext>(cx: &T, span: Span, text: &str) -> bool {
 +    fn helper(sm: &SourceMap, span: Span, text: &str) -> bool {
 +        let pos = sm.lookup_byte_offset(span.lo());
 +        let Some(ref src) = pos.sf.src else {
 +            return false;
 +        };
 +        let end = span.hi() - pos.sf.start_pos;
 +        src.get(pos.pos.0 as usize..end.0 as usize)
 +            // Expression spans can include wrapping parenthesis. Remove them first.
 +            .map_or(false, |s| s.trim_start_matches('(').starts_with(text))
 +    }
 +    helper(cx.sess().source_map(), span, text)
 +}
 +
 +/// 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{};\n{}\n}}", code, string))
 +    }
 +}
 +
 +/// 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))
 +    })
 +}
 +
 +/// 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(|| 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()
++}
++
 +#[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 75d27d3b594822ff342ab4c1bde86804e16a4dfd,0000000000000000000000000000000000000000..a10515d2fec635f70471ad01107081cb9eb43b22
mode 100644,000000..100644
--- /dev/null
@@@ -1,657 -1,0 +1,669 @@@
-     self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy, VariantDiscr,
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use rustc_ast::ast::Mutability;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, LangItem, TyKind, Unsafety};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::{ConstValue, Scalar};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 +use rustc_middle::ty::{
-     let ty = cx.tcx.erase_regions(ty);
++    self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
++    VariantDiscr,
 +};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 +use rustc_target::abi::{Size, VariantIdx};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::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.at(DUMMY_SP), cx.param_env)
 +}
 +
 +/// 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 the same as `other_ty`
 +pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => other_ty == inner_ty,
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// 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,
 +    })
 +}
 +
 +/// 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)
 +        .map(|assoc| {
 +            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
 +            cx.tcx.normalize_erasing_regions(cx.param_env, proj)
 +        })
 +}
 +
 +/// 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/doc/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_params = cx.tcx.mk_substs(ty_params.iter());
-     cx.tcx.infer_ctxt().enter(|infcx| {
++    let ty = tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
-             .type_implements_trait(trait_id, ty, ty_params, cx.param_env)
++    let ty_params = tcx.mk_substs(ty_params.iter());
++    tcx.infer_ctxt().enter(|infcx| {
 +        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 result = cx.tcx.infer_ctxt().enter(|infcx| {
 +        let cause = rustc_middle::traits::ObligationCause::dummy();
 +        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()
 +            .require(lang_item)
 +            .map_or(false, |li| li == 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>>),
 +    Closure(Binder<'tcx, FnSig<'tcx>>),
 +    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
 +}
 +impl<'tcx> ExprFnSig<'tcx> {
 +    /// Gets the argument type at the given offset.
 +    pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> {
 +        match self {
 +            Self::Sig(sig) => sig.input(i),
 +            Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
 +            Self::Trait(inputs, _) => 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,
 +        }
 +    }
 +}
 +
 +/// 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)))
 +    } else {
 +        let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
 +        match *ty.kind() {
 +            ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
 +            ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
 +            ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
 +            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().expect("return type was a const")));
 +                        Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
 +                    },
 +                    _ => None,
 +                }
 +            },
 +            ty::Param(_) | ty::Projection(..) => {
 +                let mut inputs = None;
 +                let mut output = None;
 +                let lang_items = cx.tcx.lang_items();
 +
 +                for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
 +                    let mut is_input = false;
 +                    if let Some(ty) = pred
 +                        .kind()
 +                        .map_bound(|pred| match pred {
 +                            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 =>
 +                            {
 +                                is_input = true;
 +                                Some(p.trait_ref.substs.type_at(1))
 +                            },
 +                            PredicateKind::Projection(p)
 +                                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
 +                                    && p.projection_ty.self_ty() == ty =>
 +                            {
 +                                is_input = false;
 +                                p.term.ty()
 +                            },
 +                            _ => None,
 +                        })
 +                        .transpose()
 +                    {
 +                        if is_input && inputs.is_none() {
 +                            inputs = Some(ty);
 +                        } else if !is_input && output.is_none() {
 +                            output = Some(ty);
 +                        } else {
 +                            // Multiple different fn trait impls. Is this even allowed?
 +                            return None;
 +                        }
 +                    }
 +                }
 +
 +                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 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
 +    }
 +}
index e8f0c338fd58aa4472a36e3455559085e4f7866f,0000000000000000000000000000000000000000..3e0b1c5c4f782c3d55afab4ee2734b2451a1333c
mode 100644,000000..100644
--- /dev/null
@@@ -1,696 -1,0 +1,697 @@@
-     /// // Bad
-     /// Insert a short example of code that triggers the lint
-     ///
-     /// // Good
-     /// Insert a short example of improved code that doesn't trigger the lint
 +# Adding a new lint
 +
 +You are probably here because you want to add a new lint to Clippy. If this is
 +the first time you're contributing to Clippy, this document guides you through
 +creating an example lint from scratch.
 +
 +To get started, we will create a lint that detects functions called `foo`,
 +because that's clearly a non-descriptive name.
 +
 +- [Adding a new lint](#adding-a-new-lint)
 +  - [Setup](#setup)
 +  - [Getting Started](#getting-started)
 +  - [Testing](#testing)
 +    - [Cargo lints](#cargo-lints)
 +  - [Rustfix tests](#rustfix-tests)
 +  - [Edition 2018 tests](#edition-2018-tests)
 +  - [Testing manually](#testing-manually)
 +  - [Lint declaration](#lint-declaration)
 +  - [Lint registration](#lint-registration)
 +  - [Lint passes](#lint-passes)
 +  - [Emitting a lint](#emitting-a-lint)
 +  - [Adding the lint logic](#adding-the-lint-logic)
 +  - [Specifying the lint's minimum supported Rust version (MSRV)](#specifying-the-lints-minimum-supported-rust-version-msrv)
 +  - [Author lint](#author-lint)
 +  - [Print HIR lint](#print-hir-lint)
 +  - [Documentation](#documentation)
 +  - [Running rustfmt](#running-rustfmt)
 +  - [Debugging](#debugging)
 +  - [PR Checklist](#pr-checklist)
 +  - [Adding configuration to a lint](#adding-configuration-to-a-lint)
 +  - [Cheat Sheet](#cheat-sheet)
 +
 +## Setup
 +
 +See the [Basics](basics.md#get-the-code) documentation.
 +
 +## Getting Started
 +
 +There is a bit of boilerplate code that needs to be set up when creating a new
 +lint. Fortunately, you can use the clippy dev tools to handle this for you. We
 +are naming our new lint `foo_functions` (lints are generally written in snake
 +case), and we don't need type information so it will have an early pass type
 +(more on this later on). If you're not sure if the name you chose fits the lint,
 +take a look at our [lint naming guidelines][lint_naming]. To get started on this
 +lint you can run `cargo dev new_lint --name=foo_functions --pass=early
 +--category=pedantic` (category will default to nursery if not provided). This
 +command will create two files: `tests/ui/foo_functions.rs` and
 +`clippy_lints/src/foo_functions.rs`, as well as
 +[registering the lint](#lint-registration). For cargo lints, two project
 +hierarchies (fail/pass) will be created by default under `tests/ui-cargo`.
 +
 +Next, we'll open up these files and add our lint!
 +
 +## Testing
 +
 +Let's write some tests first that we can execute while we iterate on our lint.
 +
 +Clippy uses UI tests for testing. UI tests check that the output of Clippy is
 +exactly as expected. Each test is just a plain Rust file that contains the code
 +we want to check. The output of Clippy is compared against a `.stderr` file.
 +Note that you don't have to create this file yourself, we'll get to
 +generating the `.stderr` files further down.
 +
 +We start by opening the test file created at `tests/ui/foo_functions.rs`.
 +
 +Update the file with some examples to get started:
 +
 +```rust
 +#![warn(clippy::foo_functions)]
 +
 +// Impl methods
 +struct A;
 +impl A {
 +    pub fn fo(&self) {}
 +    pub fn foo(&self) {}
 +    pub fn food(&self) {}
 +}
 +
 +// Default trait methods
 +trait B {
 +    fn fo(&self) {}
 +    fn foo(&self) {}
 +    fn food(&self) {}
 +}
 +
 +// Plain functions
 +fn fo() {}
 +fn foo() {}
 +fn food() {}
 +
 +fn main() {
 +    // We also don't want to lint method calls
 +    foo();
 +    let a = A;
 +    a.foo();
 +}
 +```
 +
 +Now we can run the test with `TESTNAME=foo_functions cargo uitest`,
 +currently this test is meaningless though.
 +
 +While we are working on implementing our lint, we can keep running the UI
 +test. That allows us to check if the output is turning into what we want.
 +
 +Once we are satisfied with the output, we need to run
 +`cargo dev bless` to update the `.stderr` file for our lint.
 +Please note that, we should run `TESTNAME=foo_functions cargo uitest`
 +every time before running `cargo dev bless`.
 +Running `TESTNAME=foo_functions cargo uitest` should pass then. When we commit
 +our lint, we need to commit the generated `.stderr` files, too. In general, you
 +should only commit files changed by `cargo dev bless` for the
 +specific lint you are creating/editing. Note that if the generated files are
 +empty, they should be removed.
 +
 +Note that you can run multiple test files by specifying a comma separated list:
 +`TESTNAME=foo_functions,test2,test3`.
 +
 +### Cargo lints
 +
 +For cargo lints, the process of testing differs in that we are interested in
 +the `Cargo.toml` manifest file. We also need a minimal crate associated
 +with that manifest.
 +
 +If our new lint is named e.g. `foo_categories`, after running `cargo dev new_lint`
 +we will find by default two new crates, each with its manifest file:
 +
 +* `tests/ui-cargo/foo_categories/fail/Cargo.toml`: this file should cause the new lint to raise an error.
 +* `tests/ui-cargo/foo_categories/pass/Cargo.toml`: this file should not trigger the lint.
 +
 +If you need more cases, you can copy one of those crates (under `foo_categories`) and rename it.
 +
 +The process of generating the `.stderr` file is the same, and prepending the `TESTNAME`
 +variable to `cargo uitest` works too.
 +
 +## Rustfix tests
 +
 +If the lint you are working on is making use of structured suggestions, the
 +test file should include a `// run-rustfix` comment at the top. This will
 +additionally run [rustfix] for that test. Rustfix will apply the suggestions
 +from the lint to the code of the test file and compare that to the contents of
 +a `.fixed` file.
 +
 +Use `cargo dev bless` to automatically generate the
 +`.fixed` file after running the tests.
 +
 +[rustfix]: https://github.com/rust-lang/rustfix
 +
 +## Edition 2018 tests
 +
 +Some features require the 2018 edition to work (e.g. `async_await`), but
 +compile-test tests run on the 2015 edition by default. To change this behavior
 +add `// edition:2018` at the top of the test file (note that it's space-sensitive).
 +
 +## Testing manually
 +
 +Manually testing against an example file can be useful if you have added some
 +`println!`s and the test suite output becomes unreadable. To try Clippy with
 +your local modifications, run
 +
 +```
 +cargo dev lint input.rs
 +```
 +
 +from the working copy root. With tests in place, let's have a look at
 +implementing our lint now.
 +
 +## Lint declaration
 +
 +Let's start by opening the new file created in the `clippy_lints` crate
 +at `clippy_lints/src/foo_functions.rs`. That's the crate where all the
 +lint code is. This file has already imported some initial things we will need:
 +
 +```rust
 +use rustc_lint::{EarlyLintPass, EarlyContext};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_ast::ast::*;
 +```
 +
 +The next step is to update the lint declaration. Lints are declared using the
 +[`declare_clippy_lint!`][declare_clippy_lint] macro, and we just need to update
 +the auto-generated lint declaration to have a real description, something like this:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +* The section of lines prefixed with `///` constitutes the lint documentation
 +  section. This is the default documentation style and will be displayed
 +  [like this][example_lint_page]. To render and open this documentation locally
 +  in a browser, run `cargo dev serve`.
 +* The `#[clippy::version]` attribute will be rendered as part of the lint documentation.
 +  The value should be set to the current Rust version that the lint is developed in,
 +  it can be retrieved by running `rustc -vV` in the rust-clippy directory. The version
 +  is listed under *release*. (Use the version without the `-nightly`) suffix.
 +* `FOO_FUNCTIONS` is the name of our lint. Be sure to follow the
 +  [lint naming guidelines][lint_naming] here when naming your lint.
 +  In short, the name should state the thing that is being checked for and
 +  read well when used with `allow`/`warn`/`deny`.
 +* `pedantic` sets the lint level to `Allow`.
 +  The exact mapping can be found [here][category_level_mapping]
 +* The last part should be a text that explains what exactly is wrong with the
 +  code
 +
 +The rest of this file contains an empty implementation for our lint pass,
 +which in this case is `EarlyLintPass` and should look like this:
 +
 +```rust
 +// clippy_lints/src/foo_functions.rs
 +
 +// .. imports and lint declaration ..
 +
 +declare_lint_pass!(FooFunctions => [FOO_FUNCTIONS]);
 +
 +impl EarlyLintPass for FooFunctions {}
 +```
 +
 +[declare_clippy_lint]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L60
 +[example_lint_page]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +[category_level_mapping]: https://github.com/rust-lang/rust-clippy/blob/557f6848bd5b7183f55c1e1522a326e9e1df6030/clippy_lints/src/lib.rs#L110
 +
 +## Lint registration
 +
 +When using `cargo dev new_lint`, the lint is automatically registered and
 +nothing more has to be done.
 +
 +When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
 +pass may have to be registered manually in the `register_plugins` function in
 +`clippy_lints/src/lib.rs`:
 +
 +```rust
 +store.register_early_pass(|| Box::new(foo_functions::FooFunctions));
 +```
 +
 +As one may expect, there is a corresponding `register_late_pass` method
 +available as well. Without a call to one of `register_early_pass` or
 +`register_late_pass`, the lint pass in question will not be run.
 +
 +One reason that `cargo dev update_lints` does not automate this step is that
 +multiple lints can use the same lint pass, so registering the lint pass may
 +already be done when adding a new lint. Another reason that this step is not
 +automated is that the order that the passes are registered determines the order
 +the passes actually run, which in turn affects the order that any emitted lints
 +are output in.
 +
 +## Lint passes
 +
 +Writing a lint that only checks for the name of a function means that we only
 +have to deal with the AST and don't have to deal with the type system at all.
 +This is good, because it makes writing this particular lint less complicated.
 +
 +We have to make this decision with every new Clippy lint. It boils down to using
 +either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass].
 +
 +In short, the `LateLintPass` has access to type information while the
 +`EarlyLintPass` doesn't. If you don't need access to type information, use the
 +`EarlyLintPass`. The `EarlyLintPass` is also faster. However linting speed
 +hasn't really been a concern with Clippy so far.
 +
 +Since we don't need type information for checking the function name, we used
 +`--pass=early` when running the new lint automation and all the imports were
 +added accordingly.
 +
 +[early_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html
 +[late_lint_pass]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.LateLintPass.html
 +
 +## Emitting a lint
 +
 +With UI tests and the lint declaration in place, we can start working on the
 +implementation of the lint logic.
 +
 +Let's start by implementing the `EarlyLintPass` for our `FooFunctions`:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        // TODO: Emit lint here
 +    }
 +}
 +```
 +
 +We implement the [`check_fn`][check_fn] method from the
 +[`EarlyLintPass`][early_lint_pass] trait. This gives us access to various
 +information about the function that is currently being checked. More on that in
 +the next section. Let's worry about the details later and emit our lint for
 +*every* function definition first.
 +
 +Depending on how complex we want our lint message to be, we can choose from a
 +variety of lint emission functions. They can all be found in
 +[`clippy_utils/src/diagnostics.rs`][diagnostics].
 +
 +`span_lint_and_help` seems most appropriate in this case. It allows us to
 +provide an extra help message and we can't really suggest a better name
 +automatically. This is how it looks:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        span_lint_and_help(
 +            cx,
 +            FOO_FUNCTIONS,
 +            span,
 +            "function named `foo`",
 +            None,
 +            "consider using a more meaningful name"
 +        );
 +    }
 +}
 +```
 +
 +Running our UI test should now produce output that contains the lint message.
 +
 +According to [the rustc-dev-guide], the text should be matter of fact and avoid
 +capitalization and periods, unless multiple sentences are needed.
 +When code or an identifier must appear in a message or label, it should be
 +surrounded with single grave accents \`.
 +
 +[check_fn]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/trait.EarlyLintPass.html#method.check_fn
 +[diagnostics]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_utils/src/diagnostics.rs
 +[the rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/diagnostics.html
 +
 +## Adding the lint logic
 +
 +Writing the logic for your lint will most likely be different from our example,
 +so this section is kept rather short.
 +
 +Using the [`check_fn`][check_fn] method gives us access to [`FnKind`][fn_kind]
 +that has the [`FnKind::Fn`] variant. It provides access to the name of the
 +function/method via an [`Ident`][ident].
 +
 +With that we can expand our `check_fn` method to:
 +
 +```rust
 +impl EarlyLintPass for FooFunctions {
 +    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, span: Span, _: NodeId) {
 +        if is_foo_fn(fn_kind) {
 +            span_lint_and_help(
 +                cx,
 +                FOO_FUNCTIONS,
 +                span,
 +                "function named `foo`",
 +                None,
 +                "consider using a more meaningful name"
 +            );
 +        }
 +    }
 +}
 +```
 +
 +We separate the lint conditional from the lint emissions because it makes the
 +code a bit easier to read. In some cases this separation would also allow to
 +write some unit tests (as opposed to only UI tests) for the separate function.
 +
 +In our example, `is_foo_fn` looks like:
 +
 +```rust
 +// use statements, impl EarlyLintPass, check_fn, ..
 +
 +fn is_foo_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => {
 +            // check if `fn` name is `foo`
 +            ident.name.as_str() == "foo"
 +        }
 +        // ignore closures
 +        FnKind::Closure(..) => false
 +    }
 +}
 +```
 +
 +Now we should also run the full test suite with `cargo test`. At this point
 +running `cargo test` should produce the expected output. Remember to run
 +`cargo dev bless` to update the `.stderr` file.
 +
 +`cargo test` (as opposed to `cargo uitest`) will also ensure that our lint
 +implementation is not violating any Clippy lints itself.
 +
 +That should be it for the lint implementation. Running `cargo test` should now
 +pass.
 +
 +[fn_kind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html
 +[`FnKind::Fn`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/visit/enum.FnKind.html#variant.Fn
 +[ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html
 +
 +## Specifying the lint's minimum supported Rust version (MSRV)
 +
 +Sometimes a lint makes suggestions that require a certain version of Rust. For example, the `manual_strip` lint suggests
 +using `str::strip_prefix` and `str::strip_suffix` which is only available after Rust 1.45. In such cases, you need to
 +ensure that the MSRV configured for the project is >= the MSRV of the required Rust feature. If multiple features are
 +required, just use the one with a lower MSRV.
 +
 +First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`](/clippy_utils/src/msrvs.rs). This can be
 +accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 +
 +```rust
 +msrv_aliases! {
 +    ..
 +    1,45,0 { STR_STRIP_PREFIX }
 +}
 +```
 +
 +In order to access the project-configured MSRV, you need to have an `msrv` field in the LintPass struct, and a
 +constructor to initialize the field. The `msrv` value is passed to the constructor in `clippy_lints/lib.rs`.
 +
 +```rust
 +pub struct ManualStrip {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualStrip {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +```
 +
 +The project's MSRV can then be matched against the feature MSRV in the LintPass
 +using the `meets_msrv` utility function.
 +
 +``` rust
 +if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) {
 +    return;
 +}
 +```
 +
 +The project's MSRV can also be specified as an inner attribute, which overrides
 +the value from `clippy.toml`. This can be accounted for using the
 +`extract_msrv_attr!(LintContext)` macro and passing
 +`LateContext`/`EarlyContext`.
 +
 +```rust
 +impl<'tcx> LateLintPass<'tcx> for ManualStrip {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        ...
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +```
 +
 +Once the `msrv` is added to the lint, a relevant test case should be added to
 +`tests/ui/min_rust_version_attr.rs` which verifies that the lint isn't emitted
 +if the project's MSRV is lower.
 +
 +As a last step, the lint should be added to the lint documentation. This is done
 +in `clippy_lints/src/utils/conf.rs`:
 +
 +```rust
 +define_Conf! {
 +    /// Lint: LIST, OF, LINTS, <THE_NEWLY_ADDED_LINT>. The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    ...
 +}
 +```
 +
 +## Author lint
 +
 +If you have trouble implementing your lint, there is also the internal `author`
 +lint to generate Clippy code that detects the offending pattern. It does not
 +work for all of the Rust syntax, but can give a good starting point.
 +
 +The quickest way to use it, is the
 +[Rust playground: play.rust-lang.org][author_example].
 +Put the code you want to lint into the editor and add the `#[clippy::author]`
 +attribute above the item. Then run Clippy via `Tools -> Clippy` and you should
 +see the generated code in the output below.
 +
 +[Here][author_example] is an example on the playground.
 +
 +If the command was executed successfully, you can copy the code over to where
 +you are implementing your lint.
 +
 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55
 +
 +## Print HIR lint
 +
 +To implement a lint, it's helpful to first understand the internal representation
 +that rustc uses. Clippy has the `#[clippy::dump]` attribute that prints the
 +[_High-Level Intermediate Representation (HIR)_] of the item, statement, or 
 +expression that the attribute is attached to. To attach the attribute to expressions
 +you often need to enable `#![feature(stmt_expr_attributes)]`.
 +
 +[Here][print_hir_example] you can find an example, just select _Tools_ and run _Clippy_.
 +
 +[_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html
 +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb
 +
 +## Documentation
 +
 +The final thing before submitting our PR is to add some documentation to our
 +lint declaration.
 +
 +Please document your lint with a doc comment akin to the following:
 +
 +```rust
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for ... (describe what the lint matches).
 +    ///
 +    /// ### Why is this bad?
 +    /// Supply the reason for linting the code.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust,ignore
++    /// // A short example of code that triggers the lint
++    /// ```
++    /// 
++    /// Use instead:
++    /// ```rust,ignore
++    /// // A short example of improved code that doesn't trigger the lint
 +    /// ```
 +    #[clippy::version = "1.29.0"]
 +    pub FOO_FUNCTIONS,
 +    pedantic,
 +    "function named `foo`, which is not a descriptive name"
 +}
 +```
 +
 +Once your lint is merged, this documentation will show up in the [lint
 +list][lint_list].
 +
 +[lint_list]: https://rust-lang.github.io/rust-clippy/master/index.html
 +
 +## Running rustfmt
 +
 +[Rustfmt] is a tool for formatting Rust code according to style guidelines.
 +Your code has to be formatted by `rustfmt` before a PR can be merged.
 +Clippy uses nightly `rustfmt` in the CI.
 +
 +It can be installed via `rustup`:
 +
 +```bash
 +rustup component add rustfmt --toolchain=nightly
 +```
 +
 +Use `cargo dev fmt` to format the whole codebase. Make sure that `rustfmt` is
 +installed for the nightly toolchain.
 +
 +[Rustfmt]: https://github.com/rust-lang/rustfmt
 +
 +## Debugging
 +
 +If you want to debug parts of your lint implementation, you can use the [`dbg!`]
 +macro anywhere in your code. Running the tests should then include the debug
 +output in the `stdout` part.
 +
 +[`dbg!`]: https://doc.rust-lang.org/std/macro.dbg.html
 +
 +## PR Checklist
 +
 +Before submitting your PR make sure you followed all of the basic requirements:
 +
 +<!-- Sync this with `.github/PULL_REQUEST_TEMPLATE` -->
 +
 +- \[ ] Followed [lint naming conventions][lint_naming]
 +- \[ ] Added passing UI tests (including committed `.stderr` file)
 +- \[ ] `cargo test` passes locally
 +- \[ ] Executed `cargo dev update_lints`
 +- \[ ] Added lint documentation
 +- \[ ] Run `cargo dev fmt`
 +
 +## Adding configuration to a lint
 +
 +Clippy supports the configuration of lints values using a `clippy.toml` file in the workspace
 +directory. Adding a configuration to a lint can be useful for thresholds or to constrain some
 +behavior that can be seen as a false positive for some users. Adding a configuration is done
 +in the following steps:
 +
 +1. Adding a new configuration entry to [clippy_lints::utils::conf](/clippy_lints/src/utils/conf.rs)
 +    like this:
 +    ```rust
 +    /// Lint: LINT_NAME.
 +    ///
 +    /// <The configuration field doc comment>
 +    (configuration_ident: Type = DefaultValue),
 +    ```
 +    The doc comment is automatically added to the documentation of the listed lints. The default
 +    value will be formatted using the `Debug` implementation of the type.
 +2. Adding the configuration value to the lint impl struct:
 +    1. This first requires the definition of a lint impl struct. Lint impl structs are usually
 +        generated with the `declare_lint_pass!` macro. This struct needs to be defined manually
 +        to add some kind of metadata to it:
 +        ```rust
 +        // Generated struct definition
 +        declare_lint_pass!(StructName => [
 +            LINT_NAME
 +        ]);
 +
 +        // New manual definition struct
 +        #[derive(Copy, Clone)]
 +        pub struct StructName {}
 +
 +        impl_lint_pass!(StructName => [
 +            LINT_NAME
 +        ]);
 +        ```
 +
 +    2. Next add the configuration value and a corresponding creation method like this:
 +        ```rust
 +        #[derive(Copy, Clone)]
 +        pub struct StructName {
 +            configuration_ident: Type,
 +        }
 +
 +        // ...
 +
 +        impl StructName {
 +            pub fn new(configuration_ident: Type) -> Self {
 +                Self {
 +                    configuration_ident,
 +                }
 +            }
 +        }
 +        ```
 +3. Passing the configuration value to the lint impl struct:
 +
 +    First find the struct construction in the [clippy_lints lib file](/clippy_lints/src/lib.rs).
 +    The configuration value is now cloned or copied into a local value that is then passed to the
 +    impl struct like this:
 +    ```rust
 +    // Default generated registration:
 +    store.register_*_pass(|| box module::StructName);
 +
 +    // New registration with configuration value
 +    let configuration_ident = conf.configuration_ident.clone();
 +    store.register_*_pass(move || box module::StructName::new(configuration_ident));
 +    ```
 +
 +    Congratulations the work is almost done. The configuration value can now be accessed
 +    in the linting code via `self.configuration_ident`.
 +
 +4. Adding tests:
 +    1. The default configured value can be tested like any normal lint in [`tests/ui`](/tests/ui).
 +    2. The configuration itself will be tested separately in [`tests/ui-toml`](/tests/ui-toml).
 +        Simply add a new subfolder with a fitting name. This folder contains a `clippy.toml` file
 +        with the configuration value and a rust file that should be linted by Clippy. The test can
 +        otherwise be written as usual.
 +
 +## Cheat Sheet
 +
 +Here are some pointers to things you are likely going to need for every lint:
 +
 +* [Clippy utils][utils] - Various helper functions. Maybe the function you need
 +  is already in here ([`is_type_diagnostic_item`], [`implements_trait`], [`snippet`], etc)
 +* [Clippy diagnostics][diagnostics]
 +* [Let chains][let-chains]
 +* [`from_expansion`][from_expansion] and [`in_external_macro`][in_external_macro]
 +* [`Span`][span]
 +* [`Applicability`][applicability]
 +* [Common tools for writing lints](common_tools_writing_lints.md) helps with common operations
 +* [The rustc-dev-guide][rustc-dev-guide] explains a lot of internal compiler concepts
 +* [The nightly rustc docs][nightly_docs] which has been linked to throughout
 +  this guide
 +
 +For `EarlyLintPass` lints:
 +
 +* [`EarlyLintPass`][early_lint_pass]
 +* [`rustc_ast::ast`][ast]
 +
 +For `LateLintPass` lints:
 +
 +* [`LateLintPass`][late_lint_pass]
 +* [`Ty::TyKind`][ty]
 +
 +While most of Clippy's lint utils are documented, most of rustc's internals lack
 +documentation currently. This is unfortunate, but in most cases you can probably
 +get away with copying things from existing similar lints. If you are stuck,
 +don't hesitate to ask on [Zulip] or in the issue/PR.
 +
 +[utils]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/index.html
 +[`is_type_diagnostic_item`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.is_type_diagnostic_item.html
 +[`implements_trait`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/ty/fn.implements_trait.html
 +[`snippet`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/source/fn.snippet.html
 +[let-chains]: https://github.com/rust-lang/rust/pull/94927
 +[from_expansion]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html#method.from_expansion
 +[in_external_macro]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/lint/fn.in_external_macro.html
 +[span]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/struct.Span.html
 +[applicability]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/enum.Applicability.html
 +[rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/
 +[nightly_docs]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/
 +[ast]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_ast/ast/index.html
 +[ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/sty/index.html
 +[Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/clippy
index 131ac3c3611e83ffcb74678f2712ee69e3babeae,0000000000000000000000000000000000000000..1d1aee0da2cc7be9c0cd84ad8613f35322df7b81
mode 100644,000000..100644
--- /dev/null
@@@ -1,265 -1,0 +1,266 @@@
- use clippy_utils::{is_type_diagnostic_item, return_ty};
 +# Common tools for writing lints
 +
 +You may need following tooltips to catch up with common operations.
 +
 +- [Common tools for writing lints](#common-tools-for-writing-lints)
 +  - [Retrieving the type of an expression](#retrieving-the-type-of-an-expression)
 +  - [Checking if an expr is calling a specific method](#checking-if-an-expr-is-calling-a-specific-method)
 +  - [Checking for a specific type](#checking-for-a-specific-type)
 +  - [Checking if a type implements a specific trait](#checking-if-a-type-implements-a-specific-trait)
 +  - [Checking if a type defines a specific method](#checking-if-a-type-defines-a-specific-method)
 +  - [Dealing with macros](#dealing-with-macros-and-expansions)
 +
 +Useful Rustc dev guide links:
 +- [Stages of compilation](https://rustc-dev-guide.rust-lang.org/compiler-src.html#the-main-stages-of-compilation)
 +- [Diagnostic items](https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html)
 +- [Type checking](https://rustc-dev-guide.rust-lang.org/type-checking.html)
 +- [Ty module](https://rustc-dev-guide.rust-lang.org/ty.html)
 +
 +## Retrieving the type of an expression
 +
 +Sometimes you may want to retrieve the type `Ty` of an expression `Expr`, for example to answer following questions:
 +
 +- which type does this expression correspond to (using its [`TyKind`][TyKind])?
 +- is it a sized type?
 +- is it a primitive type?
 +- does it implement a trait?
 +
 +This operation is performed using the [`expr_ty()`][expr_ty] method from the [`TypeckResults`][TypeckResults] struct,
 +that gives you access to the underlying structure [`Ty`][Ty].
 +
 +Example of use:
 +```rust
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        // Get type of `expr`
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        // Match its kind to enter its type
 +        match ty.kind {
 +            ty::Adt(adt_def, _) if adt_def.is_struct() => println!("Our `expr` is a struct!"),
 +            _ => ()
 +        }
 +    }
 +}
 +```
 +
 +Similarly in [`TypeckResults`][TypeckResults] methods, you have the [`pat_ty()`][pat_ty] method
 +to retrieve a type from a pattern.
 +
 +Two noticeable items here:
 +- `cx` is the lint context [`LateContext`][LateContext]. The two most useful
 +  data structures in this context are `tcx` and the `TypeckResults` returned by
 +  `LateContext::typeck_results`, allowing us to jump to type definitions and
 +  other compilation stages such as HIR.
 +- `typeck_results`'s return value is [`TypeckResults`][TypeckResults] and is
 +  created by type checking step, it includes useful information such as types
 +  of expressions, ways to resolve methods and so on.
 +
 +## Checking if an expr is calling a specific method
 +
 +Starting with an `expr`, you can check whether it is calling a specific method `some_method`:
 +
 +```rust
 +impl<'tcx> LateLintPass<'tcx> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        // Check our expr is calling a method
 +        if let hir::ExprKind::MethodCall(path, _, [_self_arg, ..]) = &expr.kind
 +            // Check the name of this method is `some_method`
 +            && path.ident.name == sym!(some_method)
 +            // Optionally, check the type of the self argument.
 +            // - See "Checking for a specific type"
 +        {
 +                // ...
 +        }
 +    }
 +}
 +```
 +
 +## Checking for a specific type
 +
 +There are three ways to check if an expression type is a specific type we want to check for.
 +All of these methods only check for the base type, generic arguments have to be checked separately.
 +
 +```rust
 +use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 +use clippy_utils::{paths, match_def_path};
 +use rustc_span::symbol::sym;
 +use rustc_hir::LangItem;
 +
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        // Getting the expression type
 +        let ty = cx.typeck_results().expr_ty(expr);
 +
 +        // 1. Using diagnostic items
 +        // The last argument is the diagnostic item to check for
 +        if is_type_diagnostic_item(cx, ty, sym::Option) {
 +            // The type is an `Option`
 +        }
 +
 +        // 2. Using lang items
 +        if is_type_lang_item(cx, ty, LangItem::RangeFull) {
 +            // The type is a full range like `.drain(..)`
 +        }
 +
 +        // 3. Using the type path
 +        // This method should be avoided if possible
 +        if match_def_path(cx, def_id, &paths::RESULT) {
 +            // The type is a `core::result::Result`
 +        }
 +    }
 +}
 +```
 +
 +Prefer using diagnostic items and lang items where possible.
 +
 +## Checking if a type implements a specific trait
 +
 +There are three ways to do this, depending on if the target trait has a diagnostic item, lang item or neither.
 +
 +```rust
 +use clippy_utils::{implements_trait, is_trait_method, match_trait_method, paths};
 +use rustc_span::symbol::sym;
 +
 +impl LateLintPass<'_> for MyStructLint {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        // 1. Using diagnostic items with the expression
 +        // we use `is_trait_method` function from Clippy's utils
 +        if is_trait_method(cx, expr, sym::Iterator) {
 +            // method call in `expr` belongs to `Iterator` trait
 +        }
 +
 +        // 2. Using lang items with the expression type
 +        let ty = cx.typeck_results().expr_ty(expr);
 +        if cx.tcx.lang_items()
 +            // we are looking for the `DefId` of `Drop` trait in lang items
 +            .drop_trait()
 +            // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
 +                // `expr` implements `Drop` trait
 +            }
 +
 +        // 3. Using the type path with the expression
 +        // we use `match_trait_method` function from Clippy's utils
 +        // (This method should be avoided if possible)
 +        if match_trait_method(cx, expr, &paths::INTO) {
 +            // `expr` implements `Into` trait
 +        }
 +    }
 +}
 +```
 +
 +> Prefer using diagnostic and lang items, if the target trait has one.
 +
 +We access lang items through the type context `tcx`. `tcx` is of type [`TyCtxt`][TyCtxt] and is defined in the `rustc_middle` crate.
 +A list of defined paths for Clippy can be found in [paths.rs][paths]
 +
 +## Checking if a type defines a specific method
 +
 +To check if our type defines a method called `some_method`:
 +
 +```rust
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::return_ty;
 +
 +impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
 +        // Check if item is a method/function
 +        if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
 +            // Check the method is named `some_method`
 +            && impl_item.ident.name == sym!(some_method)
 +            // We can also check it has a parameter `self`
 +            && signature.decl.implicit_self.has_implicit_self()
 +            // We can go further and even check if its return type is `String`
 +            && is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id), sym!(string_type))
 +        {
 +            // ...
 +        }
 +    }
 +}
 +```
 +
 +## Dealing with macros and expansions
 +
 +Keep in mind that macros are already expanded and desugaring is already applied
 +to the code representation that you are working with in Clippy. This unfortunately causes a lot of
 +false positives because macro expansions are "invisible" unless you actively check for them.
 +Generally speaking, code with macro expansions should just be ignored by Clippy because that code can be
 +dynamic in ways that are difficult or impossible to see.
 +Use the following functions to deal with macros:
 +
 +- `span.from_expansion()`: detects if a span is from macro expansion or desugaring.
 +  Checking this is a common first step in a lint.
 +
 +   ```rust
 +   if expr.span.from_expansion() {
 +       // just forget it
 +       return;
 +   }
 +   ```
 +
 +- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, which macro call expanded it.
 +   It is sometimes useful to check if the context of two spans are equal.
 +
 +   ```rust
 +   // expands to `1 + 0`, but don't lint
 +   1 + mac!()
 +   ```
 +   ```rust
 +   if left.span.ctxt() != right.span.ctxt() {
 +       // the coder most likely cannot modify this expression
 +       return;
 +   }
 +   ```
 +  Note: Code that is not from expansion is in the "root" context. So any spans where `from_expansion` returns `true` can
 +  be assumed to have the same context. And so just using `span.from_expansion()` is often good enough.
 +
 +
 +- `in_external_macro(span)`: detect if the given span is from a macro defined in a foreign crate.
 +   If you want the lint to work with macro-generated code, this is the next line of defense to avoid macros
 +   not defined in the current crate. It doesn't make sense to lint code that the coder can't change.
 +
 +   You may want to use it for example to not start linting in macros from other crates
 +
 +   ```rust
 +   #[macro_use]
 +   extern crate a_crate_with_macros;
 +
 +   // `foo` is defined in `a_crate_with_macros`
 +   foo!("bar");
 +
 +   // if we lint the `match` of `foo` call and test its span
 +   assert_eq!(in_external_macro(cx.sess(), match_span), true);
 +   ```
 +
 +- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it
 +
 +One thing `SpanContext` is useful for is to check if two spans are in the same context. For example,
 +in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some
 +expression with a different context from `a`.
 +
 +   ```rust
 +   macro_rules! m {
 +       ($a:expr, $b:expr) => {
 +           if $a.is_some() {
 +               $b;
 +           }
 +       }
 +   }
 +
 +   let x: Option<u32> = Some(42);
 +   m!(x, x.unwrap());
 +
 +   // These spans are not from the same context
 +   // x.is_some() is from inside the macro
 +   // x.unwrap() is from outside the macro
 +   assert_eq!(x_is_some_span.ctxt(), x_unwrap_span.ctxt());
 +   ```
 +
 +[Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html
 +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html
 +[TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html
 +[expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty
 +[LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html
 +[TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html
 +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty
 +[paths]: ../clippy_utils/src/paths.rs
index e63f65ce2f75dfd142652d15fe90683b149090a1,0000000000000000000000000000000000000000..504d58b5197a105b4da50e5e3c43149dd29be060
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,24 @@@
- clap = "2.33"
 +[package]
 +name = "lintcheck"
 +version = "0.0.1"
 +description = "tool to monitor impact of changes in Clippys lints on a part of the ecosystem"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +categories = ["development-tools"]
 +edition = "2021"
 +publish = false
 +
 +[dependencies]
 +cargo_metadata = "0.14"
++clap = "3.1"
 +flate2 = "1.0"
 +rayon = "1.5.1"
 +serde = { version = "1.0", features = ["derive"] }
 +tar = "0.4"
 +toml = "0.5"
 +ureq = "2.2"
 +walkdir = "2.3"
 +
 +[features]
 +deny-warnings = []
index de32b48436019c5a2d774c1181cc554dda24ba99,0000000000000000000000000000000000000000..a6f93d2a1c0a62177f8b55a59c4a2b0ae549e18f
mode 100644,000000..100644
--- /dev/null
@@@ -1,140 -1,0 +1,140 @@@
- use clap::{App, Arg, ArgMatches};
++use clap::{Arg, ArgMatches, Command};
 +use std::env;
 +use std::path::PathBuf;
 +
- fn get_clap_config<'a>() -> ArgMatches<'a> {
-     App::new("lintcheck")
++fn get_clap_config() -> ArgMatches {
++    Command::new("lintcheck")
 +        .about("run clippy on a set of crates and check output")
 +        .arg(
-             Arg::with_name("only")
++            Arg::new("only")
 +                .takes_value(true)
 +                .value_name("CRATE")
 +                .long("only")
 +                .help("Only process a single crate of the list"),
 +        )
 +        .arg(
-             Arg::with_name("crates-toml")
++            Arg::new("crates-toml")
 +                .takes_value(true)
 +                .value_name("CRATES-SOURCES-TOML-PATH")
 +                .long("crates-toml")
 +                .help("Set the path for a crates.toml where lintcheck should read the sources from"),
 +        )
 +        .arg(
-             Arg::with_name("threads")
++            Arg::new("threads")
 +                .takes_value(true)
 +                .value_name("N")
-                 .short("j")
++                .short('j')
 +                .long("jobs")
 +                .help("Number of threads to use, 0 automatic choice"),
 +        )
 +        .arg(
-             Arg::with_name("fix")
++            Arg::new("fix")
 +                .long("--fix")
 +                .help("Runs cargo clippy --fix and checks if all suggestions apply"),
 +        )
 +        .arg(
-             Arg::with_name("filter")
++            Arg::new("filter")
 +                .long("--filter")
 +                .takes_value(true)
-                 .multiple(true)
++                .multiple_occurrences(true)
 +                .value_name("clippy_lint_name")
 +                .help("Apply a filter to only collect specified lints, this also overrides `allow` attributes"),
 +        )
 +        .arg(
-             Arg::with_name("markdown")
++            Arg::new("markdown")
 +                .long("--markdown")
 +                .help("Change the reports table to use markdown links"),
 +        )
 +        .get_matches()
 +}
 +
 +#[derive(Debug)]
 +pub(crate) struct LintcheckConfig {
 +    /// max number of jobs to spawn (default 1)
 +    pub max_jobs: usize,
 +    /// we read the sources to check from here
 +    pub sources_toml_path: PathBuf,
 +    /// we save the clippy lint results here
 +    pub lintcheck_results_path: PathBuf,
 +    /// Check only a specified package
 +    pub only: Option<String>,
 +    /// whether to just run --fix and not collect all the warnings
 +    pub fix: bool,
 +    /// A list of lints that this lintcheck run should focus on
 +    pub lint_filter: Vec<String>,
 +    /// Indicate if the output should support markdown syntax
 +    pub markdown: bool,
 +}
 +
 +impl LintcheckConfig {
 +    pub fn new() -> Self {
 +        let clap_config = get_clap_config();
 +
 +        // first, check if we got anything passed via the LINTCHECK_TOML env var,
 +        // if not, ask clap if we got any value for --crates-toml  <foo>
 +        // if not, use the default "lintcheck/lintcheck_crates.toml"
 +        let sources_toml = env::var("LINTCHECK_TOML").unwrap_or_else(|_| {
 +            clap_config
 +                .value_of("crates-toml")
 +                .clone()
 +                .unwrap_or("lintcheck/lintcheck_crates.toml")
 +                .to_string()
 +        });
 +
 +        let markdown = clap_config.is_present("markdown");
 +        let sources_toml_path = PathBuf::from(sources_toml);
 +
 +        // for the path where we save the lint results, get the filename without extension (so for
 +        // wasd.toml, use "wasd"...)
 +        let filename: PathBuf = sources_toml_path.file_stem().unwrap().into();
 +        let lintcheck_results_path = PathBuf::from(format!(
 +            "lintcheck-logs/{}_logs.{}",
 +            filename.display(),
 +            if markdown { "md" } else { "txt" }
 +        ));
 +
 +        // look at the --threads arg, if 0 is passed, ask rayon rayon how many threads it would spawn and
 +        // use half of that for the physical core count
 +        // by default use a single thread
 +        let max_jobs = match clap_config.value_of("threads") {
 +            Some(threads) => {
 +                let threads: usize = threads
 +                    .parse()
 +                    .unwrap_or_else(|_| panic!("Failed to parse '{}' to a digit", threads));
 +                if threads == 0 {
 +                    // automatic choice
 +                    // Rayon seems to return thread count so half that for core count
 +                    (rayon::current_num_threads() / 2) as usize
 +                } else {
 +                    threads
 +                }
 +            },
 +            // no -j passed, use a single thread
 +            None => 1,
 +        };
 +
 +        let lint_filter: Vec<String> = clap_config
 +            .values_of("filter")
 +            .map(|iter| {
 +                iter.map(|lint_name| {
 +                    let mut filter = lint_name.replace('_', "-");
 +                    if !filter.starts_with("clippy::") {
 +                        filter.insert_str(0, "clippy::");
 +                    }
 +                    filter
 +                })
 +                .collect()
 +            })
 +            .unwrap_or_default();
 +
 +        LintcheckConfig {
 +            max_jobs,
 +            sources_toml_path,
 +            lintcheck_results_path,
 +            only: clap_config.value_of("only").map(String::from),
 +            fix: clap_config.is_present("fix"),
 +            lint_filter,
 +            markdown,
 +        }
 +    }
 +}
index 997e7ba9382b2104c1f789de87dbfd5fedb67d8e,0000000000000000000000000000000000000000..2386a751f04f6cefdf3aaa055d4d857093fbba09
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-05-19"
 +[toolchain]
++channel = "nightly-2022-06-04"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index c9710e3db8e8d264e956dc8cb8e2eb04f07911dd,0000000000000000000000000000000000000000..7d2198357232453732b111cd16f5983f3999e049
mode 100644,000000..100644
--- /dev/null
@@@ -1,362 -1,0 +1,451 @@@
-         help: Try adding to dev-dependencies in Cargo.toml",
-         not_found
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
++#![feature(is_sorted)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use compiletest_rs as compiletest;
 +use compiletest_rs::common::Mode as TestMode;
 +
 +use std::collections::HashMap;
 +use std::env::{self, remove_var, set_var, var_os};
 +use std::ffi::{OsStr, OsString};
 +use std::fs;
 +use std::io;
 +use std::lazy::SyncLazy;
 +use std::path::{Path, PathBuf};
 +use test_utils::IS_RUSTC_TEST_SUITE;
 +
 +mod test_utils;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 +
 +/// All crates used in UI tests are listed here
 +static TEST_DEPENDENCIES: &[&str] = &[
++    "clap",
 +    "clippy_utils",
 +    "derive_new",
 +    "futures",
 +    "if_chain",
 +    "itertools",
 +    "quote",
 +    "regex",
 +    "serde",
 +    "serde_derive",
 +    "syn",
 +    "tokio",
 +    "parking_lot",
 +    "rustc_semver",
 +];
 +
 +// Test dependencies may need an `extern crate` here to ensure that they show up
 +// in the depinfo file (otherwise cargo thinks they are unused)
 +#[allow(unused_extern_crates)]
++extern crate clap;
++#[allow(unused_extern_crates)]
 +extern crate clippy_utils;
 +#[allow(unused_extern_crates)]
 +extern crate derive_new;
 +#[allow(unused_extern_crates)]
 +extern crate futures;
 +#[allow(unused_extern_crates)]
 +extern crate if_chain;
 +#[allow(unused_extern_crates)]
 +extern crate itertools;
 +#[allow(unused_extern_crates)]
 +extern crate parking_lot;
 +#[allow(unused_extern_crates)]
 +extern crate quote;
 +#[allow(unused_extern_crates)]
 +extern crate rustc_semver;
 +#[allow(unused_extern_crates)]
 +extern crate syn;
 +#[allow(unused_extern_crates)]
 +extern crate tokio;
 +
 +/// Produces a string with an `--extern` flag for all UI test crate
 +/// dependencies.
 +///
 +/// The dependency files are located by parsing the depinfo file for this test
 +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
 +/// dependencies must be added to Cargo.toml at the project root. Test
 +/// dependencies that are not *directly* used by this test module require an
 +/// `extern crate` declaration.
 +static EXTERN_FLAGS: SyncLazy<String> = SyncLazy::new(|| {
 +    let current_exe_depinfo = {
 +        let mut path = env::current_exe().unwrap();
 +        path.set_extension("d");
 +        fs::read_to_string(path).unwrap()
 +    };
 +    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
 +    for line in current_exe_depinfo.lines() {
 +        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
 +        let parse_name_path = || {
 +            if line.starts_with(char::is_whitespace) {
 +                return None;
 +            }
 +            let path_str = line.strip_suffix(':')?;
 +            let path = Path::new(path_str);
 +            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
 +                return None;
 +            }
 +            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
 +            // the "lib" prefix is not present for dll files
 +            let name = name.strip_prefix("lib").unwrap_or(name);
 +            Some((name, path_str))
 +        };
 +        if let Some((name, path)) = parse_name_path() {
 +            if TEST_DEPENDENCIES.contains(&name) {
 +                // A dependency may be listed twice if it is available in sysroot,
 +                // and the sysroot dependencies are listed first. As of the writing,
 +                // this only seems to apply to if_chain.
 +                crates.insert(name, path);
 +            }
 +        }
 +    }
 +    let not_found: Vec<&str> = TEST_DEPENDENCIES
 +        .iter()
 +        .copied()
 +        .filter(|n| !crates.contains_key(n))
 +        .collect();
 +    assert!(
 +        not_found.is_empty(),
 +        "dependencies not found in depinfo: {:?}\n\
 +        help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
-     let config = base_config("ui");
++        help: Try adding to dev-dependencies in Cargo.toml\n\
++        help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
++        not_found,
 +    );
 +    crates
 +        .into_iter()
 +        .map(|(name, path)| format!(" --extern {}={}", name, path))
 +        .collect()
 +});
 +
 +fn base_config(test_dir: &str) -> compiletest::Config {
 +    let mut config = compiletest::Config {
 +        edition: Some("2021".into()),
 +        mode: TestMode::Ui,
 +        ..compiletest::Config::default()
 +    };
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(ToString::to_string).collect();
 +    }
 +
 +    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
 +        let path = PathBuf::from(path);
 +        config.run_lib_path = path.clone();
 +        config.compile_lib_path = path;
 +    }
 +    let current_exe_path = env::current_exe().unwrap();
 +    let deps_path = current_exe_path.parent().unwrap();
 +    let profile_path = deps_path.parent().unwrap();
 +
 +    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
 +    // This is valuable because a) it allows us to monitor what external dependencies are used
 +    // and b) it ensures that conflicting rlibs are resolved properly.
 +    let host_libs = option_env!("HOST_LIBS")
 +        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
 +        .unwrap_or_default();
 +    config.target_rustcflags = Some(format!(
 +        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
 +        deps_path.display(),
 +        host_libs,
 +        &*EXTERN_FLAGS,
 +    ));
 +
 +    config.src_base = Path::new("tests").join(test_dir);
 +    config.build_base = profile_path.join("test").join(test_dir);
 +    config.rustc_path = profile_path.join(if cfg!(windows) {
 +        "clippy-driver.exe"
 +    } else {
 +        "clippy-driver"
 +    });
 +    config
 +}
 +
 +fn run_ui() {
++    let mut config = base_config("ui");
++    config.rustfix_coverage = true;
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
 +    let _threads = VarGuard::set(
 +        "RUST_TEST_THREADS",
 +        // if RUST_TEST_THREADS is set, adhere to it, otherwise override it
 +        env::var("RUST_TEST_THREADS").unwrap_or_else(|_| {
 +            std::thread::available_parallelism()
 +                .map_or(1, std::num::NonZeroUsize::get)
 +                .to_string()
 +        }),
 +    );
 +    compiletest::run_tests(&config);
++    check_rustfix_coverage();
 +}
 +
 +fn run_internal_tests() {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    let config = base_config("ui-internal");
 +    compiletest::run_tests(&config);
 +}
 +
 +fn run_ui_toml() {
 +    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +            let dir_path = dir.path();
 +            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
 +            for file in fs::read_dir(&dir_path)? {
 +                let file = file?;
 +                let file_path = file.path();
 +                if file.file_type()?.is_dir() {
 +                    continue;
 +                }
 +                if file_path.extension() != Some(OsStr::new("rs")) {
 +                    continue;
 +                }
 +                let paths = compiletest::common::TestPaths {
 +                    file: file_path,
 +                    base: config.src_base.clone(),
 +                    relative_dir: dir_path.file_name().unwrap().into(),
 +                };
 +                let test_name = compiletest::make_test_name(config, &paths);
 +                let index = tests
 +                    .iter()
 +                    .position(|test| test.desc.name == test_name)
 +                    .expect("The test should be in there");
 +                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    let mut config = base_config("ui-toml");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let res = run_tests(&config, tests);
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn run_ui_cargo() {
 +    fn run_tests(
 +        config: &compiletest::Config,
 +        filters: &[String],
 +        mut tests: Vec<tester::TestDescAndFn>,
 +    ) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +
 +            // Use the filter if provided
 +            let dir_path = dir.path();
 +            for filter in filters {
 +                if !dir_path.ends_with(filter) {
 +                    continue;
 +                }
 +            }
 +
 +            for case in fs::read_dir(&dir_path)? {
 +                let case = case?;
 +                if !case.file_type()?.is_dir() {
 +                    continue;
 +                }
 +
 +                let src_path = case.path().join("src");
 +
 +                // When switching between branches, if the previous branch had a test
 +                // that the current branch does not have, the directory is not removed
 +                // because an ignored Cargo.lock file exists.
 +                if !src_path.exists() {
 +                    continue;
 +                }
 +
 +                env::set_current_dir(&src_path)?;
 +                for file in fs::read_dir(&src_path)? {
 +                    let file = file?;
 +                    if file.file_type()?.is_dir() {
 +                        continue;
 +                    }
 +
 +                    // Search for the main file to avoid running a test for each file in the project
 +                    let file_path = file.path();
 +                    match file_path.file_name().and_then(OsStr::to_str) {
 +                        Some("main.rs") => {},
 +                        _ => continue,
 +                    }
 +                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
 +                    let paths = compiletest::common::TestPaths {
 +                        file: file_path,
 +                        base: config.src_base.clone(),
 +                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
 +                    };
 +                    let test_name = compiletest::make_test_name(config, &paths);
 +                    let index = tests
 +                        .iter()
 +                        .position(|test| test.desc.name == test_name)
 +                        .expect("The test should be in there");
 +                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +                }
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +
 +    let mut config = base_config("ui-cargo");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let current_dir = env::current_dir().unwrap();
 +    let res = run_tests(&config, &config.filters, tests);
 +    env::set_current_dir(current_dir).unwrap();
 +
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +#[test]
 +fn compile_test() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    run_ui();
 +    run_ui_toml();
 +    run_ui_cargo();
 +    run_internal_tests();
 +}
 +
++const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
++    "assign_ops2.rs",
++    "borrow_deref_ref_unfixable.rs",
++    "cast_size_32bit.rs",
++    "char_lit_as_u8.rs",
++    "cmp_owned/without_suggestion.rs",
++    "dbg_macro.rs",
++    "deref_addrof_double_trigger.rs",
++    "doc/unbalanced_ticks.rs",
++    "eprint_with_newline.rs",
++    "explicit_counter_loop.rs",
++    "iter_skip_next_unfixable.rs",
++    "let_and_return.rs",
++    "literals.rs",
++    "map_flatten.rs",
++    "map_unwrap_or.rs",
++    "match_bool.rs",
++    "mem_replace_macro.rs",
++    "needless_arbitrary_self_type_unfixable.rs",
++    "needless_borrow_pat.rs",
++    "needless_for_each_unfixable.rs",
++    "nonminimal_bool.rs",
++    "print_literal.rs",
++    "print_with_newline.rs",
++    "redundant_static_lifetimes_multiple.rs",
++    "ref_binding_to_reference.rs",
++    "repl_uninit.rs",
++    "result_map_unit_fn_unfixable.rs",
++    "search_is_some.rs",
++    "single_component_path_imports_nested_first.rs",
++    "string_add.rs",
++    "toplevel_ref_arg_non_rustfix.rs",
++    "unit_arg.rs",
++    "unnecessary_clone.rs",
++    "unnecessary_lazy_eval_unfixable.rs",
++    "write_literal.rs",
++    "write_literal_2.rs",
++    "write_with_newline.rs",
++];
++
++fn check_rustfix_coverage() {
++    let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt");
++
++    if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) {
++        assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new));
++
++        for rs_path in missing_coverage_contents.lines() {
++            if Path::new(rs_path).starts_with("tests/ui/crashes") {
++                continue;
++            }
++            let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap();
++            assert!(
++                RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
++                    .binary_search_by_key(&filename, Path::new)
++                    .is_ok(),
++                "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
++                Please either add `// run-rustfix` at the top of the file or add the file to \
++                `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
++                rs_path,
++            );
++        }
++    }
++}
++
++#[test]
++fn rustfix_coverage_known_exceptions_accuracy() {
++    for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS {
++        let rs_path = Path::new("tests/ui").join(filename);
++        assert!(
++            rs_path.exists(),
++            "`{}` does not exists",
++            rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
++        );
++        let fixed_path = rs_path.with_extension("fixed");
++        assert!(
++            !fixed_path.exists(),
++            "`{}` exists",
++            fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
++        );
++    }
++}
++
 +/// Restores an env var on drop
 +#[must_use]
 +struct VarGuard {
 +    key: &'static str,
 +    value: Option<OsString>,
 +}
 +
 +impl VarGuard {
 +    fn set(key: &'static str, val: impl AsRef<OsStr>) -> Self {
 +        let value = var_os(key);
 +        set_var(key, val);
 +        Self { key, value }
 +    }
 +}
 +
 +impl Drop for VarGuard {
 +    fn drop(&mut self) {
 +        match self.value.as_deref() {
 +            None => remove_var(self.key),
 +            Some(value) => set_var(self.key, value),
 +        }
 +    }
 +}
index 4f5336663a8f72d9119429fc5b7ae7a277ff6c1a,0000000000000000000000000000000000000000..6033d06e4f637b15bcf58282095d4c11866b2f01
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,21 @@@
 +// run-rustfix
 +#![feature(rustc_private)]
 +#![deny(clippy::internal)]
 +#![allow(
++    clippy::borrow_deref_ref,
 +    clippy::unnecessary_operation,
 +    unused_must_use,
 +    clippy::missing_clippy_version_attribute
 +)]
 +
 +extern crate rustc_span;
 +
 +use rustc_span::symbol::{Ident, Symbol};
 +
 +fn main() {
 +    Symbol::intern("foo") == rustc_span::sym::clippy;
 +    Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower;
 +    Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper;
 +    Ident::empty().name == rustc_span::sym::clippy;
 +    rustc_span::sym::clippy == Ident::empty().name;
 +}
index 894aa1d3bc6ea64932a7d05d51d89352e1c52a86,0000000000000000000000000000000000000000..1bb5d55f0b60cb591a388f7edc300546beeb326d
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,21 @@@
 +// run-rustfix
 +#![feature(rustc_private)]
 +#![deny(clippy::internal)]
 +#![allow(
++    clippy::borrow_deref_ref,
 +    clippy::unnecessary_operation,
 +    unused_must_use,
 +    clippy::missing_clippy_version_attribute
 +)]
 +
 +extern crate rustc_span;
 +
 +use rustc_span::symbol::{Ident, Symbol};
 +
 +fn main() {
 +    Symbol::intern("foo").as_str() == "clippy";
 +    Symbol::intern("foo").to_string() == "self";
 +    Symbol::intern("foo").to_ident_string() != "Self";
 +    &*Ident::empty().as_str() == "clippy";
 +    "clippy" == Ident::empty().to_string();
 +}
index 75367bf4bc51989a43d25b4c35f2114dff83fa7a,0000000000000000000000000000000000000000..a1f507f331d26c5cb7545fd98817c5a575495b8c
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,39 @@@
-   --> $DIR/unnecessary_symbol_str.rs:15:5
 +error: unnecessary `Symbol` to string conversion
-   --> $DIR/unnecessary_symbol_str.rs:16:5
++  --> $DIR/unnecessary_symbol_str.rs:16:5
 +   |
 +LL |     Symbol::intern("foo").as_str() == "clippy";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/unnecessary_symbol_str.rs:3:9
 +   |
 +LL | #![deny(clippy::internal)]
 +   |         ^^^^^^^^^^^^^^^^
 +   = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
 +
 +error: unnecessary `Symbol` to string conversion
-   --> $DIR/unnecessary_symbol_str.rs:17:5
++  --> $DIR/unnecessary_symbol_str.rs:17:5
 +   |
 +LL |     Symbol::intern("foo").to_string() == "self";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
 +
 +error: unnecessary `Symbol` to string conversion
-   --> $DIR/unnecessary_symbol_str.rs:18:5
++  --> $DIR/unnecessary_symbol_str.rs:18:5
 +   |
 +LL |     Symbol::intern("foo").to_ident_string() != "Self";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
 +
 +error: unnecessary `Symbol` to string conversion
-   --> $DIR/unnecessary_symbol_str.rs:19:5
++  --> $DIR/unnecessary_symbol_str.rs:19:5
 +   |
 +LL |     &*Ident::empty().as_str() == "clippy";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 +
 +error: unnecessary `Symbol` to string conversion
++  --> $DIR/unnecessary_symbol_str.rs:20:5
 +   |
 +LL |     "clippy" == Ident::empty().to_string();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4296655a040fa23a278eb5a1055ddcbb85fb8c71
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,1 @@@
++allow-dbg-in-tests = true
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5d9ce18f631b6b947fe0b6a9f991436b81295317
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++// compile-flags: --test
++#![warn(clippy::dbg_macro)]
++
++fn foo(n: u32) -> u32 {
++    if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
++}
++
++fn factorial(n: u32) -> u32 {
++    if dbg!(n <= 1) {
++        dbg!(1)
++    } else {
++        dbg!(n * factorial(n - 1))
++    }
++}
++
++fn main() {
++    dbg!(42);
++    dbg!(dbg!(dbg!(42)));
++    foo(3) + dbg!(factorial(4));
++    dbg!(1, 2, dbg!(3, 4));
++    dbg!(1, 2, 3, 4, 5);
++}
++
++#[test]
++pub fn issue8481() {
++    dbg!(1);
++}
++
++#[cfg(test)]
++fn foo2() {
++    dbg!(1);
++}
++
++#[cfg(test)]
++mod mod1 {
++    fn func() {
++        dbg!(1);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..46efb86dcfc5f5c1384b81628302619c396d53cb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,102 @@@
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:5:22
++   |
++LL |     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
++   |                      ^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::dbg-macro` implied by `-D warnings`
++help: ensure to avoid having uses of it in version control
++   |
++LL |     if let Some(n) = n.checked_sub(4) { n } else { n }
++   |                      ~~~~~~~~~~~~~~~~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:9:8
++   |
++LL |     if dbg!(n <= 1) {
++   |        ^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     if n <= 1 {
++   |        ~~~~~~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:10:9
++   |
++LL |         dbg!(1)
++   |         ^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |         1
++   |
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:12:9
++   |
++LL |         dbg!(n * factorial(n - 1))
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |         n * factorial(n - 1)
++   |
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:17:5
++   |
++LL |     dbg!(42);
++   |     ^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     42;
++   |     ~~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:18:5
++   |
++LL |     dbg!(dbg!(dbg!(42)));
++   |     ^^^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     dbg!(dbg!(42));
++   |     ~~~~~~~~~~~~~~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:19:14
++   |
++LL |     foo(3) + dbg!(factorial(4));
++   |              ^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     foo(3) + factorial(4);
++   |              ~~~~~~~~~~~~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:20:5
++   |
++LL |     dbg!(1, 2, dbg!(3, 4));
++   |     ^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     (1, 2, dbg!(3, 4));
++   |     ~~~~~~~~~~~~~~~~~~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:21:5
++   |
++LL |     dbg!(1, 2, 3, 4, 5);
++   |     ^^^^^^^^^^^^^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     (1, 2, 3, 4, 5);
++   |     ~~~~~~~~~~~~~~~
++
++error: aborting due to 9 previous errors
++
index 92838aa57dfdaa9e1e93694bee9d17d3c49dd762,0000000000000000000000000000000000000000..1d87fd91a2532a179fc8adb499558e6fa7e3ee29
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,44 @@@
 +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-unwrap-in-tests
 +           allowed-scripts
 +           array-size-threshold
 +           avoid-breaking-exported-api
 +           await-holding-invalid-types
 +           blacklisted-names
 +           cargo-ignore-publish
 +           cognitive-complexity-threshold
 +           cyclomatic-complexity-threshold
 +           disallowed-methods
 +           disallowed-types
 +           doc-valid-idents
 +           enable-raw-pointer-heuristic-for-send
 +           enforced-import-renames
 +           enum-variant-name-threshold
 +           enum-variant-size-threshold
 +           literal-representation-threshold
 +           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 74d0d7c2650dd09db2575527b1f8cd4e16510849,0000000000000000000000000000000000000000..0e82fb20e45585817de8323ec873f8ad73623d1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,73 @@@
- #![allow(unused_mut, clippy::from_iter_instead_of_collect)]
 +// 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();
 +    }
 +}
 +
 +#[test]
 +fn test() {
 +    let boxed_slice: Box<[u8]> = Box::new([0, 1, 2, 3]);
 +    let _ = boxed_slice.get(1).unwrap();
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..39f8f0c29495bef4f5489c039d609de9193a733b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++// run-rustfix
++// edition:2018
++
++#![feature(custom_inner_attributes)]
++#![feature(exclusive_range_pattern)]
++#![feature(stmt_expr_attributes)]
++#![warn(clippy::almost_complete_letter_range)]
++#![allow(ellipsis_inclusive_range_patterns)]
++
++macro_rules! a {
++    () => {
++        'a'
++    };
++}
++
++fn main() {
++    #[rustfmt::skip]
++    {
++        let _ = ('a') ..='z';
++        let _ = 'A' ..= ('Z');
++    }
++
++    let _ = 'b'..'z';
++    let _ = 'B'..'Z';
++
++    let _ = (b'a')..=(b'z');
++    let _ = b'A'..=b'Z';
++
++    let _ = b'b'..b'z';
++    let _ = b'B'..b'Z';
++
++    let _ = a!()..='z';
++
++    let _ = match 0u8 {
++        b'a'..=b'z' if true => 1,
++        b'A'..=b'Z' if true => 2,
++        b'b'..b'z' => 3,
++        b'B'..b'Z' => 4,
++        _ => 5,
++    };
++
++    let _ = match 'x' {
++        'a'..='z' if true => 1,
++        'A'..='Z' if true => 2,
++        'b'..'z' => 3,
++        'B'..'Z' => 4,
++        _ => 5,
++    };
++}
++
++fn _under_msrv() {
++    #![clippy::msrv = "1.25"]
++    let _ = match 'a' {
++        'a'...'z' => 1,
++        _ => 2,
++    };
++}
++
++fn _meets_msrv() {
++    #![clippy::msrv = "1.26"]
++    let _ = 'a'..='z';
++    let _ = match 'a' {
++        'a'..='z' => 1,
++        _ => 2,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3dc021992576079ec03384cf6a8b1dbfbabeff1d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,66 @@@
++// run-rustfix
++// edition:2018
++
++#![feature(custom_inner_attributes)]
++#![feature(exclusive_range_pattern)]
++#![feature(stmt_expr_attributes)]
++#![warn(clippy::almost_complete_letter_range)]
++#![allow(ellipsis_inclusive_range_patterns)]
++
++macro_rules! a {
++    () => {
++        'a'
++    };
++}
++
++fn main() {
++    #[rustfmt::skip]
++    {
++        let _ = ('a') ..'z';
++        let _ = 'A' .. ('Z');
++    }
++
++    let _ = 'b'..'z';
++    let _ = 'B'..'Z';
++
++    let _ = (b'a')..(b'z');
++    let _ = b'A'..b'Z';
++
++    let _ = b'b'..b'z';
++    let _ = b'B'..b'Z';
++
++    let _ = a!()..'z';
++
++    let _ = match 0u8 {
++        b'a'..b'z' if true => 1,
++        b'A'..b'Z' if true => 2,
++        b'b'..b'z' => 3,
++        b'B'..b'Z' => 4,
++        _ => 5,
++    };
++
++    let _ = match 'x' {
++        'a'..'z' if true => 1,
++        'A'..'Z' if true => 2,
++        'b'..'z' => 3,
++        'B'..'Z' => 4,
++        _ => 5,
++    };
++}
++
++fn _under_msrv() {
++    #![clippy::msrv = "1.25"]
++    let _ = match 'a' {
++        'a'..'z' => 1,
++        _ => 2,
++    };
++}
++
++fn _meets_msrv() {
++    #![clippy::msrv = "1.26"]
++    let _ = 'a'..'z';
++    let _ = match 'a' {
++        'a'..'z' => 1,
++        _ => 2,
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..74980ec1a923fc5d39abd8dd619e93e60f1ab365
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:19:17
++   |
++LL |         let _ = ('a') ..'z';
++   |                 ^^^^^^--^^^
++   |                       |
++   |                       help: use an inclusive range: `..=`
++   |
++   = note: `-D clippy::almost-complete-letter-range` implied by `-D warnings`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:20:17
++   |
++LL |         let _ = 'A' .. ('Z');
++   |                 ^^^^--^^^^^^
++   |                     |
++   |                     help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:26:13
++   |
++LL |     let _ = (b'a')..(b'z');
++   |             ^^^^^^--^^^^^^
++   |                   |
++   |                   help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:27:13
++   |
++LL |     let _ = b'A'..b'Z';
++   |             ^^^^--^^^^
++   |                 |
++   |                 help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:32:13
++   |
++LL |     let _ = a!()..'z';
++   |             ^^^^--^^^
++   |                 |
++   |                 help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:35:9
++   |
++LL |         b'a'..b'z' if true => 1,
++   |         ^^^^--^^^^
++   |             |
++   |             help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:36:9
++   |
++LL |         b'A'..b'Z' if true => 2,
++   |         ^^^^--^^^^
++   |             |
++   |             help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:43:9
++   |
++LL |         'a'..'z' if true => 1,
++   |         ^^^--^^^
++   |            |
++   |            help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:44:9
++   |
++LL |         'A'..'Z' if true => 2,
++   |         ^^^--^^^
++   |            |
++   |            help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:54:9
++   |
++LL |         'a'..'z' => 1,
++   |         ^^^--^^^
++   |            |
++   |            help: use an inclusive range: `...`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:61:13
++   |
++LL |     let _ = 'a'..'z';
++   |             ^^^--^^^
++   |                |
++   |                help: use an inclusive range: `..=`
++
++error: almost complete ascii letter range
++  --> $DIR/almost_complete_letter_range.rs:63:9
++   |
++LL |         'a'..'z' => 1,
++   |         ^^^--^^^
++   |            |
++   |            help: use an inclusive range: `..=`
++
++error: aborting due to 12 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..948f6d8e6b10b15266fb0b7fb49b0dc523d8ebf5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-rustfix
++
++#![warn(clippy::as_underscore)]
++
++fn foo(_n: usize) {}
++
++fn main() {
++    let n: u16 = 256;
++    foo(n as usize);
++
++    let n = 0_u128;
++    let _n: u8 = n as u8;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..97785ed08a8d0a53a9aa5b3949e49faf5f723103
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,13 @@@
++// run-rustfix
++
++#![warn(clippy::as_underscore)]
++
++fn foo(_n: usize) {}
++
++fn main() {
++    let n: u16 = 256;
++    foo(n as _);
++
++    let n = 0_u128;
++    let _n: u8 = n as _;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d7cd58d965acb8a334eca05a675304b395b9af9f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,20 @@@
++error: using `as _` conversion
++  --> $DIR/as_underscore.rs:9:9
++   |
++LL |     foo(n as _);
++   |         ^^^^^-
++   |              |
++   |              help: consider giving the type explicitly: `usize`
++   |
++   = note: `-D clippy::as-underscore` implied by `-D warnings`
++
++error: using `as _` conversion
++  --> $DIR/as_underscore.rs:12:18
++   |
++LL |     let _n: u8 = n as _;
++   |                  ^^^^^-
++   |                       |
++   |                       help: consider giving the type explicitly: `u8`
++
++error: aborting due to 2 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e1589843226c4582fb4112b081889ced6bc13578
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++// run-rustfix
++#![deny(clippy::bind_instead_of_map)]
++#![allow(clippy::blocks_in_if_conditions)]
++
++pub fn main() {
++    let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
++    let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) });
++
++    let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
++    let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) });
++
++    let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
++    let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) });
++
++    hard_example();
++    macro_example();
++}
++
++fn hard_example() {
++    Some("42").map(|s| {
++        if {
++            if s == "43" {
++                return 43;
++            }
++            s == "42"
++        } {
++            return 45;
++        }
++        match s.len() {
++            10 => 2,
++            20 => {
++                if foo() {
++                    return {
++                        if foo() {
++                            return 20;
++                        }
++                        println!("foo");
++                        3
++                    };
++                }
++                20
++            },
++            40 => 30,
++            _ => 1,
++        }
++    });
++}
++
++fn foo() -> bool {
++    true
++}
++
++macro_rules! m {
++    () => {
++        Some(10)
++    };
++}
++
++fn macro_example() {
++    let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) });
++    let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
++}
index 91d9d11e3c11056ced998350542ce7e9cf745a74,0000000000000000000000000000000000000000..49944403f6ddddf76a0b12137ef542f2ebc0f70c
mode 100644,000000..100644
--- /dev/null
@@@ -1,61 -1,0 +1,62 @@@
++// run-rustfix
 +#![deny(clippy::bind_instead_of_map)]
 +#![allow(clippy::blocks_in_if_conditions)]
 +
 +pub fn main() {
 +    let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) });
 +    let _ = Some("42").and_then(|s| if s.len() < 42 { None } else { Some(s.len()) });
 +
 +    let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) });
 +    let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Err(()) } else { Ok(s.len()) });
 +
 +    let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) });
 +    let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Ok(()) } else { Err(s.len()) });
 +
 +    hard_example();
 +    macro_example();
 +}
 +
 +fn hard_example() {
 +    Some("42").and_then(|s| {
 +        if {
 +            if s == "43" {
 +                return Some(43);
 +            }
 +            s == "42"
 +        } {
 +            return Some(45);
 +        }
 +        match s.len() {
 +            10 => Some(2),
 +            20 => {
 +                if foo() {
 +                    return {
 +                        if foo() {
 +                            return Some(20);
 +                        }
 +                        println!("foo");
 +                        Some(3)
 +                    };
 +                }
 +                Some(20)
 +            },
 +            40 => Some(30),
 +            _ => Some(1),
 +        }
 +    });
 +}
 +
 +fn foo() -> bool {
 +    true
 +}
 +
 +macro_rules! m {
 +    () => {
 +        Some(10)
 +    };
 +}
 +
 +fn macro_example() {
 +    let _ = Some("").and_then(|s| if s.len() == 20 { m!() } else { Some(20) });
 +    let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) });
 +}
index e4f605a4de3059ae15e3230b72f9dc143da2c70a,0000000000000000000000000000000000000000..f822b6f49fa35198906b9a5fe3b7bc7762ec16b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,73 @@@
-   --> $DIR/bind_instead_of_map_multipart.rs:5:13
 +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
-   --> $DIR/bind_instead_of_map_multipart.rs:1:9
++  --> $DIR/bind_instead_of_map_multipart.rs:6:13
 +   |
 +LL |     let _ = Some("42").and_then(|s| if s.len() < 42 { Some(0) } else { Some(s.len()) });
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: the lint level is defined here
-   --> $DIR/bind_instead_of_map_multipart.rs:8:13
++  --> $DIR/bind_instead_of_map_multipart.rs:2:9
 +   |
 +LL | #![deny(clippy::bind_instead_of_map)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: try this
 +   |
 +LL |     let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
 +   |                        ~~~                       ~          ~~~~~~~
 +
 +error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as `map(|x| y)`
-   --> $DIR/bind_instead_of_map_multipart.rs:11:13
++  --> $DIR/bind_instead_of_map_multipart.rs:9:13
 +   |
 +LL |     let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) });
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try this
 +   |
 +LL |     let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
 +   |                               ~~~                       ~          ~~~~~~~
 +
 +error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)`
-   --> $DIR/bind_instead_of_map_multipart.rs:19:5
++  --> $DIR/bind_instead_of_map_multipart.rs:12:13
 +   |
 +LL |     let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) });
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try this
 +   |
 +LL |     let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
 +   |                                ~~~~~~~                       ~~~~~~~~~~~~          ~~~~~~~
 +
 +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
-   --> $DIR/bind_instead_of_map_multipart.rs:60:13
++  --> $DIR/bind_instead_of_map_multipart.rs:20:5
 +   |
 +LL | /     Some("42").and_then(|s| {
 +LL | |         if {
 +LL | |             if s == "43" {
 +LL | |                 return Some(43);
 +...  |
 +LL | |         }
 +LL | |     });
 +   | |______^
 +   |
 +help: try this
 +   |
 +LL ~     Some("42").map(|s| {
 +LL |         if {
 +LL |             if s == "43" {
 +LL ~                 return 43;
 +LL |             }
 +LL |             s == "42"
 + ...
 +
 +error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`
++  --> $DIR/bind_instead_of_map_multipart.rs:61:13
 +   |
 +LL |     let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) });
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try this
 +   |
 +LL |     let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
 +   |                      ~~~                        ~~~~          ~~~~~~~~
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf4691c5bc97e1df47e9224ed81410895ccab254
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++
++fn main() {}
++
++mod should_lint {
++    fn one_help() {
++        let a = &12;
++        let b = a;
++
++        let b = &mut bar(&12);
++    }
++
++    fn bar(x: &u32) -> &u32 {
++        x
++    }
++}
++
++// this mod explains why we should not lint `&mut &* (&T)`
++mod should_not_lint1 {
++    fn foo(x: &mut &u32) {
++        *x = &1;
++    }
++
++    fn main() {
++        let mut x = &0;
++        foo(&mut &*x); // should not lint
++        assert_eq!(*x, 0);
++
++        foo(&mut x);
++        assert_eq!(*x, 1);
++    }
++}
++
++// similar to should_not_lint1
++mod should_not_lint2 {
++    struct S<'a> {
++        a: &'a u32,
++        b: u32,
++    }
++
++    fn main() {
++        let s = S { a: &1, b: 1 };
++        let x = &mut &*s.a;
++        *x = &2;
++    }
++}
++
++// this mod explains why we should not lint `& &* (&T)`
++mod false_negative {
++    fn foo() {
++        let x = &12;
++        let addr_x = &x as *const _ as usize;
++        let addr_y = &x as *const _ as usize; // assert ok
++        // let addr_y = &x as *const _ as usize; // assert fail
++        assert_ne!(addr_x, addr_y);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..28c005fdbef70de82d07fb5bf3245123a51fda70
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,59 @@@
++// run-rustfix
++
++#![allow(dead_code, unused_variables)]
++
++fn main() {}
++
++mod should_lint {
++    fn one_help() {
++        let a = &12;
++        let b = &*a;
++
++        let b = &mut &*bar(&12);
++    }
++
++    fn bar(x: &u32) -> &u32 {
++        x
++    }
++}
++
++// this mod explains why we should not lint `&mut &* (&T)`
++mod should_not_lint1 {
++    fn foo(x: &mut &u32) {
++        *x = &1;
++    }
++
++    fn main() {
++        let mut x = &0;
++        foo(&mut &*x); // should not lint
++        assert_eq!(*x, 0);
++
++        foo(&mut x);
++        assert_eq!(*x, 1);
++    }
++}
++
++// similar to should_not_lint1
++mod should_not_lint2 {
++    struct S<'a> {
++        a: &'a u32,
++        b: u32,
++    }
++
++    fn main() {
++        let s = S { a: &1, b: 1 };
++        let x = &mut &*s.a;
++        *x = &2;
++    }
++}
++
++// this mod explains why we should not lint `& &* (&T)`
++mod false_negative {
++    fn foo() {
++        let x = &12;
++        let addr_x = &x as *const _ as usize;
++        let addr_y = &&*x as *const _ as usize; // assert ok
++        // let addr_y = &x as *const _ as usize; // assert fail
++        assert_ne!(addr_x, addr_y);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d72de37c69ff538a6eda4b77be4ae22a3d0c6a43
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: deref on an immutable reference
++  --> $DIR/borrow_deref_ref.rs:10:17
++   |
++LL |         let b = &*a;
++   |                 ^^^ help: if you would like to reborrow, try removing `&*`: `a`
++   |
++   = note: `-D clippy::borrow-deref-ref` implied by `-D warnings`
++
++error: deref on an immutable reference
++  --> $DIR/borrow_deref_ref.rs:12:22
++   |
++LL |         let b = &mut &*bar(&12);
++   |                      ^^^^^^^^^^ help: if you would like to reborrow, try removing `&*`: `bar(&12)`
++
++error: deref on an immutable reference
++  --> $DIR/borrow_deref_ref.rs:55:23
++   |
++LL |         let addr_y = &&*x as *const _ as usize; // assert ok
++   |                       ^^^ help: if you would like to reborrow, try removing `&*`: `x`
++
++error: aborting due to 3 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8e2bbfef0f5aae09d2ea5058f645c955c427543
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++#![allow(dead_code, unused_variables)]
++
++fn main() {}
++
++mod should_lint {
++    fn two_helps() {
++        let s = &String::new();
++        let x: &str = &*s;
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..738b01e7ec1ee355cc962d74530a0f77229333d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++error: deref on an immutable reference
++  --> $DIR/borrow_deref_ref_unfixable.rs:8:23
++   |
++LL |         let x: &str = &*s;
++   |                       ^^^
++   |
++   = note: `-D clippy::borrow-deref-ref` implied by `-D warnings`
++help: if you would like to reborrow, try removing `&*`
++   |
++LL |         let x: &str = s;
++   |                       ~
++help: if you would like to deref, try using `&**`
++   |
++LL |         let x: &str = &**s;
++   |                       ~~~~
++
++error: aborting due to previous error
++
index 4ec2465be06db74a6ac1d4e70585aa4020ba33bc,0000000000000000000000000000000000000000..a68b32b097e85ae9c0d0877e9b67c4d18bd501ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,29 @@@
 +// run-rustfix
 +#![warn(clippy::cast_abs_to_unsigned)]
 +
 +fn main() {
 +    let x: i32 = -42;
 +    let y: u32 = x.unsigned_abs();
 +    println!("The absolute value of {} is {}", x, y);
++
++    let a: i32 = -3;
++    let _: usize = a.unsigned_abs() as usize;
++    let _: usize = a.unsigned_abs() as _;
++    let _ = a.unsigned_abs() as usize;
++
++    let a: i64 = -3;
++    let _ = a.unsigned_abs() as usize;
++    let _ = a.unsigned_abs() as u8;
++    let _ = a.unsigned_abs() as u16;
++    let _ = a.unsigned_abs() as u32;
++    let _ = a.unsigned_abs();
++    let _ = a.unsigned_abs() as u128;
++
++    let a: isize = -3;
++    let _ = a.unsigned_abs();
++    let _ = a.unsigned_abs() as u8;
++    let _ = a.unsigned_abs() as u16;
++    let _ = a.unsigned_abs() as u32;
++    let _ = a.unsigned_abs() as u64;
++    let _ = a.unsigned_abs() as u128;
 +}
index 59b9c8c367883b7883f05a8d065d9e688ee68eca,0000000000000000000000000000000000000000..110fbc6c2dfb637db23f49fd96e0b136e2bbd2bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,8 -1,0 +1,29 @@@
 +// run-rustfix
 +#![warn(clippy::cast_abs_to_unsigned)]
 +
 +fn main() {
 +    let x: i32 = -42;
 +    let y: u32 = x.abs() as u32;
 +    println!("The absolute value of {} is {}", x, y);
++
++    let a: i32 = -3;
++    let _: usize = a.abs() as usize;
++    let _: usize = a.abs() as _;
++    let _ = a.abs() as usize;
++
++    let a: i64 = -3;
++    let _ = a.abs() as usize;
++    let _ = a.abs() as u8;
++    let _ = a.abs() as u16;
++    let _ = a.abs() as u32;
++    let _ = a.abs() as u64;
++    let _ = a.abs() as u128;
++
++    let a: isize = -3;
++    let _ = a.abs() as usize;
++    let _ = a.abs() as u8;
++    let _ = a.abs() as u16;
++    let _ = a.abs() as u32;
++    let _ = a.abs() as u64;
++    let _ = a.abs() as u128;
 +}
index eb12857357a444abaf62ec93b3350e9c67565f62,0000000000000000000000000000000000000000..02c24e10659aceb8a94a3d652f16156241e849cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,100 @@@
- error: aborting due to previous error
 +error: casting the result of `i32::abs()` to u32
 +  --> $DIR/cast_abs_to_unsigned.rs:6:18
 +   |
 +LL |     let y: u32 = x.abs() as u32;
 +   |                  ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
 +   |
 +   = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
 +
++error: casting the result of `i32::abs()` to usize
++  --> $DIR/cast_abs_to_unsigned.rs:10:20
++   |
++LL |     let _: usize = a.abs() as usize;
++   |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i32::abs()` to usize
++  --> $DIR/cast_abs_to_unsigned.rs:11:20
++   |
++LL |     let _: usize = a.abs() as _;
++   |                    ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i32::abs()` to usize
++  --> $DIR/cast_abs_to_unsigned.rs:12:13
++   |
++LL |     let _ = a.abs() as usize;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i64::abs()` to usize
++  --> $DIR/cast_abs_to_unsigned.rs:15:13
++   |
++LL |     let _ = a.abs() as usize;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i64::abs()` to u8
++  --> $DIR/cast_abs_to_unsigned.rs:16:13
++   |
++LL |     let _ = a.abs() as u8;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i64::abs()` to u16
++  --> $DIR/cast_abs_to_unsigned.rs:17:13
++   |
++LL |     let _ = a.abs() as u16;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i64::abs()` to u32
++  --> $DIR/cast_abs_to_unsigned.rs:18:13
++   |
++LL |     let _ = a.abs() as u32;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i64::abs()` to u64
++  --> $DIR/cast_abs_to_unsigned.rs:19:13
++   |
++LL |     let _ = a.abs() as u64;
++   |             ^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `i64::abs()` to u128
++  --> $DIR/cast_abs_to_unsigned.rs:20:13
++   |
++LL |     let _ = a.abs() as u128;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `isize::abs()` to usize
++  --> $DIR/cast_abs_to_unsigned.rs:23:13
++   |
++LL |     let _ = a.abs() as usize;
++   |             ^^^^^^^^^^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `isize::abs()` to u8
++  --> $DIR/cast_abs_to_unsigned.rs:24:13
++   |
++LL |     let _ = a.abs() as u8;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `isize::abs()` to u16
++  --> $DIR/cast_abs_to_unsigned.rs:25:13
++   |
++LL |     let _ = a.abs() as u16;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `isize::abs()` to u32
++  --> $DIR/cast_abs_to_unsigned.rs:26:13
++   |
++LL |     let _ = a.abs() as u32;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `isize::abs()` to u64
++  --> $DIR/cast_abs_to_unsigned.rs:27:13
++   |
++LL |     let _ = a.abs() as u64;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: casting the result of `isize::abs()` to u128
++  --> $DIR/cast_abs_to_unsigned.rs:28:13
++   |
++LL |     let _ = a.abs() as u128;
++   |             ^^^^^^^ help: replace with: `a.unsigned_abs()`
++
++error: aborting due to 16 previous errors
 +
index 542e69484276d47b96cc79c3ad382fd6270147a6,0000000000000000000000000000000000000000..c8fb0a39e954cdeabf431e973c2cf19f830778b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,87 -1,0 +1,87 @@@
-         Some(ref s) => match &*s {
 +#![warn(clippy::collapsible_match)]
 +#![allow(
 +    clippy::needless_return,
 +    clippy::no_effect,
 +    clippy::single_match,
 +    clippy::needless_borrow
 +)]
 +
 +fn lint_cases(opt_opt: Option<Option<u32>>, res_opt: Result<Option<u32>, String>) {
 +    // if guards on outer match
 +    {
 +        match res_opt {
 +            Ok(val) if make() => match val {
 +                Some(n) => foo(n),
 +                _ => return,
 +            },
 +            _ => return,
 +        }
 +        match res_opt {
 +            Ok(val) => match val {
 +                Some(n) => foo(n),
 +                _ => return,
 +            },
 +            _ if make() => return,
 +            _ => return,
 +        }
 +    }
 +
 +    // macro
 +    {
 +        macro_rules! mac {
 +            ($outer:expr => $pat:pat, $e:expr => $inner_pat:pat, $then:expr) => {
 +                match $outer {
 +                    $pat => match $e {
 +                        $inner_pat => $then,
 +                        _ => return,
 +                    },
 +                    _ => return,
 +                }
 +            };
 +        }
 +        // Lint this since the patterns are not defined by the macro.
 +        // Allows the lint to work on if_chain! for example.
 +        // Fixing the lint requires knowledge of the specific macro, but we optimistically assume that
 +        // there is still a better way to write this.
 +        mac!(res_opt => Ok(val), val => Some(n), foo(n));
 +    }
 +
 +    // deref reference value
 +    match Some(&[1]) {
 +        Some(s) => match *s {
 +            [n] => foo(n),
 +            _ => (),
 +        },
 +        _ => (),
 +    }
 +
 +    // ref pattern and deref
 +    match Some(&[1]) {
++        Some(ref s) => match s {
 +            [n] => foo(n),
 +            _ => (),
 +        },
 +        _ => (),
 +    }
 +}
 +
 +fn no_lint() {
 +    // deref inner value (cannot pattern match with Vec)
 +    match Some(vec![1]) {
 +        Some(s) => match *s {
 +            [n] => foo(n),
 +            _ => (),
 +        },
 +        _ => (),
 +    }
 +}
 +
 +fn make<T>() -> T {
 +    unimplemented!()
 +}
 +
 +fn foo<T, U>(t: T) -> U {
 +    unimplemented!()
 +}
 +
 +fn main() {}
index 46b645aea135cd78d0e50158cbfde73ab0d0b63b,0000000000000000000000000000000000000000..fe64e4693792dcc109bd4c79f7fac53c5117585a
mode 100644,000000..100644
--- /dev/null
@@@ -1,97 -1,0 +1,97 @@@
- LL |           Some(ref s) => match &*s {
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match2.rs:13:34
 +   |
 +LL |               Ok(val) if make() => match val {
 +   |  __________________________________^
 +LL | |                 Some(n) => foo(n),
 +LL | |                 _ => return,
 +LL | |             },
 +   | |_____________^
 +   |
 +   = note: `-D clippy::collapsible-match` implied by `-D warnings`
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match2.rs:13:16
 +   |
 +LL |             Ok(val) if make() => match val {
 +   |                ^^^ replace this binding
 +LL |                 Some(n) => foo(n),
 +   |                 ^^^^^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match2.rs:20:24
 +   |
 +LL |               Ok(val) => match val {
 +   |  ________________________^
 +LL | |                 Some(n) => foo(n),
 +LL | |                 _ => return,
 +LL | |             },
 +   | |_____________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match2.rs:20:16
 +   |
 +LL |             Ok(val) => match val {
 +   |                ^^^ replace this binding
 +LL |                 Some(n) => foo(n),
 +   |                 ^^^^^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match2.rs:34:29
 +   |
 +LL |                       $pat => match $e {
 +   |  _____________________________^
 +LL | |                         $inner_pat => $then,
 +LL | |                         _ => return,
 +LL | |                     },
 +   | |_____________________^
 +...
 +LL |           mac!(res_opt => Ok(val), val => Some(n), foo(n));
 +   |           ------------------------------------------------ in this macro invocation
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match2.rs:46:28
 +   |
 +LL |         mac!(res_opt => Ok(val), val => Some(n), foo(n));
 +   |                            ^^^          ^^^^^^^ with this pattern
 +   |                            |
 +   |                            replace this binding
 +   = note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match2.rs:51:20
 +   |
 +LL |           Some(s) => match *s {
 +   |  ____________________^
 +LL | |             [n] => foo(n),
 +LL | |             _ => (),
 +LL | |         },
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match2.rs:51:14
 +   |
 +LL |         Some(s) => match *s {
 +   |              ^ replace this binding
 +LL |             [n] => foo(n),
 +   |             ^^^ with this pattern
 +
 +error: this `match` can be collapsed into the outer `match`
 +  --> $DIR/collapsible_match2.rs:60:24
 +   |
- LL |         Some(ref s) => match &*s {
++LL |           Some(ref s) => match s {
 +   |  ________________________^
 +LL | |             [n] => foo(n),
 +LL | |             _ => (),
 +LL | |         },
 +   | |_________^
 +   |
 +help: the outer pattern can be modified to include the inner pattern
 +  --> $DIR/collapsible_match2.rs:60:14
 +   |
++LL |         Some(ref s) => match s {
 +   |              ^^^^^ replace this binding
 +LL |             [n] => foo(n),
 +   |             ^^^ with this pattern
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2747ab2239a683f0219ab6fbc40c41a5efc7f91
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++fn fn_pointer_static() -> usize {
++    static FN: fn() -> usize = || 1;
++    let res = FN() + 1;
++    res
++}
++
++fn fn_pointer_const() -> usize {
++    const FN: fn() -> usize = || 1;
++    let res = FN() + 1;
++    res
++}
++
++fn deref_to_dyn_fn() -> usize {
++    struct Derefs;
++    impl std::ops::Deref for Derefs {
++        type Target = dyn Fn() -> usize;
++
++        fn deref(&self) -> &Self::Target {
++            &|| 2
++        }
++    }
++    static FN: Derefs = Derefs;
++    let res = FN() + 1;
++    res
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..620fd1edaf790e81c8bfdcab2060f4c5af5f1a05
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++error: returning the result of a `let` binding from a block
++  --> $DIR/ice-8850.rs:4:5
++   |
++LL |     let res = FN() + 1;
++   |     ------------------- unnecessary `let` binding
++LL |     res
++   |     ^^^
++   |
++   = note: `-D clippy::let-and-return` implied by `-D warnings`
++help: return the expression directly
++   |
++LL ~     
++LL ~     FN() + 1
++   |
++
++error: returning the result of a `let` binding from a block
++  --> $DIR/ice-8850.rs:10:5
++   |
++LL |     let res = FN() + 1;
++   |     ------------------- unnecessary `let` binding
++LL |     res
++   |     ^^^
++   |
++help: return the expression directly
++   |
++LL ~     
++LL ~     FN() + 1
++   |
++
++error: returning the result of a `let` binding from a block
++  --> $DIR/ice-8850.rs:24:5
++   |
++LL |     let res = FN() + 1;
++   |     ------------------- unnecessary `let` binding
++LL |     res
++   |     ^^^
++   |
++help: return the expression directly
++   |
++LL ~     
++LL ~     FN() + 1
++   |
++
++error: aborting due to 3 previous errors
++
index a3e7a7162e51dff7516e79451764518e33e6f6bf,0000000000000000000000000000000000000000..e6a65b46d975c80bdeb7eef62144efd45e19b1a7
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,146 @@@
- error: aborting due to 10 previous errors
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:5:22
 +   |
 +LL |     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::dbg-macro` implied by `-D warnings`
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     if let Some(n) = n.checked_sub(4) { n } else { n }
 +   |                      ~~~~~~~~~~~~~~~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:9:8
 +   |
 +LL |     if dbg!(n <= 1) {
 +   |        ^^^^^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     if n <= 1 {
 +   |        ~~~~~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:10:9
 +   |
 +LL |         dbg!(1)
 +   |         ^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |         1
 +   |
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:12:9
 +   |
 +LL |         dbg!(n * factorial(n - 1))
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |         n * factorial(n - 1)
 +   |
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:17:5
 +   |
 +LL |     dbg!(42);
 +   |     ^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     42;
 +   |     ~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:18:5
 +   |
 +LL |     dbg!(dbg!(dbg!(42)));
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     dbg!(dbg!(42));
 +   |     ~~~~~~~~~~~~~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:19:14
 +   |
 +LL |     foo(3) + dbg!(factorial(4));
 +   |              ^^^^^^^^^^^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     foo(3) + factorial(4);
 +   |              ~~~~~~~~~~~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:20:5
 +   |
 +LL |     dbg!(1, 2, dbg!(3, 4));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     (1, 2, dbg!(3, 4));
 +   |     ~~~~~~~~~~~~~~~~~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:21:5
 +   |
 +LL |     dbg!(1, 2, 3, 4, 5);
 +   |     ^^^^^^^^^^^^^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |     (1, 2, 3, 4, 5);
 +   |     ~~~~~~~~~~~~~~~
 +
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:41:9
 +   |
 +LL |         dbg!(2);
 +   |         ^^^^^^^
 +   |
 +help: ensure to avoid having uses of it in version control
 +   |
 +LL |         2;
 +   |         ~
 +
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:47:5
++   |
++LL |     dbg!(1);
++   |     ^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     1;
++   |     ~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:52:5
++   |
++LL |     dbg!(1);
++   |     ^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |     1;
++   |     ~
++
++error: `dbg!` macro is intended as a debugging tool
++  --> $DIR/dbg_macro.rs:58:9
++   |
++LL |         dbg!(1);
++   |         ^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |         1;
++   |         ~
++
++error: aborting due to 13 previous errors
 +
index c5de412556567ffe6718fc9ada4471dcb5e4cc8b,0000000000000000000000000000000000000000..46faa0a7b9117f79dd456086a56a341b3a443993
mode 100644,000000..100644
--- /dev/null
@@@ -1,133 -1,0 +1,133 @@@
- #![allow(clippy::redundant_closure_call)]
 +#![feature(custom_inner_attributes)]
 +#![rustfmt::skip]
 +#![warn(clippy::debug_assert_with_mut_call)]
++#![allow(clippy::redundant_closure_call, clippy::get_first)]
 +
 +
 +struct S;
 +
 +impl S {
 +    fn bool_self_ref(&self) -> bool { false }
 +    fn bool_self_mut(&mut self) -> bool { false }
 +    fn bool_self_ref_arg_ref(&self, _: &u32) -> bool { false }
 +    fn bool_self_ref_arg_mut(&self, _: &mut u32) -> bool { false }
 +    fn bool_self_mut_arg_ref(&mut self, _: &u32) -> bool { false }
 +    fn bool_self_mut_arg_mut(&mut self, _: &mut u32) -> bool { false }
 +
 +    fn u32_self_ref(&self) -> u32 { 0 }
 +    fn u32_self_mut(&mut self) -> u32 { 0 }
 +    fn u32_self_ref_arg_ref(&self, _: &u32) -> u32 { 0 }
 +    fn u32_self_ref_arg_mut(&self, _: &mut u32) -> u32 { 0 }
 +    fn u32_self_mut_arg_ref(&mut self, _: &u32) -> u32 { 0 }
 +    fn u32_self_mut_arg_mut(&mut self, _: &mut u32) -> u32 { 0 }
 +}
 +
 +fn bool_ref(_: &u32) -> bool { false }
 +fn bool_mut(_: &mut u32) -> bool { false }
 +fn u32_ref(_: &u32) -> u32 { 0 }
 +fn u32_mut(_: &mut u32) -> u32 { 0 }
 +
 +fn func_non_mutable() {
 +    debug_assert!(bool_ref(&3));
 +    debug_assert!(!bool_ref(&3));
 +
 +    debug_assert_eq!(0, u32_ref(&3));
 +    debug_assert_eq!(u32_ref(&3), 0);
 +
 +    debug_assert_ne!(1, u32_ref(&3));
 +    debug_assert_ne!(u32_ref(&3), 1);
 +}
 +
 +fn func_mutable() {
 +    debug_assert!(bool_mut(&mut 3));
 +    debug_assert!(!bool_mut(&mut 3));
 +
 +    debug_assert_eq!(0, u32_mut(&mut 3));
 +    debug_assert_eq!(u32_mut(&mut 3), 0);
 +
 +    debug_assert_ne!(1, u32_mut(&mut 3));
 +    debug_assert_ne!(u32_mut(&mut 3), 1);
 +}
 +
 +fn method_non_mutable() {
 +    debug_assert!(S.bool_self_ref());
 +    debug_assert!(S.bool_self_ref_arg_ref(&3));
 +
 +    debug_assert_eq!(S.u32_self_ref(), 0);
 +    debug_assert_eq!(S.u32_self_ref_arg_ref(&3), 0);
 +
 +    debug_assert_ne!(S.u32_self_ref(), 1);
 +    debug_assert_ne!(S.u32_self_ref_arg_ref(&3), 1);
 +}
 +
 +fn method_mutable() {
 +    debug_assert!(S.bool_self_mut());
 +    debug_assert!(!S.bool_self_mut());
 +    debug_assert!(S.bool_self_ref_arg_mut(&mut 3));
 +    debug_assert!(S.bool_self_mut_arg_ref(&3));
 +    debug_assert!(S.bool_self_mut_arg_mut(&mut 3));
 +
 +    debug_assert_eq!(S.u32_self_mut(), 0);
 +    debug_assert_eq!(S.u32_self_mut_arg_ref(&3), 0);
 +    debug_assert_eq!(S.u32_self_ref_arg_mut(&mut 3), 0);
 +    debug_assert_eq!(S.u32_self_mut_arg_mut(&mut 3), 0);
 +
 +    debug_assert_ne!(S.u32_self_mut(), 1);
 +    debug_assert_ne!(S.u32_self_mut_arg_ref(&3), 1);
 +    debug_assert_ne!(S.u32_self_ref_arg_mut(&mut 3), 1);
 +    debug_assert_ne!(S.u32_self_mut_arg_mut(&mut 3), 1);
 +}
 +
 +fn misc() {
 +    // with variable
 +    let mut v: Vec<u32> = vec![1, 2, 3, 4];
 +    debug_assert_eq!(v.get(0), Some(&1));
 +    debug_assert_ne!(v[0], 2);
 +    debug_assert_eq!(v.pop(), Some(1));
 +    debug_assert_ne!(Some(3), v.pop());
 +
 +    let a = &mut 3;
 +    debug_assert!(bool_mut(a));
 +
 +    // nested
 +    debug_assert!(!(bool_ref(&u32_mut(&mut 3))));
 +
 +    // chained
 +    debug_assert_eq!(v.pop().unwrap(), 3);
 +
 +    // format args
 +    debug_assert!(bool_ref(&3), "w/o format");
 +    debug_assert!(bool_mut(&mut 3), "w/o format");
 +    debug_assert!(bool_ref(&3), "{} format", "w/");
 +    debug_assert!(bool_mut(&mut 3), "{} format", "w/");
 +
 +    // sub block
 +    let mut x = 42_u32;
 +    debug_assert!({
 +        bool_mut(&mut x);
 +        x > 10
 +    });
 +
 +    // closures
 +    debug_assert!((|| {
 +        let mut x = 42;
 +        bool_mut(&mut x);
 +        x > 10
 +    })());
 +}
 +
 +async fn debug_await() {
 +    debug_assert!(async {
 +        true
 +    }.await);
 +}
 +
 +fn main() {
 +    func_non_mutable();
 +    func_mutable();
 +    method_non_mutable();
 +    method_mutable();
 +
 +    misc();
 +    debug_await();
 +}
index b26276218b78c7e0c110cb4c1831689a27372afb,0000000000000000000000000000000000000000..257393e56ff0f766adb33cff0b920627502fadc5
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,30 @@@
 +// run-rustfix
 +
 +#![warn(clippy::deref_by_slicing)]
++#![allow(clippy::borrow_deref_ref)]
 +
 +use std::io::Read;
 +
 +fn main() {
 +    let mut vec = vec![0];
 +    let _ = &*vec;
 +    let _ = &mut *vec;
 +
 +    let ref_vec = &mut vec;
 +    let _ = &**ref_vec;
 +    let mut_slice = &mut **ref_vec;
 +    let _ = &mut *mut_slice; // Err, re-borrows slice
 +
 +    let s = String::new();
 +    let _ = &*s;
 +
 +    static S: &[u8] = &[0, 1, 2];
 +    let _ = &mut &*S; // Err, re-borrows slice
 +
 +    let slice: &[u32] = &[0u32, 1u32];
 +    let slice_ref = &slice;
 +    let _ = *slice_ref; // Err, derefs slice
 +
 +    let bytes: &[u8] = &[];
 +    let _ = (&*bytes).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
 +}
index 6aa1408ba17698854ad1f9bcf361694b6aa5343e,0000000000000000000000000000000000000000..e288046f927fd44fa3f8585ec1a832102ad9ccfe
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,30 @@@
 +// run-rustfix
 +
 +#![warn(clippy::deref_by_slicing)]
++#![allow(clippy::borrow_deref_ref)]
 +
 +use std::io::Read;
 +
 +fn main() {
 +    let mut vec = vec![0];
 +    let _ = &vec[..];
 +    let _ = &mut vec[..];
 +
 +    let ref_vec = &mut vec;
 +    let _ = &ref_vec[..];
 +    let mut_slice = &mut ref_vec[..];
 +    let _ = &mut mut_slice[..]; // Err, re-borrows slice
 +
 +    let s = String::new();
 +    let _ = &s[..];
 +
 +    static S: &[u8] = &[0, 1, 2];
 +    let _ = &mut &S[..]; // Err, re-borrows slice
 +
 +    let slice: &[u32] = &[0u32, 1u32];
 +    let slice_ref = &slice;
 +    let _ = &slice_ref[..]; // Err, derefs slice
 +
 +    let bytes: &[u8] = &[];
 +    let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
 +}
index ffd76de378df1d314b7d95fa04cf96f6efaac800,0000000000000000000000000000000000000000..8f042ef47ebe326d6982a30ef2f1c2c357953d57
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,58 @@@
-   --> $DIR/deref_by_slicing.rs:9:13
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:10:13
++  --> $DIR/deref_by_slicing.rs:10:13
 +   |
 +LL |     let _ = &vec[..];
 +   |             ^^^^^^^^ help: dereference the original value instead: `&*vec`
 +   |
 +   = note: `-D clippy::deref-by-slicing` implied by `-D warnings`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:13:13
++  --> $DIR/deref_by_slicing.rs:11:13
 +   |
 +LL |     let _ = &mut vec[..];
 +   |             ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:14:21
++  --> $DIR/deref_by_slicing.rs:14:13
 +   |
 +LL |     let _ = &ref_vec[..];
 +   |             ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:15:13
++  --> $DIR/deref_by_slicing.rs:15:21
 +   |
 +LL |     let mut_slice = &mut ref_vec[..];
 +   |                     ^^^^^^^^^^^^^^^^ help: dereference the original value instead: `&mut **ref_vec`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:18:13
++  --> $DIR/deref_by_slicing.rs:16:13
 +   |
 +LL |     let _ = &mut mut_slice[..]; // Err, re-borrows slice
 +   |             ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:21:18
++  --> $DIR/deref_by_slicing.rs:19:13
 +   |
 +LL |     let _ = &s[..];
 +   |             ^^^^^^ help: dereference the original value instead: `&*s`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:25:13
++  --> $DIR/deref_by_slicing.rs:22:18
 +   |
 +LL |     let _ = &mut &S[..]; // Err, re-borrows slice
 +   |                  ^^^^^^ help: reborrow the original value instead: `&*S`
 +
 +error: slicing when dereferencing would work
-   --> $DIR/deref_by_slicing.rs:28:13
++  --> $DIR/deref_by_slicing.rs:26:13
 +   |
 +LL |     let _ = &slice_ref[..]; // Err, derefs slice
 +   |             ^^^^^^^^^^^^^^ help: dereference the original value instead: `*slice_ref`
 +
 +error: slicing when dereferencing would work
++  --> $DIR/deref_by_slicing.rs:29:13
 +   |
 +LL |     let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
 +   |             ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)`
 +
 +error: aborting due to 9 previous errors
 +
index 7d4d1b3b64906b4fdc64bcea3c44fef0b44c4a46,0000000000000000000000000000000000000000..012780258fc3a7bfb9ae2a92683705eaca9c00e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,104 @@@
 +// run-rustfix
 +
 +#![allow(unused)]
 +#![warn(clippy::derive_partial_eq_without_eq)]
 +
 +// Don't warn on structs that aren't PartialEq
 +struct NotPartialEq {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +// Eq can be derived but is missing
 +#[derive(Debug, PartialEq, Eq)]
 +struct MissingEq {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +// Eq is derived
 +#[derive(PartialEq, Eq)]
 +struct NotMissingEq {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +// Eq is manually implemented
 +#[derive(PartialEq)]
 +struct ManualEqImpl {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +impl Eq for ManualEqImpl {}
 +
 +// Cannot be Eq because f32 isn't Eq
 +#[derive(PartialEq)]
 +struct CannotBeEq {
 +    foo: u32,
 +    bar: f32,
 +}
 +
 +// Don't warn if PartialEq is manually implemented
 +struct ManualPartialEqImpl {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +impl PartialEq for ManualPartialEqImpl {
 +    fn eq(&self, other: &Self) -> bool {
 +        self.foo == other.foo && self.bar == other.bar
 +    }
 +}
 +
 +// Generic fields should be properly checked for Eq-ness
 +#[derive(PartialEq)]
 +struct GenericNotEq<T: Eq, U: PartialEq> {
 +    foo: T,
 +    bar: U,
 +}
 +
 +#[derive(PartialEq, Eq)]
 +struct GenericEq<T: Eq, U: Eq> {
 +    foo: T,
 +    bar: U,
 +}
 +
 +#[derive(PartialEq, Eq)]
 +struct TupleStruct(u32);
 +
 +#[derive(PartialEq, Eq)]
 +struct GenericTupleStruct<T: Eq>(T);
 +
 +#[derive(PartialEq)]
 +struct TupleStructNotEq(f32);
 +
 +#[derive(PartialEq, Eq)]
 +enum Enum {
 +    Foo(u32),
 +    Bar { a: String, b: () },
 +}
 +
 +#[derive(PartialEq, Eq)]
 +enum GenericEnum<T: Eq, U: Eq, V: Eq> {
 +    Foo(T),
 +    Bar { a: U, b: V },
 +}
 +
 +#[derive(PartialEq)]
 +enum EnumNotEq {
 +    Foo(u32),
 +    Bar { a: String, b: f32 },
 +}
 +
 +// Ensure that rustfix works properly when `PartialEq` has other derives on either side
 +#[derive(Debug, PartialEq, Eq, Clone)]
 +struct RustFixWithOtherDerives;
 +
++#[derive(PartialEq)]
++struct Generic<T>(T);
++
++#[derive(PartialEq, Eq)]
++struct GenericPhantom<T>(core::marker::PhantomData<T>);
++
 +fn main() {}
index ab4e1df1ca4084f2c94d491ed6edd012d989b162,0000000000000000000000000000000000000000..fc8285b0c6b753921d51eddc8388a876cfcd70cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,98 -1,0 +1,104 @@@
 +// run-rustfix
 +
 +#![allow(unused)]
 +#![warn(clippy::derive_partial_eq_without_eq)]
 +
 +// Don't warn on structs that aren't PartialEq
 +struct NotPartialEq {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +// Eq can be derived but is missing
 +#[derive(Debug, PartialEq)]
 +struct MissingEq {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +// Eq is derived
 +#[derive(PartialEq, Eq)]
 +struct NotMissingEq {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +// Eq is manually implemented
 +#[derive(PartialEq)]
 +struct ManualEqImpl {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +impl Eq for ManualEqImpl {}
 +
 +// Cannot be Eq because f32 isn't Eq
 +#[derive(PartialEq)]
 +struct CannotBeEq {
 +    foo: u32,
 +    bar: f32,
 +}
 +
 +// Don't warn if PartialEq is manually implemented
 +struct ManualPartialEqImpl {
 +    foo: u32,
 +    bar: String,
 +}
 +
 +impl PartialEq for ManualPartialEqImpl {
 +    fn eq(&self, other: &Self) -> bool {
 +        self.foo == other.foo && self.bar == other.bar
 +    }
 +}
 +
 +// Generic fields should be properly checked for Eq-ness
 +#[derive(PartialEq)]
 +struct GenericNotEq<T: Eq, U: PartialEq> {
 +    foo: T,
 +    bar: U,
 +}
 +
 +#[derive(PartialEq)]
 +struct GenericEq<T: Eq, U: Eq> {
 +    foo: T,
 +    bar: U,
 +}
 +
 +#[derive(PartialEq)]
 +struct TupleStruct(u32);
 +
 +#[derive(PartialEq)]
 +struct GenericTupleStruct<T: Eq>(T);
 +
 +#[derive(PartialEq)]
 +struct TupleStructNotEq(f32);
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    Foo(u32),
 +    Bar { a: String, b: () },
 +}
 +
 +#[derive(PartialEq)]
 +enum GenericEnum<T: Eq, U: Eq, V: Eq> {
 +    Foo(T),
 +    Bar { a: U, b: V },
 +}
 +
 +#[derive(PartialEq)]
 +enum EnumNotEq {
 +    Foo(u32),
 +    Bar { a: String, b: f32 },
 +}
 +
 +// Ensure that rustfix works properly when `PartialEq` has other derives on either side
 +#[derive(Debug, PartialEq, Clone)]
 +struct RustFixWithOtherDerives;
 +
++#[derive(PartialEq)]
++struct Generic<T>(T);
++
++#[derive(PartialEq, Eq)]
++struct GenericPhantom<T>(core::marker::PhantomData<T>);
++
 +fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ab52fb1a4a69eebe55c83af2599c4919f13f8532
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,12 @@@
++#![warn(clippy::doc_link_with_quotes)]
++
++fn main() {
++    foo()
++}
++
++/// Calls ['bar']
++pub fn foo() {
++    bar()
++}
++
++pub fn bar() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bf6d57d8afe8c8788eae2122b30a1e2f69963bb0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,10 @@@
++error: possible intra-doc link using quotes instead of backticks
++  --> $DIR/doc_link_with_quotes.rs:7:1
++   |
++LL | /// Calls ['bar']
++   | ^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::doc-link-with-quotes` implied by `-D warnings`
++
++error: aborting due to previous error
++
index 3e92bca986ab55b4990a8d32e4f3f64a89f65289,0000000000000000000000000000000000000000..d15c84d7438a09bfe683c53cbb85f030535b003b
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,123 @@@
 +// aux-build:proc_macro_attr.rs
 +#![warn(clippy::empty_line_after_outer_attr)]
 +#![allow(clippy::assertions_on_constants)]
 +#![feature(custom_inner_attributes)]
 +#![rustfmt::skip]
 +
++#[macro_use]
++extern crate clap;
++
 +#[macro_use]
 +extern crate proc_macro_attr;
 +
 +// This should produce a warning
 +#[crate_type = "lib"]
 +
 +/// some comment
 +fn with_one_newline_and_comment() { assert!(true) }
 +
 +// This should not produce a warning
 +#[crate_type = "lib"]
 +/// some comment
 +fn with_no_newline_and_comment() { assert!(true) }
 +
 +
 +// This should produce a warning
 +#[crate_type = "lib"]
 +
 +fn with_one_newline() { assert!(true) }
 +
 +// This should produce a warning, too
 +#[crate_type = "lib"]
 +
 +
 +fn with_two_newlines() { assert!(true) }
 +
 +
 +// This should produce a warning
 +#[crate_type = "lib"]
 +
 +enum Baz {
 +    One,
 +    Two
 +}
 +
 +// This should produce a warning
 +#[crate_type = "lib"]
 +
 +struct Foo {
 +    one: isize,
 +    two: isize
 +}
 +
 +// This should produce a warning
 +#[crate_type = "lib"]
 +
 +mod foo {
 +}
 +
 +/// This doc comment should not produce a warning
 +
 +/** This is also a doc comment and should not produce a warning
 + */
 +
 +// This should not produce a warning
 +#[allow(non_camel_case_types)]
 +#[allow(missing_docs)]
 +#[allow(missing_docs)]
 +fn three_attributes() { assert!(true) }
 +
 +// This should not produce a warning
 +#[doc = "
 +Returns the escaped value of the textual representation of
 +
 +"]
 +pub fn function() -> bool {
 +    true
 +}
 +
 +// This should not produce a warning
 +#[derive(Clone, Copy)]
 +pub enum FooFighter {
 +    Bar1,
 +
 +    Bar2,
 +
 +    Bar3,
 +
 +    Bar4
 +}
 +
 +// This should not produce a warning because the empty line is inside a block comment
 +#[crate_type = "lib"]
 +/*
 +
 +*/
 +pub struct S;
 +
 +// This should not produce a warning
 +#[crate_type = "lib"]
 +/* test */
 +pub struct T;
 +
 +// This should not produce a warning
 +// See https://github.com/rust-lang/rust-clippy/issues/5567
 +#[fake_async_trait]
 +pub trait Bazz {
 +    fn foo() -> Vec<u8> {
 +        let _i = "";
 +
 +
 +
 +        vec![]
 +    }
 +}
 +
++#[derive(clap::Parser)]
++#[clap(after_help = "This ia a help message.
++
++You're welcome.
++")]
++pub struct Args;
++
 +fn main() {}
index 594fca44a321071ac46cdc9ad5bcac5676f69218,0000000000000000000000000000000000000000..acc3edef9b928d437de7d1c5c6bad48357bdc45e
mode 100644,000000..100644
--- /dev/null
@@@ -1,54 -1,0 +1,54 @@@
-   --> $DIR/empty_line_after_outer_attribute.rs:11:1
 +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-   --> $DIR/empty_line_after_outer_attribute.rs:23:1
++  --> $DIR/empty_line_after_outer_attribute.rs:14:1
 +   |
 +LL | / #[crate_type = "lib"]
 +LL | |
 +LL | | /// some comment
 +LL | | fn with_one_newline_and_comment() { assert!(true) }
 +   | |_
 +   |
 +   = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings`
 +
 +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-   --> $DIR/empty_line_after_outer_attribute.rs:28:1
++  --> $DIR/empty_line_after_outer_attribute.rs:26:1
 +   |
 +LL | / #[crate_type = "lib"]
 +LL | |
 +LL | | fn with_one_newline() { assert!(true) }
 +   | |_
 +
 +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-   --> $DIR/empty_line_after_outer_attribute.rs:35:1
++  --> $DIR/empty_line_after_outer_attribute.rs:31:1
 +   |
 +LL | / #[crate_type = "lib"]
 +LL | |
 +LL | |
 +LL | | fn with_two_newlines() { assert!(true) }
 +   | |_
 +
 +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-   --> $DIR/empty_line_after_outer_attribute.rs:43:1
++  --> $DIR/empty_line_after_outer_attribute.rs:38:1
 +   |
 +LL | / #[crate_type = "lib"]
 +LL | |
 +LL | | enum Baz {
 +   | |_
 +
 +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
-   --> $DIR/empty_line_after_outer_attribute.rs:51:1
++  --> $DIR/empty_line_after_outer_attribute.rs:46:1
 +   |
 +LL | / #[crate_type = "lib"]
 +LL | |
 +LL | | struct Foo {
 +   | |_
 +
 +error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute?
++  --> $DIR/empty_line_after_outer_attribute.rs:54:1
 +   |
 +LL | / #[crate_type = "lib"]
 +LL | |
 +LL | | mod foo {
 +   | |_
 +
 +error: aborting due to 6 previous errors
 +
index 3de2a51ffa5f326c516154723da1b8ff99c53f21,0000000000000000000000000000000000000000..92f27e68549a397bf671441c962ae76912d9a4e8
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,100 @@@
- #![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)]
 +// run-rustfix
 +
++#![allow(
++    unused_variables,
++    clippy::clone_double_ref,
++    clippy::needless_borrow,
++    clippy::borrow_deref_ref
++)]
 +#![warn(clippy::explicit_deref_methods)]
 +
 +use std::ops::{Deref, DerefMut};
 +
 +fn concat(deref_str: &str) -> String {
 +    format!("{}bar", deref_str)
 +}
 +
 +fn just_return(deref_str: &str) -> &str {
 +    deref_str
 +}
 +
 +struct CustomVec(Vec<u8>);
 +impl Deref for CustomVec {
 +    type Target = Vec<u8>;
 +
 +    fn deref(&self) -> &Vec<u8> {
 +        &self.0
 +    }
 +}
 +
 +fn main() {
 +    let a: &mut String = &mut String::from("foo");
 +
 +    // these should require linting
 +
 +    let b: &str = &*a;
 +
 +    let b: &mut str = &mut **a;
 +
 +    // both derefs should get linted here
 +    let b: String = format!("{}, {}", &*a, &*a);
 +
 +    println!("{}", &*a);
 +
 +    #[allow(clippy::match_single_binding)]
 +    match &*a {
 +        _ => (),
 +    }
 +
 +    let b: String = concat(&*a);
 +
 +    let b = just_return(a);
 +
 +    let b: String = concat(just_return(a));
 +
 +    let b: &str = &**a;
 +
 +    let opt_a = Some(a.clone());
 +    let b = &*opt_a.unwrap();
 +
 +    // following should not require linting
 +
 +    let cv = CustomVec(vec![0, 42]);
 +    let c = cv.deref()[0];
 +
 +    let b: &str = &*a.deref();
 +
 +    let b: String = a.deref().clone();
 +
 +    let b: usize = a.deref_mut().len();
 +
 +    let b: &usize = &a.deref().len();
 +
 +    let b: &str = &*a;
 +
 +    let b: &mut str = &mut *a;
 +
 +    macro_rules! expr_deref {
 +        ($body:expr) => {
 +            $body.deref()
 +        };
 +    }
 +    let b: &str = expr_deref!(a);
 +
 +    let b: &str = expr_deref!(&*a);
 +
 +    // The struct does not implement Deref trait
 +    #[derive(Copy, Clone)]
 +    struct NoLint(u32);
 +    impl NoLint {
 +        pub fn deref(self) -> u32 {
 +            self.0
 +        }
 +        pub fn deref_mut(self) -> u32 {
 +            self.0
 +        }
 +    }
 +    let no_lint = NoLint(42);
 +    let b = no_lint.deref();
 +    let b = no_lint.deref_mut();
 +}
index a08d75964220a06adcf52b519b4f9029f1bc3a19,0000000000000000000000000000000000000000..d118607f992b9e0cd48fec70cb3fb82c4d9c9b77
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,100 @@@
- #![allow(unused_variables, clippy::clone_double_ref, clippy::needless_borrow)]
 +// run-rustfix
 +
++#![allow(
++    unused_variables,
++    clippy::clone_double_ref,
++    clippy::needless_borrow,
++    clippy::borrow_deref_ref
++)]
 +#![warn(clippy::explicit_deref_methods)]
 +
 +use std::ops::{Deref, DerefMut};
 +
 +fn concat(deref_str: &str) -> String {
 +    format!("{}bar", deref_str)
 +}
 +
 +fn just_return(deref_str: &str) -> &str {
 +    deref_str
 +}
 +
 +struct CustomVec(Vec<u8>);
 +impl Deref for CustomVec {
 +    type Target = Vec<u8>;
 +
 +    fn deref(&self) -> &Vec<u8> {
 +        &self.0
 +    }
 +}
 +
 +fn main() {
 +    let a: &mut String = &mut String::from("foo");
 +
 +    // these should require linting
 +
 +    let b: &str = a.deref();
 +
 +    let b: &mut str = a.deref_mut();
 +
 +    // both derefs should get linted here
 +    let b: String = format!("{}, {}", a.deref(), a.deref());
 +
 +    println!("{}", a.deref());
 +
 +    #[allow(clippy::match_single_binding)]
 +    match a.deref() {
 +        _ => (),
 +    }
 +
 +    let b: String = concat(a.deref());
 +
 +    let b = just_return(a).deref();
 +
 +    let b: String = concat(just_return(a).deref());
 +
 +    let b: &str = a.deref().deref();
 +
 +    let opt_a = Some(a.clone());
 +    let b = opt_a.unwrap().deref();
 +
 +    // following should not require linting
 +
 +    let cv = CustomVec(vec![0, 42]);
 +    let c = cv.deref()[0];
 +
 +    let b: &str = &*a.deref();
 +
 +    let b: String = a.deref().clone();
 +
 +    let b: usize = a.deref_mut().len();
 +
 +    let b: &usize = &a.deref().len();
 +
 +    let b: &str = &*a;
 +
 +    let b: &mut str = &mut *a;
 +
 +    macro_rules! expr_deref {
 +        ($body:expr) => {
 +            $body.deref()
 +        };
 +    }
 +    let b: &str = expr_deref!(a);
 +
 +    let b: &str = expr_deref!(a.deref());
 +
 +    // The struct does not implement Deref trait
 +    #[derive(Copy, Clone)]
 +    struct NoLint(u32);
 +    impl NoLint {
 +        pub fn deref(self) -> u32 {
 +            self.0
 +        }
 +        pub fn deref_mut(self) -> u32 {
 +            self.0
 +        }
 +    }
 +    let no_lint = NoLint(42);
 +    let b = no_lint.deref();
 +    let b = no_lint.deref_mut();
 +}
index 8035d77d18d5c8c333dfae8fc96bef73d371d476,0000000000000000000000000000000000000000..8e8b358972be7e39808931940a6fec095dc03881
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-   --> $DIR/explicit_deref_methods.rs:30:19
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:32:23
++  --> $DIR/explicit_deref_methods.rs:35:19
 +   |
 +LL |     let b: &str = a.deref();
 +   |                   ^^^^^^^^^ help: try this: `&*a`
 +   |
 +   = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
 +
 +error: explicit `deref_mut` method call
-   --> $DIR/explicit_deref_methods.rs:35:39
++  --> $DIR/explicit_deref_methods.rs:37:23
 +   |
 +LL |     let b: &mut str = a.deref_mut();
 +   |                       ^^^^^^^^^^^^^ help: try this: `&mut **a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:35:50
++  --> $DIR/explicit_deref_methods.rs:40:39
 +   |
 +LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
 +   |                                       ^^^^^^^^^ help: try this: `&*a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:37:20
++  --> $DIR/explicit_deref_methods.rs:40:50
 +   |
 +LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
 +   |                                                  ^^^^^^^^^ help: try this: `&*a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:40:11
++  --> $DIR/explicit_deref_methods.rs:42:20
 +   |
 +LL |     println!("{}", a.deref());
 +   |                    ^^^^^^^^^ help: try this: `&*a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:44:28
++  --> $DIR/explicit_deref_methods.rs:45:11
 +   |
 +LL |     match a.deref() {
 +   |           ^^^^^^^^^ help: try this: `&*a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:46:13
++  --> $DIR/explicit_deref_methods.rs:49:28
 +   |
 +LL |     let b: String = concat(a.deref());
 +   |                            ^^^^^^^^^ help: try this: `&*a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:48:28
++  --> $DIR/explicit_deref_methods.rs:51:13
 +   |
 +LL |     let b = just_return(a).deref();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:50:19
++  --> $DIR/explicit_deref_methods.rs:53:28
 +   |
 +LL |     let b: String = concat(just_return(a).deref());
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:53:13
++  --> $DIR/explicit_deref_methods.rs:55:19
 +   |
 +LL |     let b: &str = a.deref().deref();
 +   |                   ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
 +
 +error: explicit `deref` method call
-   --> $DIR/explicit_deref_methods.rs:79:31
++  --> $DIR/explicit_deref_methods.rs:58:13
 +   |
 +LL |     let b = opt_a.unwrap().deref();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
 +
 +error: explicit `deref` method call
++  --> $DIR/explicit_deref_methods.rs:84:31
 +   |
 +LL |     let b: &str = expr_deref!(a.deref());
 +   |                               ^^^^^^^^^ help: try this: `&*a`
 +
 +error: aborting due to 12 previous errors
 +
index 6c8c4c9c0edecc01adeaee75a87681af5b94dc06,0000000000000000000000000000000000000000..031b415f56ff6d643554a033f3f06ee6d72b7e78
mode 100644,000000..100644
--- /dev/null
@@@ -1,49 -1,0 +1,50 @@@
 +#![warn(clippy::forget_ref)]
 +#![allow(clippy::toplevel_ref_arg)]
 +#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)]
++#![allow(clippy::borrow_deref_ref)]
 +
 +use std::mem::forget;
 +
 +struct SomeStruct;
 +
 +fn main() {
 +    forget(&SomeStruct);
 +
 +    let mut owned = SomeStruct;
 +    forget(&owned);
 +    forget(&&owned);
 +    forget(&mut owned);
 +    forget(owned); //OK
 +
 +    let reference1 = &SomeStruct;
 +    forget(&*reference1);
 +
 +    let reference2 = &mut SomeStruct;
 +    forget(reference2);
 +
 +    let ref reference3 = SomeStruct;
 +    forget(reference3);
 +}
 +
 +#[allow(dead_code)]
 +fn test_generic_fn_forget<T>(val: T) {
 +    forget(&val);
 +    forget(val); //OK
 +}
 +
 +#[allow(dead_code)]
 +fn test_similarly_named_function() {
 +    fn forget<T>(_val: T) {}
 +    forget(&SomeStruct); //OK; call to unrelated function which happens to have the same name
 +    std::mem::forget(&SomeStruct);
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct Error;
 +fn produce_half_owl_error() -> Result<(), Error> {
 +    Ok(())
 +}
 +
 +fn produce_half_owl_ok() -> Result<bool, ()> {
 +    Ok(true)
 +}
index 73409388ed16ad0ae9adf9006788a747a26dac31,0000000000000000000000000000000000000000..df5cd8cacdb8dbb8566c4a0c680b70cfe85e3992
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,111 @@@
-   --> $DIR/forget_ref.rs:10:5
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:10:12
++  --> $DIR/forget_ref.rs:11:5
 +   |
 +LL |     forget(&SomeStruct);
 +   |     ^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::forget-ref` implied by `-D warnings`
 +note: argument has type `&SomeStruct`
-   --> $DIR/forget_ref.rs:13:5
++  --> $DIR/forget_ref.rs:11:12
 +   |
 +LL |     forget(&SomeStruct);
 +   |            ^^^^^^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:13:12
++  --> $DIR/forget_ref.rs:14:5
 +   |
 +LL |     forget(&owned);
 +   |     ^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&SomeStruct`
-   --> $DIR/forget_ref.rs:14:5
++  --> $DIR/forget_ref.rs:14:12
 +   |
 +LL |     forget(&owned);
 +   |            ^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:14:12
++  --> $DIR/forget_ref.rs:15:5
 +   |
 +LL |     forget(&&owned);
 +   |     ^^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&&SomeStruct`
-   --> $DIR/forget_ref.rs:15:5
++  --> $DIR/forget_ref.rs:15:12
 +   |
 +LL |     forget(&&owned);
 +   |            ^^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:15:12
++  --> $DIR/forget_ref.rs:16:5
 +   |
 +LL |     forget(&mut owned);
 +   |     ^^^^^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&mut SomeStruct`
-   --> $DIR/forget_ref.rs:19:5
++  --> $DIR/forget_ref.rs:16:12
 +   |
 +LL |     forget(&mut owned);
 +   |            ^^^^^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:19:12
++  --> $DIR/forget_ref.rs:20:5
 +   |
 +LL |     forget(&*reference1);
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&SomeStruct`
-   --> $DIR/forget_ref.rs:22:5
++  --> $DIR/forget_ref.rs:20:12
 +   |
 +LL |     forget(&*reference1);
 +   |            ^^^^^^^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:22:12
++  --> $DIR/forget_ref.rs:23:5
 +   |
 +LL |     forget(reference2);
 +   |     ^^^^^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&mut SomeStruct`
-   --> $DIR/forget_ref.rs:25:5
++  --> $DIR/forget_ref.rs:23:12
 +   |
 +LL |     forget(reference2);
 +   |            ^^^^^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:25:12
++  --> $DIR/forget_ref.rs:26:5
 +   |
 +LL |     forget(reference3);
 +   |     ^^^^^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&SomeStruct`
-   --> $DIR/forget_ref.rs:30:5
++  --> $DIR/forget_ref.rs:26:12
 +   |
 +LL |     forget(reference3);
 +   |            ^^^^^^^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:30:12
++  --> $DIR/forget_ref.rs:31:5
 +   |
 +LL |     forget(&val);
 +   |     ^^^^^^^^^^^^
 +   |
 +note: argument has type `&T`
-   --> $DIR/forget_ref.rs:38:5
++  --> $DIR/forget_ref.rs:31:12
 +   |
 +LL |     forget(&val);
 +   |            ^^^^
 +
 +error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing
-   --> $DIR/forget_ref.rs:38:22
++  --> $DIR/forget_ref.rs:39:5
 +   |
 +LL |     std::mem::forget(&SomeStruct);
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: argument has type `&SomeStruct`
++  --> $DIR/forget_ref.rs:39:22
 +   |
 +LL |     std::mem::forget(&SomeStruct);
 +   |                      ^^^^^^^^^^^
 +
 +error: aborting due to 9 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..def58afa4fbf2316651aec3456ec1887c0099e49
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++// run-rustfix
++#![warn(clippy::get_first)]
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::VecDeque;
++
++struct Bar {
++    arr: [u32; 3],
++}
++
++impl Bar {
++    fn get(&self, pos: usize) -> Option<&u32> {
++        self.arr.get(pos)
++    }
++}
++
++fn main() {
++    let x = vec![2, 3, 5];
++    let _ = x.first(); // Use x.first()
++    let _ = x.get(1);
++    let _ = x[0];
++
++    let y = [2, 3, 5];
++    let _ = y.first(); // Use y.first()
++    let _ = y.get(1);
++    let _ = y[0];
++
++    let z = &[2, 3, 5];
++    let _ = z.first(); // Use z.first()
++    let _ = z.get(1);
++    let _ = z[0];
++
++    let vecdeque: VecDeque<_> = x.iter().cloned().collect();
++    let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
++    let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
++    let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
++    let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
++    let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
++
++    let bar = Bar { arr: [0, 1, 2] };
++    let _ = bar.get(0); // Do not lint, because Bar is struct.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..85a381854cd3be0a734306362f0961e0e21dbe08
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++// run-rustfix
++#![warn(clippy::get_first)]
++use std::collections::BTreeMap;
++use std::collections::HashMap;
++use std::collections::VecDeque;
++
++struct Bar {
++    arr: [u32; 3],
++}
++
++impl Bar {
++    fn get(&self, pos: usize) -> Option<&u32> {
++        self.arr.get(pos)
++    }
++}
++
++fn main() {
++    let x = vec![2, 3, 5];
++    let _ = x.get(0); // Use x.first()
++    let _ = x.get(1);
++    let _ = x[0];
++
++    let y = [2, 3, 5];
++    let _ = y.get(0); // Use y.first()
++    let _ = y.get(1);
++    let _ = y[0];
++
++    let z = &[2, 3, 5];
++    let _ = z.get(0); // Use z.first()
++    let _ = z.get(1);
++    let _ = z[0];
++
++    let vecdeque: VecDeque<_> = x.iter().cloned().collect();
++    let hashmap: HashMap<u8, char> = HashMap::from_iter(vec![(0, 'a'), (1, 'b')]);
++    let btreemap: BTreeMap<u8, char> = BTreeMap::from_iter(vec![(0, 'a'), (1, 'b')]);
++    let _ = vecdeque.get(0); // Do not lint, because VecDeque is not slice.
++    let _ = hashmap.get(&0); // Do not lint, because HashMap is not slice.
++    let _ = btreemap.get(&0); // Do not lint, because BTreeMap is not slice.
++
++    let bar = Bar { arr: [0, 1, 2] };
++    let _ = bar.get(0); // Do not lint, because Bar is struct.
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..466beff9c92df65d6989d6642c7eb1b635b763b6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: accessing first element with `x.get(0)`
++  --> $DIR/get_first.rs:19:13
++   |
++LL |     let _ = x.get(0); // Use x.first()
++   |             ^^^^^^^^ help: try: `x.first()`
++   |
++   = note: `-D clippy::get-first` implied by `-D warnings`
++
++error: accessing first element with `y.get(0)`
++  --> $DIR/get_first.rs:24:13
++   |
++LL |     let _ = y.get(0); // Use y.first()
++   |             ^^^^^^^^ help: try: `y.first()`
++
++error: accessing first element with `z.get(0)`
++  --> $DIR/get_first.rs:29:13
++   |
++LL |     let _ = z.get(0); // Use z.first()
++   |             ^^^^^^^^ help: try: `z.first()`
++
++error: aborting due to 3 previous errors
++
index c8b363f9c38e682eccc2f99ebd4a94ff98bf32e0,0000000000000000000000000000000000000000..1e90b37687a152bf2d0373ef8b2d525fc3697465
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,49 @@@
-     let _ = x.last(); // ~ERROR Use x.last()
 +// run-rustfix
 +
 +#![warn(clippy::get_last_with_len)]
++#![allow(unused)]
++
++use std::collections::VecDeque;
 +
 +fn dont_use_last() {
 +    let x = vec![2, 3, 5];
-     dont_use_last();
-     indexing_two_from_end();
-     index_into_last();
-     use_last_with_different_vec_length();
++    let _ = x.last();
 +}
 +
 +fn indexing_two_from_end() {
 +    let x = vec![2, 3, 5];
 +    let _ = x.get(x.len() - 2);
 +}
 +
 +fn index_into_last() {
 +    let x = vec![2, 3, 5];
 +    let _ = x[x.len() - 1];
 +}
 +
 +fn use_last_with_different_vec_length() {
 +    let x = vec![2, 3, 5];
 +    let y = vec!['a', 'b', 'c'];
 +    let _ = x.get(y.len() - 1);
 +}
 +
++struct S {
++    field: Vec<usize>,
++}
++
++fn in_field(s: &S) {
++    let _ = s.field.last();
++}
++
 +fn main() {
++    let slice = &[1, 2, 3];
++    let _ = slice.last();
++
++    let array = [4, 5, 6];
++    let _ = array.last();
++
++    let deq = VecDeque::from([7, 8, 9]);
++    let _ = deq.back();
++
++    let nested = [[1]];
++    let _ = nested[0].last();
 +}
index bf9cb2d7e0ccd6e72d8f4c91402ab603d1faf8f1,0000000000000000000000000000000000000000..d63a731bd524681b297b63bd7146d0f5a3c73af9
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,49 @@@
-     let _ = x.get(x.len() - 1); // ~ERROR Use x.last()
 +// run-rustfix
 +
 +#![warn(clippy::get_last_with_len)]
++#![allow(unused)]
++
++use std::collections::VecDeque;
 +
 +fn dont_use_last() {
 +    let x = vec![2, 3, 5];
-     dont_use_last();
-     indexing_two_from_end();
-     index_into_last();
-     use_last_with_different_vec_length();
++    let _ = x.get(x.len() - 1);
 +}
 +
 +fn indexing_two_from_end() {
 +    let x = vec![2, 3, 5];
 +    let _ = x.get(x.len() - 2);
 +}
 +
 +fn index_into_last() {
 +    let x = vec![2, 3, 5];
 +    let _ = x[x.len() - 1];
 +}
 +
 +fn use_last_with_different_vec_length() {
 +    let x = vec![2, 3, 5];
 +    let y = vec!['a', 'b', 'c'];
 +    let _ = x.get(y.len() - 1);
 +}
 +
++struct S {
++    field: Vec<usize>,
++}
++
++fn in_field(s: &S) {
++    let _ = s.field.get(s.field.len() - 1);
++}
++
 +fn main() {
++    let slice = &[1, 2, 3];
++    let _ = slice.get(slice.len() - 1);
++
++    let array = [4, 5, 6];
++    let _ = array.get(array.len() - 1);
++
++    let deq = VecDeque::from([7, 8, 9]);
++    let _ = deq.get(deq.len() - 1);
++
++    let nested = [[1]];
++    let _ = nested[0].get(nested[0].len() - 1);
 +}
index 55baf87384a2499d8ddd5050564c7a03bfc8b534,0000000000000000000000000000000000000000..ac8dd6c2e41a80fc7a9ba5b5444035e4f9f76414
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,40 @@@
-   --> $DIR/get_last_with_len.rs:7:13
 +error: accessing last element with `x.get(x.len() - 1)`
- LL |     let _ = x.get(x.len() - 1); // ~ERROR Use x.last()
++  --> $DIR/get_last_with_len.rs:10:13
 +   |
- error: aborting due to previous error
++LL |     let _ = x.get(x.len() - 1);
 +   |             ^^^^^^^^^^^^^^^^^^ help: try: `x.last()`
 +   |
 +   = note: `-D clippy::get-last-with-len` implied by `-D warnings`
 +
++error: accessing last element with `s.field.get(s.field.len() - 1)`
++  --> $DIR/get_last_with_len.rs:34:13
++   |
++LL |     let _ = s.field.get(s.field.len() - 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.field.last()`
++
++error: accessing last element with `slice.get(slice.len() - 1)`
++  --> $DIR/get_last_with_len.rs:39:13
++   |
++LL |     let _ = slice.get(slice.len() - 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `slice.last()`
++
++error: accessing last element with `array.get(array.len() - 1)`
++  --> $DIR/get_last_with_len.rs:42:13
++   |
++LL |     let _ = array.get(array.len() - 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array.last()`
++
++error: accessing last element with `deq.get(deq.len() - 1)`
++  --> $DIR/get_last_with_len.rs:45:13
++   |
++LL |     let _ = deq.get(deq.len() - 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try: `deq.back()`
++
++error: accessing last element with `nested[0].get(nested[0].len() - 1)`
++  --> $DIR/get_last_with_len.rs:48:13
++   |
++LL |     let _ = nested[0].get(nested[0].len() - 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `nested[0].last()`
++
++error: aborting due to 6 previous errors
 +
index 8f165d675890ca9fc9cd4f9edabdef8c83d2f0da,0000000000000000000000000000000000000000..5827fc7d76e60b8e71303caf3cbac793d1d585de
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- #![allow(unused_mut, clippy::from_iter_instead_of_collect)]
 +// run-rustfix
 +
++#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
 +#![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[1];
 +        let _ = &some_slice[0];
 +        let _ = &some_vec[0];
 +        let _ = &some_vecdeque[0];
 +        let _ = &some_hashmap[&1];
 +        let _ = &some_btreemap[&1];
 +        #[allow(clippy::unwrap_used)]
 +        let _ = false_positive.get(0).unwrap();
 +        // Test with deref
 +        let _: u8 = boxed_slice[1];
 +    }
 +
 +    {
 +        // Test `get_mut().unwrap()`
 +        boxed_slice[0] = 1;
 +        some_slice[0] = 1;
 +        some_vec[0] = 1;
 +        some_vecdeque[0] = 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[0..1].to_vec();
 +        let _ = some_vec[0..1].to_vec();
 +    }
 +}
index 786749daa746e802a228c5cf1165ee544ce1fc20,0000000000000000000000000000000000000000..a2a323c14fb7c827f6a439e8495d17708b3fa3a3
mode 100644,000000..100644
--- /dev/null
@@@ -1,67 -1,0 +1,67 @@@
- #![allow(unused_mut, clippy::from_iter_instead_of_collect)]
 +// run-rustfix
 +
++#![allow(unused_mut, clippy::from_iter_instead_of_collect, clippy::get_first)]
 +#![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();
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f9cebe212abd89844733156582e3c64771c0524
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,119 @@@
++// run-rustfix
++
++#![warn(clippy::identity_op)]
++#![allow(
++    clippy::eq_op,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::op_ref,
++    clippy::double_parens,
++    unused
++)]
++
++use std::fmt::Write as _;
++
++const ONE: i64 = 1;
++const NEG_ONE: i64 = -1;
++const ZERO: i64 = 0;
++
++struct A(String);
++
++impl std::ops::Shl<i32> for A {
++    type Output = A;
++    fn shl(mut self, other: i32) -> Self {
++        let _ = write!(self.0, "{}", other);
++        self
++    }
++}
++
++struct Length(u8);
++struct Meter;
++
++impl core::ops::Mul<Meter> for u8 {
++    type Output = Length;
++    fn mul(self, _: Meter) -> Length {
++        Length(self)
++    }
++}
++
++#[rustfmt::skip]
++fn main() {
++    let x = 0;
++
++    x;
++    x;
++    x + 1;
++    x;
++    1 + x;
++    x - ZERO; //no error, as we skip lookups (for now)
++    x;
++    ((ZERO)) | x; //no error, as we skip lookups (for now)
++
++    x;
++    x;
++    x / ONE; //no error, as we skip lookups (for now)
++
++    x / 2; //no false positive
++
++    x & NEG_ONE; //no error, as we skip lookups (for now)
++    x;
++
++    let u: u8 = 0;
++    u;
++
++    1 << 0; // no error, this case is allowed, see issue 3430
++    42;
++    1;
++    42;
++    &x;
++    x;
++
++    let mut a = A("".into());
++    let b = a << 0; // no error: non-integer
++
++    1 * Meter; // no error: non-integer
++
++    2;
++    -2;
++    2 + x;
++    -2 + x;
++    x + 1;
++    (x + 1) % 3; // no error
++    4 % 3; // no error
++    4 % -3; // no error
++
++    // See #8724
++    let a = 0;
++    let b = true;
++    (if b { 1 } else { 2 });
++    (if b { 1 } else { 2 }) + if b { 3 } else { 4 };
++    (match a { 0 => 10, _ => 20 });
++    (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 };
++    (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 };
++    (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
++    (if b { 1 } else { 2 });
++
++    ({ a }) + 3;
++    ({ a } * 2);
++    (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 };
++
++    fn f(_: i32) {
++        todo!();
++    }
++    f(a + { 8 * 5 });
++    f(if b { 1 } else { 2 } + 3);
++    const _: i32 = { 2 * 4 } + 3;
++    const _: i32 = { 1 + 2 * 3 } + 3;
++
++    a as usize;
++    let _ = a as usize;
++    ({ a } as usize);
++
++    2 * { a };
++    (({ a } + 4));
++    1;
++}
++
++pub fn decide(a: bool, b: bool) -> u32 {
++    (if a { 1 } else { 2 }) + if b { 3 } else { 5 }
++}
index fec54d00ccb4b8869b90a7c4cdb55e510f8ff543,0000000000000000000000000000000000000000..ca799c9cfac0f8a4c7e82c24d2e605d770fec561
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,119 @@@
- #[allow(
-     clippy::eq_op,
-     clippy::no_effect,
-     clippy::unnecessary_operation,
-     clippy::op_ref,
-     clippy::double_parens
- )]
- #[warn(clippy::identity_op)]
++// run-rustfix
++
++#![warn(clippy::identity_op)]
++#![allow(
++    clippy::eq_op,
++    clippy::no_effect,
++    clippy::unnecessary_operation,
++    clippy::op_ref,
++    clippy::double_parens,
++    unused
++)]
++
 +use std::fmt::Write as _;
 +
 +const ONE: i64 = 1;
 +const NEG_ONE: i64 = -1;
 +const ZERO: i64 = 0;
 +
 +struct A(String);
 +
 +impl std::ops::Shl<i32> for A {
 +    type Output = A;
 +    fn shl(mut self, other: i32) -> Self {
 +        let _ = write!(self.0, "{}", other);
 +        self
 +    }
 +}
 +
 +struct Length(u8);
 +struct Meter;
 +
 +impl core::ops::Mul<Meter> for u8 {
 +    type Output = Length;
 +    fn mul(self, _: Meter) -> Length {
 +        Length(self)
 +    }
 +}
 +
-     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }; // no error
 +#[rustfmt::skip]
 +fn main() {
 +    let x = 0;
 +
 +    x + 0;
 +    x + (1 - 1);
 +    x + 1;
 +    0 + x;
 +    1 + x;
 +    x - ZERO; //no error, as we skip lookups (for now)
 +    x | (0);
 +    ((ZERO)) | x; //no error, as we skip lookups (for now)
 +
 +    x * 1;
 +    1 * x;
 +    x / ONE; //no error, as we skip lookups (for now)
 +
 +    x / 2; //no false positive
 +
 +    x & NEG_ONE; //no error, as we skip lookups (for now)
 +    -1 & x;
 +
 +    let u: u8 = 0;
 +    u & 255;
 +
 +    1 << 0; // no error, this case is allowed, see issue 3430
 +    42 << 0;
 +    1 >> 0;
 +    42 >> 0;
 +    &x >> 0;
 +    x >> &0;
 +
 +    let mut a = A("".into());
 +    let b = a << 0; // no error: non-integer
 +
 +    1 * Meter; // no error: non-integer
 +
 +    2 % 3;
 +    -2 % 3;
 +    2 % -3 + x;
 +    -2 % -3 + x;
 +    x + 1 % 3;
 +    (x + 1) % 3; // no error
 +    4 % 3; // no error
 +    4 % -3; // no error
 +
 +    // See #8724
 +    let a = 0;
 +    let b = true;
 +    0 + if b { 1 } else { 2 };
-     0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 }; // no error
-     0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 }; // no error
-     0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 }; // no error
-     
-     0 + if b { 0 + 1 } else { 2 };
-     0 + match a { 0 =>  0 + 10, _ => 20 };
-     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
-     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
-     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
-     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
-     
-     0 + { a } + 3; // no error
-     0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 }; // no error
-     
++    0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
 +    0 + match a { 0 => 10, _ => 20 };
-     f(0 + if b { 1 } else { 2 } + 3); // no error
++    0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
++    0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
++    0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
++    (if b { 1 } else { 2 }) + 0;
++
++    0 + { a } + 3;
++    0 + { a } * 2;
++    0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
++
 +    fn f(_: i32) {
 +        todo!();
 +    }
 +    f(1 * a + { 8 * 5 });
-     const _: i32 = 0 + { 1 + 2 * 3 } + 3; // no error
++    f(0 + if b { 1 } else { 2 } + 3);
 +    const _: i32 = { 2 * 4 } + 0 + 3;
++    const _: i32 = 0 + { 1 + 2 * 3 } + 3;
++
++    0 + a as usize;
++    let _ = 0 + a as usize;
++    0 + { a } as usize;
++
++    2 * (0 + { a });
++    1 * ({ a } + 4);
++    1 * 1;
++}
++
++pub fn decide(a: bool, b: bool) -> u32 {
++    0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
 +}
index d8cb65839cbba5595b6c1cc422427ca30603983b,0000000000000000000000000000000000000000..1a104a20b841c1688e9412fffaf261867ef26905
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,238 @@@
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:39:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:43:5
 +   |
 +LL |     x + 0;
-    |     ^^^^^
++   |     ^^^^^ help: consider reducing it to: `x`
 +   |
 +   = note: `-D clippy::identity-op` implied by `-D warnings`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:40:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:44:5
 +   |
 +LL |     x + (1 - 1);
-    |     ^^^^^^^^^^^
++   |     ^^^^^^^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:42:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:46:5
 +   |
 +LL |     0 + x;
-    |     ^^^^^
++   |     ^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:45:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:49:5
 +   |
 +LL |     x | (0);
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:48:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:52:5
 +   |
 +LL |     x * 1;
-    |     ^^^^^
++   |     ^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:49:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:53:5
 +   |
 +LL |     1 * x;
-    |     ^^^^^
++   |     ^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:55:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:59:5
 +   |
 +LL |     -1 & x;
-    |     ^^^^^^
++   |     ^^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `u`
-   --> $DIR/identity_op.rs:58:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:62:5
 +   |
 +LL |     u & 255;
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `u`
 +
- error: the operation is ineffective. Consider reducing it to `42`
-   --> $DIR/identity_op.rs:61:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:65:5
 +   |
 +LL |     42 << 0;
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `42`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:62:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:66:5
 +   |
 +LL |     1 >> 0;
-    |     ^^^^^^
++   |     ^^^^^^ help: consider reducing it to: `1`
 +
- error: the operation is ineffective. Consider reducing it to `42`
-   --> $DIR/identity_op.rs:63:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:67:5
 +   |
 +LL |     42 >> 0;
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `42`
 +
- error: the operation is ineffective. Consider reducing it to `&x`
-   --> $DIR/identity_op.rs:64:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:68:5
 +   |
 +LL |     &x >> 0;
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `&x`
 +
- error: the operation is ineffective. Consider reducing it to `x`
-   --> $DIR/identity_op.rs:65:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:69:5
 +   |
 +LL |     x >> &0;
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `x`
 +
- error: the operation is ineffective. Consider reducing it to `2`
-   --> $DIR/identity_op.rs:72:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:76:5
 +   |
 +LL |     2 % 3;
-    |     ^^^^^
++   |     ^^^^^ help: consider reducing it to: `2`
 +
- error: the operation is ineffective. Consider reducing it to `-2`
-   --> $DIR/identity_op.rs:73:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:77:5
 +   |
 +LL |     -2 % 3;
-    |     ^^^^^^
++   |     ^^^^^^ help: consider reducing it to: `-2`
 +
- error: the operation is ineffective. Consider reducing it to `2`
-   --> $DIR/identity_op.rs:74:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:78:5
 +   |
 +LL |     2 % -3 + x;
-    |     ^^^^^^
++   |     ^^^^^^ help: consider reducing it to: `2`
 +
- error: the operation is ineffective. Consider reducing it to `-2`
-   --> $DIR/identity_op.rs:75:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:79:5
 +   |
 +LL |     -2 % -3 + x;
-    |     ^^^^^^^
++   |     ^^^^^^^ help: consider reducing it to: `-2`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:76:9
++error: this operation has no effect
++  --> $DIR/identity_op.rs:80:9
 +   |
 +LL |     x + 1 % 3;
-    |         ^^^^^
++   |         ^^^^^ help: consider reducing it to: `1`
 +
- error: the operation is ineffective. Consider reducing it to `if b { 1 } else { 2 }`
-   --> $DIR/identity_op.rs:84:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:88:5
 +   |
 +LL |     0 + if b { 1 } else { 2 };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
 +
- error: the operation is ineffective. Consider reducing it to `match a { 0 => 10, _ => 20 }`
-   --> $DIR/identity_op.rs:86:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:89:5
++   |
++LL |     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
++
++error: this operation has no effect
++  --> $DIR/identity_op.rs:90:5
 +   |
 +LL |     0 + match a { 0 => 10, _ => 20 };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
 +
- error: the operation is ineffective. Consider reducing it to `if b { 0 + 1 } else { 2 }`
++error: this operation has no effect
 +  --> $DIR/identity_op.rs:91:5
 +   |
- LL |     0 + if b { 0 + 1 } else { 2 };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:91:16
++error: this operation has no effect
++  --> $DIR/identity_op.rs:92:5
 +   |
- LL |     0 + if b { 0 + 1 } else { 2 };
-    |                ^^^^^
++LL |     0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
 +
- error: the operation is ineffective. Consider reducing it to `match a { 0 =>  0 + 10, _ => 20 }`
-   --> $DIR/identity_op.rs:92:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:93:5
++   |
++LL |     0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(match a { 0 => 10, _ => 20 })`
++
++error: this operation has no effect
++  --> $DIR/identity_op.rs:94:5
++   |
++LL |     (if b { 1 } else { 2 }) + 0;
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
++
++error: this operation has no effect
++  --> $DIR/identity_op.rs:96:5
 +   |
- LL |     0 + match a { 0 =>  0 + 10, _ => 20 };
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     0 + { a } + 3;
++   |     ^^^^^^^^^ help: consider reducing it to: `({ a })`
 +
- error: the operation is ineffective. Consider reducing it to `10`
-   --> $DIR/identity_op.rs:92:25
++error: this operation has no effect
++  --> $DIR/identity_op.rs:97:5
 +   |
- LL |     0 + match a { 0 =>  0 + 10, _ => 20 };
-    |                         ^^^^^^
++LL |     0 + { a } * 2;
++   |     ^^^^^^^^^^^^^ help: consider reducing it to: `({ a } * 2)`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:93:16
++error: this operation has no effect
++  --> $DIR/identity_op.rs:98:5
 +   |
- LL |     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
-    |                ^^^^^
++LL |     0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(loop { let mut c = 0; if c == 10 { break c; } c += 1; })`
 +
- error: the operation is ineffective. Consider reducing it to `30`
-   --> $DIR/identity_op.rs:93:52
++error: this operation has no effect
++  --> $DIR/identity_op.rs:103:7
 +   |
- LL |     0 + if b { 0 + 1 } else { 2 } + match a { 0 => 0 + 30, _ => 40 };
-    |                                                    ^^^^^^
++LL |     f(1 * a + { 8 * 5 });
++   |       ^^^^^ help: consider reducing it to: `a`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:95:20
++error: this operation has no effect
++  --> $DIR/identity_op.rs:104:7
 +   |
- LL |     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
-    |                    ^^^^^
++LL |     f(0 + if b { 1 } else { 2 } + 3);
++   |       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `if b { 1 } else { 2 }`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:95:52
++error: this operation has no effect
++  --> $DIR/identity_op.rs:105:20
 +   |
- LL |     let _ = 0 + if 0 + 1 > 0 { 1 } else { 2 } + if 0 + 1 > 0 { 3 } else { 4 };
-    |                                                    ^^^^^
++LL |     const _: i32 = { 2 * 4 } + 0 + 3;
++   |                    ^^^^^^^^^^^^^ help: consider reducing it to: `{ 2 * 4 }`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:96:23
++error: this operation has no effect
++  --> $DIR/identity_op.rs:106:20
 +   |
- LL |     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
-    |                       ^^^^^
++LL |     const _: i32 = 0 + { 1 + 2 * 3 } + 3;
++   |                    ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `{ 1 + 2 * 3 }`
 +
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/identity_op.rs:96:58
++error: this operation has no effect
++  --> $DIR/identity_op.rs:108:5
 +   |
- LL |     let _ = 0 + match 0 + 1 { 0 => 10, _ => 20 } + match 0 + 1  { 0 => 30, _ => 40 };
-    |                                                          ^^^^^
++LL |     0 + a as usize;
++   |     ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
 +
- error: the operation is ineffective. Consider reducing it to `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }`
-   --> $DIR/identity_op.rs:98:5
++error: this operation has no effect
++  --> $DIR/identity_op.rs:109:13
 +   |
- LL |     0 + if b { 1 } else { 2 } + if b { 3 } else { 4 } + 0;
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     let _ = 0 + a as usize;
++   |             ^^^^^^^^^^^^^^ help: consider reducing it to: `a as usize`
 +
- error: the operation is ineffective. Consider reducing it to `a`
-   --> $DIR/identity_op.rs:106:7
++error: this operation has no effect
++  --> $DIR/identity_op.rs:110:5
 +   |
- LL |     f(1 * a + { 8 * 5 });
-    |       ^^^^^
++LL |     0 + { a } as usize;
++   |     ^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `({ a } as usize)`
 +
- error: the operation is ineffective. Consider reducing it to `{ 2 * 4 }`
-   --> $DIR/identity_op.rs:108:20
++error: this operation has no effect
++  --> $DIR/identity_op.rs:112:9
 +   |
- LL |     const _: i32 = { 2 * 4 } + 0 + 3;
-    |                    ^^^^^^^^^^^^^
++LL |     2 * (0 + { a });
++   |         ^^^^^^^^^^^ help: consider reducing it to: `{ a }`
++
++error: this operation has no effect
++  --> $DIR/identity_op.rs:113:5
++   |
++LL |     1 * ({ a } + 4);
++   |     ^^^^^^^^^^^^^^^ help: consider reducing it to: `(({ a } + 4))`
++
++error: this operation has no effect
++  --> $DIR/identity_op.rs:114:5
++   |
++LL |     1 * 1;
++   |     ^^^^^ help: consider reducing it to: `1`
++
++error: this operation has no effect
++  --> $DIR/identity_op.rs:118:5
++   |
++LL |     0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if a { 1 } else { 2 })`
 +
- error: aborting due to 33 previous errors
++error: aborting due to 39 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..33770fc2a2cf9cca67a956157de5fc83599d93d2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++// run-rustfix
++#![warn(clippy::implicit_clone)]
++#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
++use std::borrow::Borrow;
++use std::ffi::{OsStr, OsString};
++use std::path::PathBuf;
++
++fn return_owned_from_slice(slice: &[u32]) -> Vec<u32> {
++    slice.to_owned()
++}
++
++pub fn own_same<T>(v: T) -> T
++where
++    T: ToOwned<Owned = T>,
++{
++    v.to_owned()
++}
++
++pub fn own_same_from_ref<T>(v: &T) -> T
++where
++    T: ToOwned<Owned = T>,
++{
++    v.to_owned()
++}
++
++pub fn own_different<T, U>(v: T) -> U
++where
++    T: ToOwned<Owned = U>,
++{
++    v.to_owned()
++}
++
++#[derive(Copy, Clone)]
++struct Kitten;
++impl Kitten {
++    // badly named method
++    fn to_vec(self) -> Kitten {
++        Kitten {}
++    }
++}
++impl Borrow<BorrowedKitten> for Kitten {
++    fn borrow(&self) -> &BorrowedKitten {
++        static VALUE: BorrowedKitten = BorrowedKitten {};
++        &VALUE
++    }
++}
++
++struct BorrowedKitten;
++impl ToOwned for BorrowedKitten {
++    type Owned = Kitten;
++    fn to_owned(&self) -> Kitten {
++        Kitten {}
++    }
++}
++
++mod weird {
++    #[allow(clippy::ptr_arg)]
++    pub fn to_vec(v: &Vec<u32>) -> Vec<u32> {
++        v.clone()
++    }
++}
++
++fn main() {
++    let vec = vec![5];
++    let _ = return_owned_from_slice(&vec);
++    let _ = vec.clone();
++    let _ = vec.clone();
++
++    let vec_ref = &vec;
++    let _ = return_owned_from_slice(vec_ref);
++    let _ = vec_ref.clone();
++    let _ = vec_ref.clone();
++
++    // we expect no lint for this
++    let _ = weird::to_vec(&vec);
++
++    // we expect no lints for this
++    let slice: &[u32] = &[1, 2, 3, 4, 5];
++    let _ = return_owned_from_slice(slice);
++    let _ = slice.to_owned();
++    let _ = slice.to_vec();
++
++    let str = "hello world".to_string();
++    let _ = str.clone();
++
++    // testing w/ an arbitrary type
++    let kitten = Kitten {};
++    let _ = kitten.clone();
++    let _ = own_same_from_ref(&kitten);
++    // this shouln't lint
++    let _ = kitten.to_vec();
++
++    // we expect no lints for this
++    let borrowed = BorrowedKitten {};
++    let _ = borrowed.to_owned();
++
++    let pathbuf = PathBuf::new();
++    let _ = pathbuf.clone();
++    let _ = pathbuf.clone();
++
++    let os_string = OsString::from("foo");
++    let _ = os_string.clone();
++    let _ = os_string.clone();
++
++    // we expect no lints for this
++    let os_str = OsStr::new("foo");
++    let _ = os_str.to_owned();
++    let _ = os_str.to_os_string();
++
++    // issue #8227
++    let pathbuf_ref = &pathbuf;
++    let pathbuf_ref = &pathbuf_ref;
++    let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf`
++    let _ = (*pathbuf_ref).clone();
++    let pathbuf_ref = &pathbuf_ref;
++    let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf`
++    let _ = (**pathbuf_ref).clone();
++}
index 2549c9f32f9049326e8ae416a651a81526a23b47,0000000000000000000000000000000000000000..fc896525bd2709ba82b41b56073bf8418649488e
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,118 @@@
- #![allow(clippy::redundant_clone)]
++// run-rustfix
 +#![warn(clippy::implicit_clone)]
++#![allow(clippy::clone_on_copy, clippy::redundant_clone)]
 +use std::borrow::Borrow;
 +use std::ffi::{OsStr, OsString};
 +use std::path::PathBuf;
 +
 +fn return_owned_from_slice(slice: &[u32]) -> Vec<u32> {
 +    slice.to_owned()
 +}
 +
 +pub fn own_same<T>(v: T) -> T
 +where
 +    T: ToOwned<Owned = T>,
 +{
 +    v.to_owned()
 +}
 +
 +pub fn own_same_from_ref<T>(v: &T) -> T
 +where
 +    T: ToOwned<Owned = T>,
 +{
 +    v.to_owned()
 +}
 +
 +pub fn own_different<T, U>(v: T) -> U
 +where
 +    T: ToOwned<Owned = U>,
 +{
 +    v.to_owned()
 +}
 +
 +#[derive(Copy, Clone)]
 +struct Kitten;
 +impl Kitten {
 +    // badly named method
 +    fn to_vec(self) -> Kitten {
 +        Kitten {}
 +    }
 +}
 +impl Borrow<BorrowedKitten> for Kitten {
 +    fn borrow(&self) -> &BorrowedKitten {
 +        static VALUE: BorrowedKitten = BorrowedKitten {};
 +        &VALUE
 +    }
 +}
 +
 +struct BorrowedKitten;
 +impl ToOwned for BorrowedKitten {
 +    type Owned = Kitten;
 +    fn to_owned(&self) -> Kitten {
 +        Kitten {}
 +    }
 +}
 +
 +mod weird {
 +    #[allow(clippy::ptr_arg)]
 +    pub fn to_vec(v: &Vec<u32>) -> Vec<u32> {
 +        v.clone()
 +    }
 +}
 +
 +fn main() {
 +    let vec = vec![5];
 +    let _ = return_owned_from_slice(&vec);
 +    let _ = vec.to_owned();
 +    let _ = vec.to_vec();
 +
 +    let vec_ref = &vec;
 +    let _ = return_owned_from_slice(vec_ref);
 +    let _ = vec_ref.to_owned();
 +    let _ = vec_ref.to_vec();
 +
 +    // we expect no lint for this
 +    let _ = weird::to_vec(&vec);
 +
 +    // we expect no lints for this
 +    let slice: &[u32] = &[1, 2, 3, 4, 5];
 +    let _ = return_owned_from_slice(slice);
 +    let _ = slice.to_owned();
 +    let _ = slice.to_vec();
 +
 +    let str = "hello world".to_string();
 +    let _ = str.to_owned();
 +
 +    // testing w/ an arbitrary type
 +    let kitten = Kitten {};
 +    let _ = kitten.to_owned();
 +    let _ = own_same_from_ref(&kitten);
 +    // this shouln't lint
 +    let _ = kitten.to_vec();
 +
 +    // we expect no lints for this
 +    let borrowed = BorrowedKitten {};
 +    let _ = borrowed.to_owned();
 +
 +    let pathbuf = PathBuf::new();
 +    let _ = pathbuf.to_owned();
 +    let _ = pathbuf.to_path_buf();
 +
 +    let os_string = OsString::from("foo");
 +    let _ = os_string.to_owned();
 +    let _ = os_string.to_os_string();
 +
 +    // we expect no lints for this
 +    let os_str = OsStr::new("foo");
 +    let _ = os_str.to_owned();
 +    let _ = os_str.to_os_string();
 +
 +    // issue #8227
 +    let pathbuf_ref = &pathbuf;
 +    let pathbuf_ref = &pathbuf_ref;
 +    let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&PathBuf`
 +    let _ = pathbuf_ref.to_path_buf();
 +    let pathbuf_ref = &pathbuf_ref;
 +    let _ = pathbuf_ref.to_owned(); // Don't lint. Returns `&&PathBuf`
 +    let _ = pathbuf_ref.to_path_buf();
 +}
index 0f4124241907f3b73dd9d38d0d9abac3e885053b,0000000000000000000000000000000000000000..92c1aa58affb2411eeeafbaa816568b23440c7ec
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-   --> $DIR/implicit_clone.rs:65:13
 +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
-   --> $DIR/implicit_clone.rs:66:13
++  --> $DIR/implicit_clone.rs:66:13
 +   |
 +LL |     let _ = vec.to_owned();
 +   |             ^^^^^^^^^^^^^^ help: consider using: `vec.clone()`
 +   |
 +   = note: `-D clippy::implicit-clone` implied by `-D warnings`
 +
 +error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type
-   --> $DIR/implicit_clone.rs:70:13
++  --> $DIR/implicit_clone.rs:67:13
 +   |
 +LL |     let _ = vec.to_vec();
 +   |             ^^^^^^^^^^^^ help: consider using: `vec.clone()`
 +
 +error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
-   --> $DIR/implicit_clone.rs:71:13
++  --> $DIR/implicit_clone.rs:71:13
 +   |
 +LL |     let _ = vec_ref.to_owned();
 +   |             ^^^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()`
 +
 +error: implicitly cloning a `Vec` by calling `to_vec` on its dereferenced type
-   --> $DIR/implicit_clone.rs:83:13
++  --> $DIR/implicit_clone.rs:72:13
 +   |
 +LL |     let _ = vec_ref.to_vec();
 +   |             ^^^^^^^^^^^^^^^^ help: consider using: `vec_ref.clone()`
 +
 +error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
-   --> $DIR/implicit_clone.rs:87:13
++  --> $DIR/implicit_clone.rs:84:13
 +   |
 +LL |     let _ = str.to_owned();
 +   |             ^^^^^^^^^^^^^^ help: consider using: `str.clone()`
 +
 +error: implicitly cloning a `Kitten` by calling `to_owned` on its dereferenced type
-   --> $DIR/implicit_clone.rs:97:13
++  --> $DIR/implicit_clone.rs:88:13
 +   |
 +LL |     let _ = kitten.to_owned();
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `kitten.clone()`
 +
 +error: implicitly cloning a `PathBuf` by calling `to_owned` on its dereferenced type
-   --> $DIR/implicit_clone.rs:98:13
++  --> $DIR/implicit_clone.rs:98:13
 +   |
 +LL |     let _ = pathbuf.to_owned();
 +   |             ^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()`
 +
 +error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type
-   --> $DIR/implicit_clone.rs:101:13
++  --> $DIR/implicit_clone.rs:99:13
 +   |
 +LL |     let _ = pathbuf.to_path_buf();
 +   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `pathbuf.clone()`
 +
 +error: implicitly cloning a `OsString` by calling `to_owned` on its dereferenced type
-   --> $DIR/implicit_clone.rs:102:13
++  --> $DIR/implicit_clone.rs:102:13
 +   |
 +LL |     let _ = os_string.to_owned();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()`
 +
 +error: implicitly cloning a `OsString` by calling `to_os_string` on its dereferenced type
-   --> $DIR/implicit_clone.rs:113:13
++  --> $DIR/implicit_clone.rs:103:13
 +   |
 +LL |     let _ = os_string.to_os_string();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `os_string.clone()`
 +
 +error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type
-   --> $DIR/implicit_clone.rs:116:13
++  --> $DIR/implicit_clone.rs:114:13
 +   |
 +LL |     let _ = pathbuf_ref.to_path_buf();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(*pathbuf_ref).clone()`
 +
 +error: implicitly cloning a `PathBuf` by calling `to_path_buf` on its dereferenced type
++  --> $DIR/implicit_clone.rs:117:13
 +   |
 +LL |     let _ = pathbuf_ref.to_path_buf();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(**pathbuf_ref).clone()`
 +
 +error: aborting due to 12 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..942e99fa8787b0bae936f6997d0b5eec2176f965
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,26 @@@
++// run-rustfix
++#![deny(clippy::while_let_on_iterator)]
++#![allow(unused_mut)]
++
++use std::iter::Iterator;
++
++struct Foo;
++
++impl Foo {
++    fn foo1<I: Iterator<Item = usize>>(mut it: I) {
++        while let Some(_) = it.next() {
++            println!("{:?}", it.size_hint());
++        }
++    }
++
++    fn foo2<I: Iterator<Item = usize>>(mut it: I) {
++        for e in it {
++            println!("{:?}", e);
++        }
++    }
++}
++
++fn main() {
++    Foo::foo1(vec![].into_iter());
++    Foo::foo2(vec![].into_iter());
++}
index da580a1839a176b4bf05325302a26c928a16e732,0000000000000000000000000000000000000000..b000234ea59662dac9c06ad0e05d4d2a2562824b
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,26 @@@
++// run-rustfix
 +#![deny(clippy::while_let_on_iterator)]
++#![allow(unused_mut)]
 +
 +use std::iter::Iterator;
 +
 +struct Foo;
 +
 +impl Foo {
 +    fn foo1<I: Iterator<Item = usize>>(mut it: I) {
 +        while let Some(_) = it.next() {
 +            println!("{:?}", it.size_hint());
 +        }
 +    }
 +
 +    fn foo2<I: Iterator<Item = usize>>(mut it: I) {
 +        while let Some(e) = it.next() {
 +            println!("{:?}", e);
 +        }
 +    }
 +}
 +
 +fn main() {
 +    Foo::foo1(vec![].into_iter());
 +    Foo::foo2(vec![].into_iter());
 +}
index 51b872e21c08582afd3e9bea7943c2d64d0af4b0,0000000000000000000000000000000000000000..4e3ff7522e0bb7c346bcf4b2086792c0dfce4c2a
mode 100644,000000..100644
--- /dev/null
@@@ -1,14 -1,0 +1,14 @@@
-   --> $DIR/issue_2356.rs:15:9
 +error: this loop could be written as a `for` loop
-   --> $DIR/issue_2356.rs:1:9
++  --> $DIR/issue_2356.rs:17:9
 +   |
 +LL |         while let Some(e) = it.next() {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for e in it`
 +   |
 +note: the lint level is defined here
++  --> $DIR/issue_2356.rs:2:9
 +   |
 +LL | #![deny(clippy::while_let_on_iterator)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to previous error
 +
index 11ffc8edb149e8726fe4caedebb3a6113dc4b728,0000000000000000000000000000000000000000..f612d26aaabccd26bb41e0c712de7ddb8327da8d
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,24 @@@
-     let _ = s.get(0);
-     // Should be replaced by s.get(0)
 +// run-rustfix
 +#![warn(clippy::iter_next_slice)]
 +
 +fn main() {
 +    // test code goes here
 +    let s = [1, 2, 3];
 +    let v = vec![1, 2, 3];
 +
-     let _ = v.get(0);
-     // Should be replaced by v.get(0)
++    let _ = s.first();
++    // Should be replaced by s.first()
 +
 +    let _ = s.get(2);
 +    // Should be replaced by s.get(2)
 +
 +    let _ = v.get(5);
 +    // Should be replaced by v.get(5)
 +
++    let _ = v.first();
++    // Should be replaced by v.first()
 +
 +    let o = Some(5);
 +    o.iter().next();
 +    // Shouldn't be linted since this is not a Slice or an Array
 +}
index e0d3aabd54acdfbf9fb06048593d464bfe6e8c39,0000000000000000000000000000000000000000..5195f1c86675fcde359783afbeb8468e5b59f17a
mode 100644,000000..100644
--- /dev/null
@@@ -1,24 -1,0 +1,24 @@@
-     // Should be replaced by s.get(0)
 +// run-rustfix
 +#![warn(clippy::iter_next_slice)]
 +
 +fn main() {
 +    // test code goes here
 +    let s = [1, 2, 3];
 +    let v = vec![1, 2, 3];
 +
 +    let _ = s.iter().next();
-     // Should be replaced by v.get(0)
++    // Should be replaced by s.first()
 +
 +    let _ = s[2..].iter().next();
 +    // Should be replaced by s.get(2)
 +
 +    let _ = v[5..].iter().next();
 +    // Should be replaced by v.get(5)
 +
 +    let _ = v.iter().next();
++    // Should be replaced by v.first()
 +
 +    let o = Some(5);
 +    o.iter().next();
 +    // Shouldn't be linted since this is not a Slice or an Array
 +}
index a78d2c2d5e83801ca2271d255c6e5bdbbb0597bb,0000000000000000000000000000000000000000..d8b89061ff8954ebb4862cad8b0b5e443ab64949
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,28 @@@
-    |             ^^^^^^^^^^^^^^^ help: try calling: `s.get(0)`
 +error: using `.iter().next()` on an array
 +  --> $DIR/iter_next_slice.rs:9:13
 +   |
 +LL |     let _ = s.iter().next();
-    |             ^^^^^^^^^^^^^^^ help: try calling: `v.get(0)`
++   |             ^^^^^^^^^^^^^^^ help: try calling: `s.first()`
 +   |
 +   = note: `-D clippy::iter-next-slice` implied by `-D warnings`
 +
 +error: using `.iter().next()` on a Slice without end index
 +  --> $DIR/iter_next_slice.rs:12:13
 +   |
 +LL |     let _ = s[2..].iter().next();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: try calling: `s.get(2)`
 +
 +error: using `.iter().next()` on a Slice without end index
 +  --> $DIR/iter_next_slice.rs:15:13
 +   |
 +LL |     let _ = v[5..].iter().next();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: try calling: `v.get(5)`
 +
 +error: using `.iter().next()` on an array
 +  --> $DIR/iter_next_slice.rs:18:13
 +   |
 +LL |     let _ = v.iter().next();
++   |             ^^^^^^^^^^^^^^^ help: try calling: `v.first()`
 +
 +error: aborting due to 4 previous errors
 +
index cee9e2372c2271f5a07e5fb30feb72a5bfa9e9d6,0000000000000000000000000000000000000000..23152a13322e82ab3b73d7bb4481db22cc1f9668
mode 100644,000000..100644
--- /dev/null
@@@ -1,103 -1,0 +1,135 @@@
 +// aux-build:macro_rules.rs
 +
 +#![allow(dead_code)]
 +#![allow(unused_variables)]
 +#![warn(clippy::large_enum_variant)]
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +enum LargeEnum {
 +    A(i32),
 +    B([i32; 8000]),
 +}
 +
 +enum GenericEnumOk<T> {
 +    A(i32),
 +    B([T; 8000]),
 +}
 +
 +enum GenericEnum2<T> {
 +    A(i32),
 +    B([i32; 8000]),
 +    C(T, [i32; 8000]),
 +}
 +
 +trait SomeTrait {
 +    type Item;
 +}
 +
 +enum LargeEnumGeneric<A: SomeTrait> {
 +    Var(A::Item),
 +}
 +
 +enum LargeEnum2 {
 +    VariantOk(i32, u32),
 +    ContainingLargeEnum(LargeEnum),
 +}
 +
 +enum LargeEnum3 {
 +    ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
 +    VoidVariant,
 +    StructLikeLittle { x: i32, y: i32 },
 +}
 +
 +enum LargeEnum4 {
 +    VariantOk(i32, u32),
 +    StructLikeLarge { x: [i32; 8000], y: i32 },
 +}
 +
 +enum LargeEnum5 {
 +    VariantOk(i32, u32),
 +    StructLikeLarge2 { x: [i32; 8000] },
 +}
 +
 +enum LargeEnumOk {
 +    LargeA([i32; 8000]),
 +    LargeB([i32; 8001]),
 +}
 +
 +enum LargeEnum6 {
 +    A,
 +    B([u8; 255]),
 +    C([u8; 200]),
 +}
 +
 +enum LargeEnum7 {
 +    A,
 +    B([u8; 1255]),
 +    C([u8; 200]),
 +}
 +
 +enum LargeEnum8 {
 +    VariantOk(i32, u32),
 +    ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
 +}
 +
 +enum LargeEnum9 {
 +    A(Struct<()>),
 +    B(Struct2),
 +}
 +
 +enum LargeEnumOk2<T> {
 +    A(T),
 +    B(Struct2),
 +}
 +
 +enum LargeEnumOk3<T> {
 +    A(Struct<T>),
 +    B(Struct2),
 +}
 +
 +struct Struct<T> {
 +    a: i32,
 +    t: T,
 +}
 +
 +struct Struct2 {
 +    a: [i32; 8000],
 +}
 +
++#[derive(Copy, Clone)]
++enum CopyableLargeEnum {
++    A(bool),
++    B([u128; 4000]),
++}
++
++enum ManuallyCopyLargeEnum {
++    A(bool),
++    B([u128; 4000]),
++}
++
++impl Clone for ManuallyCopyLargeEnum {
++    fn clone(&self) -> Self {
++        *self
++    }
++}
++
++impl Copy for ManuallyCopyLargeEnum {}
++
++enum SomeGenericPossiblyCopyEnum<T> {
++    A(bool, std::marker::PhantomData<T>),
++    B([u64; 4000]),
++}
++
++impl<T: Copy> Clone for SomeGenericPossiblyCopyEnum<T> {
++    fn clone(&self) -> Self {
++        *self
++    }
++}
++
++impl<T: Copy> Copy for SomeGenericPossiblyCopyEnum<T> {}
++
 +fn main() {
 +    large_enum_variant!();
 +}
index cbf2ac972e2b2102d60318d55b129ee66af73e47,0000000000000000000000000000000000000000..0248327262da0f63852ecfa684f6b38c058670f7
mode 100644,000000..100644
--- /dev/null
@@@ -1,131 -1,0 +1,197 @@@
- error: aborting due to 8 previous errors
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:12:5
 +   |
 +LL |     B([i32; 8000]),
 +   |     ^^^^^^^^^^^^^^ this variant is 32000 bytes
 +   |
 +   = note: `-D clippy::large-enum-variant` implied by `-D warnings`
 +note: and the second-largest variant is 4 bytes:
 +  --> $DIR/large_enum_variant.rs:11:5
 +   |
 +LL |     A(i32),
 +   |     ^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     B(Box<[i32; 8000]>),
 +   |       ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:36:5
 +   |
 +LL |     ContainingLargeEnum(LargeEnum),
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
 +   |
 +note: and the second-largest variant is 8 bytes:
 +  --> $DIR/large_enum_variant.rs:35:5
 +   |
 +LL |     VariantOk(i32, u32),
 +   |     ^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     ContainingLargeEnum(Box<LargeEnum>),
 +   |                         ~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:40:5
 +   |
 +LL |     ContainingMoreThanOneField(i32, [i32; 8000], [i32; 9500]),
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70004 bytes
 +   |
 +note: and the second-largest variant is 8 bytes:
 +  --> $DIR/large_enum_variant.rs:42:5
 +   |
 +LL |     StructLikeLittle { x: i32, y: i32 },
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     ContainingMoreThanOneField(i32, Box<[i32; 8000]>, Box<[i32; 9500]>),
 +   |                                     ~~~~~~~~~~~~~~~~  ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:47:5
 +   |
 +LL |     StructLikeLarge { x: [i32; 8000], y: i32 },
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32004 bytes
 +   |
 +note: and the second-largest variant is 8 bytes:
 +  --> $DIR/large_enum_variant.rs:46:5
 +   |
 +LL |     VariantOk(i32, u32),
 +   |     ^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     StructLikeLarge { x: Box<[i32; 8000]>, y: i32 },
 +   |                          ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:52:5
 +   |
 +LL |     StructLikeLarge2 { x: [i32; 8000] },
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 32000 bytes
 +   |
 +note: and the second-largest variant is 8 bytes:
 +  --> $DIR/large_enum_variant.rs:51:5
 +   |
 +LL |     VariantOk(i32, u32),
 +   |     ^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     StructLikeLarge2 { x: Box<[i32; 8000]> },
 +   |                           ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:68:5
 +   |
 +LL |     B([u8; 1255]),
 +   |     ^^^^^^^^^^^^^ this variant is 1255 bytes
 +   |
 +note: and the second-largest variant is 200 bytes:
 +  --> $DIR/large_enum_variant.rs:69:5
 +   |
 +LL |     C([u8; 200]),
 +   |     ^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     B(Box<[u8; 1255]>),
 +   |       ~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:74:5
 +   |
 +LL |     ContainingMoreThanOneField([i32; 8000], [i32; 2], [i32; 9500], [i32; 30]),
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this variant is 70128 bytes
 +   |
 +note: and the second-largest variant is 8 bytes:
 +  --> $DIR/large_enum_variant.rs:73:5
 +   |
 +LL |     VariantOk(i32, u32),
 +   |     ^^^^^^^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     ContainingMoreThanOneField(Box<[i32; 8000]>, [i32; 2], Box<[i32; 9500]>, [i32; 30]),
 +   |                                ~~~~~~~~~~~~~~~~            ~~~~~~~~~~~~~~~~
 +
 +error: large size difference between variants
 +  --> $DIR/large_enum_variant.rs:79:5
 +   |
 +LL |     B(Struct2),
 +   |     ^^^^^^^^^^ this variant is 32000 bytes
 +   |
 +note: and the second-largest variant is 4 bytes:
 +  --> $DIR/large_enum_variant.rs:78:5
 +   |
 +LL |     A(Struct<()>),
 +   |     ^^^^^^^^^^^^^
 +help: consider boxing the large fields to reduce the total size of the enum
 +   |
 +LL |     B(Box<Struct2>),
 +   |       ~~~~~~~~~~~~
 +
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:104:5
++   |
++LL |     B([u128; 4000]),
++   |     ^^^^^^^^^^^^^^^ this variant is 64000 bytes
++   |
++note: and the second-largest variant is 1 bytes:
++  --> $DIR/large_enum_variant.rs:103:5
++   |
++LL |     A(bool),
++   |     ^^^^^^^
++note: boxing a variant would require the type no longer be `Copy`
++  --> $DIR/large_enum_variant.rs:102:6
++   |
++LL | enum CopyableLargeEnum {
++   |      ^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++  --> $DIR/large_enum_variant.rs:104:5
++   |
++LL |     B([u128; 4000]),
++   |     ^^^^^^^^^^^^^^^
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:109:5
++   |
++LL |     B([u128; 4000]),
++   |     ^^^^^^^^^^^^^^^ this variant is 64000 bytes
++   |
++note: and the second-largest variant is 1 bytes:
++  --> $DIR/large_enum_variant.rs:108:5
++   |
++LL |     A(bool),
++   |     ^^^^^^^
++note: boxing a variant would require the type no longer be `Copy`
++  --> $DIR/large_enum_variant.rs:107:6
++   |
++LL | enum ManuallyCopyLargeEnum {
++   |      ^^^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++  --> $DIR/large_enum_variant.rs:109:5
++   |
++LL |     B([u128; 4000]),
++   |     ^^^^^^^^^^^^^^^
++
++error: large size difference between variants
++  --> $DIR/large_enum_variant.rs:122:5
++   |
++LL |     B([u64; 4000]),
++   |     ^^^^^^^^^^^^^^ this variant is 32000 bytes
++   |
++note: and the second-largest variant is 1 bytes:
++  --> $DIR/large_enum_variant.rs:121:5
++   |
++LL |     A(bool, std::marker::PhantomData<T>),
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++note: boxing a variant would require the type no longer be `Copy`
++  --> $DIR/large_enum_variant.rs:120:6
++   |
++LL | enum SomeGenericPossiblyCopyEnum<T> {
++   |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++help: consider boxing the large fields to reduce the total size of the enum
++  --> $DIR/large_enum_variant.rs:122:5
++   |
++LL |     B([u64; 4000]),
++   |     ^^^^^^^^^^^^^^
++
++error: aborting due to 11 previous errors
 +
index fec3a95edd62da54cddde339c8566e0fa56656e9,0000000000000000000000000000000000000000..928e5bd509c3fbf6c358883647b747beba2abdc1
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,68 @@@
 +// run-rustfix
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(clippy::let_underscore_drop)]
 +#![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()
++    //         .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)
++        .and_then(|_| {
++// we need some newlines
++// so that the span is big enough
++// we need some newlines
++// so that the span is big enough
++// for a splitted output of the diagnostic
++            Some("")
++ // whitespace beforehand is important as well
++        });
 +}
index aa1f76e335af0dad3a395921b3e25e6fbf82b6a4,0000000000000000000000000000000000000000..4345c6eee746120afc03cb46fc3e6f79a2d94f40
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,67 @@@
 +// run-rustfix
 +
 +#![warn(clippy::all, clippy::pedantic)]
 +#![allow(clippy::let_underscore_drop)]
 +#![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 splitted output of the diagnostic
++            Some("")
++ // whitespace beforehand is important as well
++        })
++        .flatten();
 +}
index c91c73846b69fb475a8d2d35064c8352b4aca6e9,0000000000000000000000000000000000000000..828e24acaad6c71ce4de550f0f3bb2c02418d8ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,106 @@@
- error: aborting due to 7 previous errors
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:18:47
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().map(option_id).flatten().collect();
 +   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::map-flatten` implied by `-D warnings`
 +help: try replacing `map` with `filter_map`, and remove the `.flatten()`
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id).collect();
 +   |                                               ~~~~~~~~~~~~~~~~~~~~~
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:19: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()`
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_ref).collect();
 +   |                                               ~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:20: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()`
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(option_id_closure).collect();
 +   |                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:21: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()`
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().filter_map(|x| x.checked_add(1)).collect();
 +   |                                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 +
 +error: called `map(..).flatten()` on `Iterator`
 +  --> $DIR/map_flatten_fixable.rs:24: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()`
 +   |
 +LL |     let _: Vec<_> = vec![5_i8; 6].into_iter().flat_map(|x| 0..x).collect();
 +   |                                               ~~~~~~~~~~~~~~~~~~
 +
 +error: called `map(..).flatten()` on `Option`
 +  --> $DIR/map_flatten_fixable.rs:27:40
 +   |
 +LL |     let _: Option<_> = (Some(Some(1))).map(|x| x).flatten();
 +   |                                        ^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try replacing `map` with `and_then`, and remove the `.flatten()`
 +   |
 +LL |     let _: Option<_> = (Some(Some(1))).and_then(|x| x);
 +   |                                        ~~~~~~~~~~~~~~~
 +
 +error: called `map(..).flatten()` on `Result`
 +  --> $DIR/map_flatten_fixable.rs:30:42
 +   |
 +LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
 +   |                                          ^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try replacing `map` with `and_then`, and remove the `.flatten()`
 +   |
 +LL |     let _: Result<_, &str> = (Ok(Ok(1))).and_then(|x| x);
 +   |                                          ~~~~~~~~~~~~~~~
 +
++error: called `map(..).flatten()` on `Option`
++  --> $DIR/map_flatten_fixable.rs:59:10
++   |
++LL |           .map(|_| {
++   |  __________^
++LL | | // we need some newlines
++LL | | // so that the span is big enough
++LL | | // for a splitted output of the diagnostic
++...  |
++LL | |         })
++LL | |         .flatten();
++   | |__________________^
++   |
++help: try replacing `map` with `and_then`
++   |
++LL ~         .and_then(|_| {
++LL + // we need some newlines
++LL + // so that the span is big enough
++   |
++help: and remove the `.flatten()`
++   |
++LL +             Some("")
++LL +  // whitespace beforehand is important as well
++LL ~         });
++   |
++
++error: aborting due to 8 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1b6c2d92412185a30b5034e6336928b782d96537
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,118 @@@
++// run-rustfix
++#![warn(clippy::match_ref_pats)]
++#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)]
++
++fn ref_pats() {
++    {
++        let v = &Some(0);
++        match *v {
++            Some(v) => println!("{:?}", v),
++            None => println!("none"),
++        }
++        match v {
++            // This doesn't trigger; we have a different pattern.
++            &Some(v) => println!("some"),
++            other => println!("other"),
++        }
++    }
++    let tup = &(1, 2);
++    match tup {
++        &(v, 1) => println!("{}", v),
++        _ => println!("none"),
++    }
++    // Special case: using `&` both in expr and pats.
++    let w = Some(0);
++    match w {
++        Some(v) => println!("{:?}", v),
++        None => println!("none"),
++    }
++    // False positive: only wildcard pattern.
++    let w = Some(0);
++    #[allow(clippy::match_single_binding)]
++    match w {
++        _ => println!("none"),
++    }
++
++    let a = &Some(0);
++    if a.is_none() {
++        println!("none");
++    }
++
++    let b = Some(0);
++    if b.is_none() {
++        println!("none");
++    }
++}
++
++mod ice_3719 {
++    macro_rules! foo_variant(
++        ($idx:expr) => (Foo::get($idx).unwrap())
++    );
++
++    enum Foo {
++        A,
++        B,
++    }
++
++    impl Foo {
++        fn get(idx: u8) -> Option<&'static Self> {
++            match idx {
++                0 => Some(&Foo::A),
++                1 => Some(&Foo::B),
++                _ => None,
++            }
++        }
++    }
++
++    fn ice_3719() {
++        // ICE #3719
++        match foo_variant!(0) {
++            &Foo::A => println!("A"),
++            _ => println!("Wild"),
++        }
++    }
++}
++
++mod issue_7740 {
++    macro_rules! foobar_variant(
++        ($idx:expr) => (FooBar::get($idx).unwrap())
++    );
++
++    enum FooBar {
++        Foo,
++        Bar,
++        FooBar,
++        BarFoo,
++    }
++
++    impl FooBar {
++        fn get(idx: u8) -> Option<&'static Self> {
++            match idx {
++                0 => Some(&FooBar::Foo),
++                1 => Some(&FooBar::Bar),
++                2 => Some(&FooBar::FooBar),
++                3 => Some(&FooBar::BarFoo),
++                _ => None,
++            }
++        }
++    }
++
++    fn issue_7740() {
++        // Issue #7740
++        match *foobar_variant!(0) {
++            FooBar::Foo => println!("Foo"),
++            FooBar::Bar => println!("Bar"),
++            FooBar::FooBar => println!("FooBar"),
++            _ => println!("Wild"),
++        }
++
++        // This shouldn't trigger
++        if let &FooBar::BarFoo = foobar_variant!(3) {
++            println!("BarFoo");
++        } else {
++            println!("Wild");
++        }
++    }
++}
++
++fn main() {}
index 7e3674ab8c9f2c633094cce82c11d3351ebb8cb8,0000000000000000000000000000000000000000..68dfac4e2e978ba44c018e17701be7e7c2569664
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,118 @@@
- #![allow(clippy::equatable_if_let, clippy::enum_variant_names)]
++// run-rustfix
 +#![warn(clippy::match_ref_pats)]
++#![allow(dead_code, unused_variables, clippy::equatable_if_let, clippy::enum_variant_names)]
 +
 +fn ref_pats() {
 +    {
 +        let v = &Some(0);
 +        match v {
 +            &Some(v) => println!("{:?}", v),
 +            &None => println!("none"),
 +        }
 +        match v {
 +            // This doesn't trigger; we have a different pattern.
 +            &Some(v) => println!("some"),
 +            other => println!("other"),
 +        }
 +    }
 +    let tup = &(1, 2);
 +    match tup {
 +        &(v, 1) => println!("{}", v),
 +        _ => println!("none"),
 +    }
 +    // Special case: using `&` both in expr and pats.
 +    let w = Some(0);
 +    match &w {
 +        &Some(v) => println!("{:?}", v),
 +        &None => println!("none"),
 +    }
 +    // False positive: only wildcard pattern.
 +    let w = Some(0);
 +    #[allow(clippy::match_single_binding)]
 +    match w {
 +        _ => println!("none"),
 +    }
 +
 +    let a = &Some(0);
 +    if let &None = a {
 +        println!("none");
 +    }
 +
 +    let b = Some(0);
 +    if let &None = &b {
 +        println!("none");
 +    }
 +}
 +
 +mod ice_3719 {
 +    macro_rules! foo_variant(
 +        ($idx:expr) => (Foo::get($idx).unwrap())
 +    );
 +
 +    enum Foo {
 +        A,
 +        B,
 +    }
 +
 +    impl Foo {
 +        fn get(idx: u8) -> Option<&'static Self> {
 +            match idx {
 +                0 => Some(&Foo::A),
 +                1 => Some(&Foo::B),
 +                _ => None,
 +            }
 +        }
 +    }
 +
 +    fn ice_3719() {
 +        // ICE #3719
 +        match foo_variant!(0) {
 +            &Foo::A => println!("A"),
 +            _ => println!("Wild"),
 +        }
 +    }
 +}
 +
 +mod issue_7740 {
 +    macro_rules! foobar_variant(
 +        ($idx:expr) => (FooBar::get($idx).unwrap())
 +    );
 +
 +    enum FooBar {
 +        Foo,
 +        Bar,
 +        FooBar,
 +        BarFoo,
 +    }
 +
 +    impl FooBar {
 +        fn get(idx: u8) -> Option<&'static Self> {
 +            match idx {
 +                0 => Some(&FooBar::Foo),
 +                1 => Some(&FooBar::Bar),
 +                2 => Some(&FooBar::FooBar),
 +                3 => Some(&FooBar::BarFoo),
 +                _ => None,
 +            }
 +        }
 +    }
 +
 +    fn issue_7740() {
 +        // Issue #7740
 +        match foobar_variant!(0) {
 +            &FooBar::Foo => println!("Foo"),
 +            &FooBar::Bar => println!("Bar"),
 +            &FooBar::FooBar => println!("FooBar"),
 +            _ => println!("Wild"),
 +        }
 +
 +        // This shouldn't trigger
 +        if let &FooBar::BarFoo = foobar_variant!(3) {
 +            println!("BarFoo");
 +        } else {
 +            println!("Wild");
 +        }
 +    }
 +}
 +
 +fn main() {}
index 901820077e20e974b651c9a68ef26df9b364cf59,0000000000000000000000000000000000000000..353f7399d9c27007744c4f2cf79905405052ef82
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,68 @@@
-   --> $DIR/match_ref_pats.rs:7:9
 +error: you don't need to add `&` to all patterns
-   --> $DIR/match_ref_pats.rs:24:5
++  --> $DIR/match_ref_pats.rs:8:9
 +   |
 +LL | /         match v {
 +LL | |             &Some(v) => println!("{:?}", v),
 +LL | |             &None => println!("none"),
 +LL | |         }
 +   | |_________^
 +   |
 +   = note: `-D clippy::match-ref-pats` implied by `-D warnings`
 +help: instead of prefixing all patterns with `&`, you can dereference the expression
 +   |
 +LL ~         match *v {
 +LL ~             Some(v) => println!("{:?}", v),
 +LL ~             None => println!("none"),
 +   |
 +
 +error: you don't need to add `&` to both the expression and the patterns
-   --> $DIR/match_ref_pats.rs:36:12
++  --> $DIR/match_ref_pats.rs:25:5
 +   |
 +LL | /     match &w {
 +LL | |         &Some(v) => println!("{:?}", v),
 +LL | |         &None => println!("none"),
 +LL | |     }
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     match w {
 +LL ~         Some(v) => println!("{:?}", v),
 +LL ~         None => println!("none"),
 +   |
 +
 +error: redundant pattern matching, consider using `is_none()`
-   --> $DIR/match_ref_pats.rs:41:12
++  --> $DIR/match_ref_pats.rs:37:12
 +   |
 +LL |     if let &None = a {
 +   |     -------^^^^^---- help: try this: `if a.is_none()`
 +   |
 +   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 +
 +error: redundant pattern matching, consider using `is_none()`
-   --> $DIR/match_ref_pats.rs:101:9
++  --> $DIR/match_ref_pats.rs:42:12
 +   |
 +LL |     if let &None = &b {
 +   |     -------^^^^^----- help: try this: `if b.is_none()`
 +
 +error: you don't need to add `&` to all patterns
++  --> $DIR/match_ref_pats.rs:102:9
 +   |
 +LL | /         match foobar_variant!(0) {
 +LL | |             &FooBar::Foo => println!("Foo"),
 +LL | |             &FooBar::Bar => println!("Bar"),
 +LL | |             &FooBar::FooBar => println!("FooBar"),
 +LL | |             _ => println!("Wild"),
 +LL | |         }
 +   | |_________^
 +   |
 +help: instead of prefixing all patterns with `&`, you can dereference the expression
 +   |
 +LL ~         match *foobar_variant!(0) {
 +LL ~             FooBar::Foo => println!("Foo"),
 +LL ~             FooBar::Bar => println!("Bar"),
 +LL ~             FooBar::FooBar => println!("FooBar"),
 +   |
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e436bcf495fd23042108e31312e2927694d42026
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,186 @@@
++// run-rustfix
++#![warn(clippy::match_str_case_mismatch)]
++#![allow(dead_code)]
++
++// Valid
++
++fn as_str_match() {
++    let var = "BAR";
++
++    match var.to_ascii_lowercase().as_str() {
++        "foo" => {},
++        "bar" => {},
++        _ => {},
++    }
++}
++
++fn non_alphabetic() {
++    let var = "~!@#$%^&*()-_=+FOO";
++
++    match var.to_ascii_lowercase().as_str() {
++        "1234567890" => {},
++        "~!@#$%^&*()-_=+foo" => {},
++        "\n\r\t\x7F" => {},
++        _ => {},
++    }
++}
++
++fn unicode_cased() {
++    let var = "ВОДЫ";
++
++    match var.to_lowercase().as_str() {
++        "水" => {},
++        "νερό" => {},
++        "воды" => {},
++        "물" => {},
++        _ => {},
++    }
++}
++
++fn titlecase() {
++    let var = "BarDz";
++
++    match var.to_lowercase().as_str() {
++        "foolj" => {},
++        "bardz" => {},
++        _ => {},
++    }
++}
++
++fn no_case_equivalent() {
++    let var = "barʁ";
++
++    match var.to_uppercase().as_str() {
++        "FOOɕ" => {},
++        "BARʁ" => {},
++        _ => {},
++    }
++}
++
++fn addrof_unary_match() {
++    let var = "BAR";
++
++    match &*var.to_ascii_lowercase() {
++        "foo" => {},
++        "bar" => {},
++        _ => {},
++    }
++}
++
++fn alternating_chain() {
++    let var = "BAR";
++
++    match &*var
++        .to_ascii_lowercase()
++        .to_uppercase()
++        .to_lowercase()
++        .to_ascii_uppercase()
++    {
++        "FOO" => {},
++        "BAR" => {},
++        _ => {},
++    }
++}
++
++fn unrelated_method() {
++    struct Item {
++        a: String,
++    }
++
++    impl Item {
++        #[allow(clippy::wrong_self_convention)]
++        fn to_lowercase(self) -> String {
++            self.a
++        }
++    }
++
++    let item = Item { a: String::from("BAR") };
++
++    match &*item.to_lowercase() {
++        "FOO" => {},
++        "BAR" => {},
++        _ => {},
++    }
++}
++
++// Invalid
++
++fn as_str_match_mismatch() {
++    let var = "BAR";
++
++    match var.to_ascii_lowercase().as_str() {
++        "foo" => {},
++        "bar" => {},
++        _ => {},
++    }
++}
++
++fn non_alphabetic_mismatch() {
++    let var = "~!@#$%^&*()-_=+FOO";
++
++    match var.to_ascii_lowercase().as_str() {
++        "1234567890" => {},
++        "~!@#$%^&*()-_=+foo" => {},
++        "\n\r\t\x7F" => {},
++        _ => {},
++    }
++}
++
++fn unicode_cased_mismatch() {
++    let var = "ВОДЫ";
++
++    match var.to_lowercase().as_str() {
++        "水" => {},
++        "νερό" => {},
++        "воды" => {},
++        "물" => {},
++        _ => {},
++    }
++}
++
++fn titlecase_mismatch() {
++    let var = "BarDz";
++
++    match var.to_lowercase().as_str() {
++        "foolj" => {},
++        "bardz" => {},
++        _ => {},
++    }
++}
++
++fn no_case_equivalent_mismatch() {
++    let var = "barʁ";
++
++    match var.to_uppercase().as_str() {
++        "FOOɕ" => {},
++        "BARʁ" => {},
++        _ => {},
++    }
++}
++
++fn addrof_unary_match_mismatch() {
++    let var = "BAR";
++
++    match &*var.to_ascii_lowercase() {
++        "foo" => {},
++        "bar" => {},
++        _ => {},
++    }
++}
++
++fn alternating_chain_mismatch() {
++    let var = "BAR";
++
++    match &*var
++        .to_ascii_lowercase()
++        .to_uppercase()
++        .to_lowercase()
++        .to_ascii_uppercase()
++    {
++        "FOO" => {},
++        "BAR" => {},
++        _ => {},
++    }
++}
++
++fn main() {}
index ac555c87d83b21a14beecece000d2cd7a55cbbdf,0000000000000000000000000000000000000000..92e2a000ade25077478a58ca265cbb6159dc5fd9
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,186 @@@
++// run-rustfix
 +#![warn(clippy::match_str_case_mismatch)]
++#![allow(dead_code)]
 +
 +// Valid
 +
 +fn as_str_match() {
 +    let var = "BAR";
 +
 +    match var.to_ascii_lowercase().as_str() {
 +        "foo" => {},
 +        "bar" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn non_alphabetic() {
 +    let var = "~!@#$%^&*()-_=+FOO";
 +
 +    match var.to_ascii_lowercase().as_str() {
 +        "1234567890" => {},
 +        "~!@#$%^&*()-_=+foo" => {},
 +        "\n\r\t\x7F" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn unicode_cased() {
 +    let var = "ВОДЫ";
 +
 +    match var.to_lowercase().as_str() {
 +        "水" => {},
 +        "νερό" => {},
 +        "воды" => {},
 +        "물" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn titlecase() {
 +    let var = "BarDz";
 +
 +    match var.to_lowercase().as_str() {
 +        "foolj" => {},
 +        "bardz" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn no_case_equivalent() {
 +    let var = "barʁ";
 +
 +    match var.to_uppercase().as_str() {
 +        "FOOɕ" => {},
 +        "BARʁ" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn addrof_unary_match() {
 +    let var = "BAR";
 +
 +    match &*var.to_ascii_lowercase() {
 +        "foo" => {},
 +        "bar" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn alternating_chain() {
 +    let var = "BAR";
 +
 +    match &*var
 +        .to_ascii_lowercase()
 +        .to_uppercase()
 +        .to_lowercase()
 +        .to_ascii_uppercase()
 +    {
 +        "FOO" => {},
 +        "BAR" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn unrelated_method() {
 +    struct Item {
 +        a: String,
 +    }
 +
 +    impl Item {
 +        #[allow(clippy::wrong_self_convention)]
 +        fn to_lowercase(self) -> String {
 +            self.a
 +        }
 +    }
 +
 +    let item = Item { a: String::from("BAR") };
 +
 +    match &*item.to_lowercase() {
 +        "FOO" => {},
 +        "BAR" => {},
 +        _ => {},
 +    }
 +}
 +
 +// Invalid
 +
 +fn as_str_match_mismatch() {
 +    let var = "BAR";
 +
 +    match var.to_ascii_lowercase().as_str() {
 +        "foo" => {},
 +        "Bar" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn non_alphabetic_mismatch() {
 +    let var = "~!@#$%^&*()-_=+FOO";
 +
 +    match var.to_ascii_lowercase().as_str() {
 +        "1234567890" => {},
 +        "~!@#$%^&*()-_=+Foo" => {},
 +        "\n\r\t\x7F" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn unicode_cased_mismatch() {
 +    let var = "ВОДЫ";
 +
 +    match var.to_lowercase().as_str() {
 +        "水" => {},
 +        "νερό" => {},
 +        "Воды" => {},
 +        "물" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn titlecase_mismatch() {
 +    let var = "BarDz";
 +
 +    match var.to_lowercase().as_str() {
 +        "foolj" => {},
 +        "barDz" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn no_case_equivalent_mismatch() {
 +    let var = "barʁ";
 +
 +    match var.to_uppercase().as_str() {
 +        "FOOɕ" => {},
 +        "bARʁ" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn addrof_unary_match_mismatch() {
 +    let var = "BAR";
 +
 +    match &*var.to_ascii_lowercase() {
 +        "foo" => {},
 +        "Bar" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn alternating_chain_mismatch() {
 +    let var = "BAR";
 +
 +    match &*var
 +        .to_ascii_lowercase()
 +        .to_uppercase()
 +        .to_lowercase()
 +        .to_ascii_uppercase()
 +    {
 +        "FOO" => {},
 +        "bAR" => {},
 +        _ => {},
 +    }
 +}
 +
 +fn main() {}
index 92baa40ef28f06f99746326de19b31e53055d703,0000000000000000000000000000000000000000..197520a3d608174bb8e639643d55a185e2cfb37f
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,80 @@@
-   --> $DIR/match_str_case_mismatch.rs:111:9
 +error: this `match` arm has a differing case than its expression
-   --> $DIR/match_str_case_mismatch.rs:121:9
++  --> $DIR/match_str_case_mismatch.rs:113:9
 +   |
 +LL |         "Bar" => {},
 +   |         ^^^^^
 +   |
 +   = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings`
 +help: consider changing the case of this arm to respect `to_ascii_lowercase`
 +   |
 +LL |         "bar" => {},
 +   |         ~~~~~
 +
 +error: this `match` arm has a differing case than its expression
-   --> $DIR/match_str_case_mismatch.rs:133:9
++  --> $DIR/match_str_case_mismatch.rs:123:9
 +   |
 +LL |         "~!@#$%^&*()-_=+Foo" => {},
 +   |         ^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: consider changing the case of this arm to respect `to_ascii_lowercase`
 +   |
 +LL |         "~!@#$%^&*()-_=+foo" => {},
 +   |         ~~~~~~~~~~~~~~~~~~~~
 +
 +error: this `match` arm has a differing case than its expression
-   --> $DIR/match_str_case_mismatch.rs:144:9
++  --> $DIR/match_str_case_mismatch.rs:135:9
 +   |
 +LL |         "Воды" => {},
 +   |         ^^^^^^
 +   |
 +help: consider changing the case of this arm to respect `to_lowercase`
 +   |
 +LL |         "воды" => {},
 +   |         ~~~~~~
 +
 +error: this `match` arm has a differing case than its expression
-   --> $DIR/match_str_case_mismatch.rs:154:9
++  --> $DIR/match_str_case_mismatch.rs:146:9
 +   |
 +LL |         "barDz" => {},
 +   |         ^^^^^^
 +   |
 +help: consider changing the case of this arm to respect `to_lowercase`
 +   |
 +LL |         "bardz" => {},
 +   |         ~~~~~~
 +
 +error: this `match` arm has a differing case than its expression
-   --> $DIR/match_str_case_mismatch.rs:164:9
++  --> $DIR/match_str_case_mismatch.rs:156:9
 +   |
 +LL |         "bARʁ" => {},
 +   |         ^^^^^^
 +   |
 +help: consider changing the case of this arm to respect `to_uppercase`
 +   |
 +LL |         "BARʁ" => {},
 +   |         ~~~~~~
 +
 +error: this `match` arm has a differing case than its expression
-   --> $DIR/match_str_case_mismatch.rs:179:9
++  --> $DIR/match_str_case_mismatch.rs:166:9
 +   |
 +LL |         "Bar" => {},
 +   |         ^^^^^
 +   |
 +help: consider changing the case of this arm to respect `to_ascii_lowercase`
 +   |
 +LL |         "bar" => {},
 +   |         ~~~~~
 +
 +error: this `match` arm has a differing case than its expression
++  --> $DIR/match_str_case_mismatch.rs:181:9
 +   |
 +LL |         "bAR" => {},
 +   |         ^^^^^
 +   |
 +help: consider changing the case of this arm to respect `to_ascii_uppercase`
 +   |
 +LL |         "BAR" => {},
 +   |         ~~~~~
 +
 +error: aborting due to 7 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8f286c9304ccb4b3859964c2292846bd376f8b76
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++#![warn(clippy::mismatching_type_param_order)]
++#![allow(clippy::blacklisted_name)]
++
++fn main() {
++    struct Foo<A, B> {
++        x: A,
++        y: B,
++    }
++
++    // lint on both params
++    impl<B, A> Foo<B, A> {}
++
++    // lint on the 2nd param
++    impl<C, A> Foo<C, A> {}
++
++    // should not lint
++    impl<A, B> Foo<A, B> {}
++
++    struct FooLifetime<'l, 'm, A, B> {
++        x: &'l A,
++        y: &'m B,
++    }
++
++    // should not lint on lifetimes
++    impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
++
++    struct Bar {
++        x: i32,
++    }
++
++    // should not lint
++    impl Bar {}
++
++    // also works for enums
++    enum FooEnum<A, B, C> {
++        X(A),
++        Y(B),
++        Z(C),
++    }
++
++    impl<C, A, B> FooEnum<C, A, B> {}
++
++    // also works for unions
++    union FooUnion<A: Copy, B>
++    where
++        B: Copy,
++    {
++        x: A,
++        y: B,
++    }
++
++    impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
++
++    impl<A, B> FooUnion<A, B>
++    where
++        A: Copy,
++        B: Copy,
++    {
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb720256c50e5cdd6dd7a60ddeba60bc996bc468
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:11:20
++   |
++LL |     impl<B, A> Foo<B, A> {}
++   |                    ^
++   |
++   = note: `-D clippy::mismatching-type-param-order` implied by `-D warnings`
++   = help: try `A`, or a name that does not conflict with `Foo`'s generic params
++
++error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:11:23
++   |
++LL |     impl<B, A> Foo<B, A> {}
++   |                       ^
++   |
++   = help: try `B`, or a name that does not conflict with `Foo`'s generic params
++
++error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:14:23
++   |
++LL |     impl<C, A> Foo<C, A> {}
++   |                       ^
++   |
++   = help: try `B`, or a name that does not conflict with `Foo`'s generic params
++
++error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:25:44
++   |
++LL |     impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
++   |                                            ^
++   |
++   = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params
++
++error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:25:47
++   |
++LL |     impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {}
++   |                                               ^
++   |
++   = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params
++
++error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:41:27
++   |
++LL |     impl<C, A, B> FooEnum<C, A, B> {}
++   |                           ^
++   |
++   = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params
++
++error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:41:30
++   |
++LL |     impl<C, A, B> FooEnum<C, A, B> {}
++   |                              ^
++   |
++   = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params
++
++error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:41:33
++   |
++LL |     impl<C, A, B> FooEnum<C, A, B> {}
++   |                                 ^
++   |
++   = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params
++
++error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:52:31
++   |
++LL |     impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
++   |                               ^
++   |
++   = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params
++
++error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order
++  --> $DIR/mismatching_type_param_order.rs:52:34
++   |
++LL |     impl<B: Copy, A> FooUnion<B, A> where A: Copy {}
++   |                                  ^
++   |
++   = help: try `B`, or a name that does not conflict with `FooUnion`'s generic params
++
++error: aborting due to 10 previous errors
++
index 678a312f66e53af9ed72aeb8765c9db6cbbb53e1,0000000000000000000000000000000000000000..adff08e5d1e8b206df72074f4ffb4da718b180bd
mode 100644,000000..100644
--- /dev/null
@@@ -1,23 -1,0 +1,23 @@@
- #![allow(clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::modulo_one)]
++#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)]
 +
 +static STATIC_ONE: usize = 2 - 1;
 +static STATIC_NEG_ONE: i64 = 1 - 2;
 +
 +fn main() {
 +    10 % 1;
 +    10 % -1;
 +    10 % 2;
 +    i32::MIN % (-1); // also caught by rustc
 +
 +    const ONE: u32 = 1 * 1;
 +    const NEG_ONE: i64 = 1 - 2;
 +    const INT_MIN: i64 = i64::MIN;
 +
 +    2 % ONE;
 +    5 % STATIC_ONE; // NOT caught by lint
 +    2 % NEG_ONE;
 +    5 % STATIC_NEG_ONE; // NOT caught by lint
 +    INT_MIN % NEG_ONE; // also caught by rustc
 +    INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc
 +}
index 03f460897fce2f5467b88f89b9efa5ffcda0bcd1,0000000000000000000000000000000000000000..04ecdef5e994e399002693fa0b08b9004159a2e0
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,60 @@@
- error: the operation is ineffective. Consider reducing it to `1`
-   --> $DIR/modulo_one.rs:13:22
-    |
- LL |     const ONE: u32 = 1 * 1;
-    |                      ^^^^^
-    |
-    = note: `-D clippy::identity-op` implied by `-D warnings`
 +error: this operation will panic at runtime
 +  --> $DIR/modulo_one.rs:11:5
 +   |
 +LL |     i32::MIN % (-1); // also caught by rustc
 +   |     ^^^^^^^^^^^^^^^ attempt to compute the remainder of `i32::MIN % -1_i32`, which would overflow
 +   |
 +   = note: `#[deny(unconditional_panic)]` on by default
 +
 +error: this operation will panic at runtime
 +  --> $DIR/modulo_one.rs:21:5
 +   |
 +LL |     INT_MIN % NEG_ONE; // also caught by rustc
 +   |     ^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow
 +
 +error: this operation will panic at runtime
 +  --> $DIR/modulo_one.rs:22:5
 +   |
 +LL |     INT_MIN % STATIC_NEG_ONE; // ONLY caught by rustc
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^ attempt to compute the remainder of `i64::MIN % -1_i64`, which would overflow
 +
 +error: any number modulo 1 will be 0
 +  --> $DIR/modulo_one.rs:8:5
 +   |
 +LL |     10 % 1;
 +   |     ^^^^^^
 +   |
 +   = note: `-D clippy::modulo-one` implied by `-D warnings`
 +
 +error: any number modulo -1 will panic/overflow or result in 0
 +  --> $DIR/modulo_one.rs:9:5
 +   |
 +LL |     10 % -1;
 +   |     ^^^^^^^
 +
 +error: any number modulo -1 will panic/overflow or result in 0
 +  --> $DIR/modulo_one.rs:11:5
 +   |
 +LL |     i32::MIN % (-1); // also caught by rustc
 +   |     ^^^^^^^^^^^^^^^
 +
- error: aborting due to 10 previous errors
 +error: any number modulo 1 will be 0
 +  --> $DIR/modulo_one.rs:17:5
 +   |
 +LL |     2 % ONE;
 +   |     ^^^^^^^
 +
 +error: any number modulo -1 will panic/overflow or result in 0
 +  --> $DIR/modulo_one.rs:19:5
 +   |
 +LL |     2 % NEG_ONE;
 +   |     ^^^^^^^^^^^
 +
 +error: any number modulo -1 will panic/overflow or result in 0
 +  --> $DIR/modulo_one.rs:21:5
 +   |
 +LL |     INT_MIN % NEG_ONE; // also caught by rustc
 +   |     ^^^^^^^^^^^^^^^^^
 +
++error: aborting due to 9 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fee8e3030b8087cb16a52a6ffa55f67967c9158f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,273 @@@
++// run-rustfix
++#![feature(let_chains)]
++#![allow(
++    unused,
++    clippy::assign_op_pattern,
++    clippy::blocks_in_if_conditions,
++    clippy::let_and_return,
++    clippy::let_unit_value,
++    clippy::nonminimal_bool
++)]
++
++use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
++use std::rc::Rc;
++
++struct SignificantDrop;
++impl std::ops::Drop for SignificantDrop {
++    fn drop(&mut self) {
++        println!("dropped");
++    }
++}
++
++fn simple() {
++    
++    let a = "zero";
++
++    
++    
++    let b = 1;
++    let c = 2;
++
++    
++    let d: usize = 1;
++
++    
++    let e = format!("{}", d);
++}
++
++fn main() {
++    
++    let n = 1;
++    let a = match n {
++        1 => "one",
++        _ => {
++            "two"
++        },
++    };
++
++    
++    let b = if n == 3 {
++        "four"
++    } else {
++        "five"
++    };
++
++    
++    let d = if true {
++        let temp = 5;
++        temp
++    } else {
++        15
++    };
++
++    
++    let e = if true {
++        format!("{} {}", a, b)
++    } else {
++        format!("{}", n)
++    };
++
++    
++    let f = match 1 {
++        1 => "three",
++        _ => return,
++    }; // has semi
++
++    
++    let g: usize = if true {
++        5
++    } else {
++        panic!();
++    };
++
++    // Drop order only matters if both are significant
++    
++    let y = SignificantDrop;
++    let x = 1;
++
++    
++    let y = 1;
++    let x = SignificantDrop;
++
++    
++    // types that should be considered insignificant
++    let y = 1;
++    let y = "2";
++    let y = String::new();
++    let y = vec![3.0];
++    let y = HashMap::<usize, usize>::new();
++    let y = BTreeMap::<usize, usize>::new();
++    let y = HashSet::<usize>::new();
++    let y = BTreeSet::<usize>::new();
++    let y = Box::new(4);
++    let x = SignificantDrop;
++}
++
++async fn in_async() -> &'static str {
++    async fn f() -> &'static str {
++        "one"
++    }
++
++    
++    let n = 1;
++    let a = match n {
++        1 => f().await,
++        _ => {
++            "two"
++        },
++    };
++
++    a
++}
++
++const fn in_const() -> &'static str {
++    const fn f() -> &'static str {
++        "one"
++    }
++
++    
++    let n = 1;
++    let a = match n {
++        1 => f(),
++        _ => {
++            "two"
++        },
++    };
++
++    a
++}
++
++fn does_not_lint() {
++    let z;
++    if false {
++        z = 1;
++    }
++
++    let x;
++    let y;
++    if true {
++        x = 1;
++    } else {
++        y = 1;
++    }
++
++    let mut x;
++    if true {
++        x = 5;
++        x = 10 / x;
++    } else {
++        x = 2;
++    }
++
++    let x;
++    let _ = match 1 {
++        1 => x = 10,
++        _ => x = 20,
++    };
++
++    // using tuples would be possible, but not always preferable
++    let x;
++    let y;
++    if true {
++        x = 1;
++        y = 2;
++    } else {
++        x = 3;
++        y = 4;
++    }
++
++    // could match with a smarter heuristic to avoid multiple assignments
++    let x;
++    if true {
++        let mut y = 5;
++        y = 6;
++        x = y;
++    } else {
++        x = 2;
++    }
++
++    let (x, y);
++    if true {
++        x = 1;
++    } else {
++        x = 2;
++    }
++    y = 3;
++
++    macro_rules! assign {
++        ($i:ident) => {
++            $i = 1;
++        };
++    }
++    let x;
++    assign!(x);
++
++    let x;
++    if true {
++        assign!(x);
++    } else {
++        x = 2;
++    }
++
++    macro_rules! in_macro {
++        () => {
++            let x;
++            x = 1;
++
++            let x;
++            if true {
++                x = 1;
++            } else {
++                x = 2;
++            }
++        };
++    }
++    in_macro!();
++
++    // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
++    let x;
++    if let Some(n) = Some("v") {
++        x = 1;
++    } else {
++        x = 2;
++    }
++
++    let x;
++    if true && let Some(n) = Some("let chains too") {
++        x = 1;
++    } else {
++        x = 2;
++    }
++
++    // ignore mut bindings
++    // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
++    // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
++    let mut x: usize;
++    x = 1;
++    x = 2;
++    x = 3;
++
++    // should not move the declaration if `x` has a significant drop, and there
++    // is another binding with a significant drop between it and the first usage
++    let x;
++    let y = SignificantDrop;
++    x = SignificantDrop;
++}
++
++#[rustfmt::skip]
++fn issue8911() -> u32 {
++    let x;
++    match 1 {
++        _ if { x = 1; false } => return 1,
++        _ => return 2,
++    }
++
++    let x;
++    if { x = 1; true } {
++        return 1;
++    } else {
++        return 2;
++    }
++
++    3
++}
index 54e66b391b8d8a46068df048cf91c17bf8a5d03c,0000000000000000000000000000000000000000..402d9f9ef7f81945f4dd9087c314ca0dfe0f8b32
mode 100644,000000..100644
--- /dev/null
@@@ -1,231 -1,0 +1,273 @@@
- #![allow(unused, clippy::nonminimal_bool, clippy::let_unit_value)]
++// run-rustfix
 +#![feature(let_chains)]
++#![allow(
++    unused,
++    clippy::assign_op_pattern,
++    clippy::blocks_in_if_conditions,
++    clippy::let_and_return,
++    clippy::let_unit_value,
++    clippy::nonminimal_bool
++)]
 +
 +use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
 +use std::rc::Rc;
 +
 +struct SignificantDrop;
 +impl std::ops::Drop for SignificantDrop {
 +    fn drop(&mut self) {
 +        println!("dropped");
 +    }
 +}
 +
++fn simple() {
++    let a;
++    a = "zero";
++
++    let b;
++    let c;
++    b = 1;
++    c = 2;
++
++    let d: usize;
++    d = 1;
++
++    let e;
++    e = format!("{}", d);
++}
++
 +fn main() {
 +    let a;
 +    let n = 1;
 +    match n {
 +        1 => a = "one",
 +        _ => {
 +            a = "two";
 +        },
 +    }
 +
 +    let b;
 +    if n == 3 {
 +        b = "four";
 +    } else {
 +        b = "five"
 +    }
 +
 +    let d;
 +    if true {
 +        let temp = 5;
 +        d = temp;
 +    } else {
 +        d = 15;
 +    }
 +
 +    let e;
 +    if true {
 +        e = format!("{} {}", a, b);
 +    } else {
 +        e = format!("{}", n);
 +    }
 +
 +    let f;
 +    match 1 {
 +        1 => f = "three",
 +        _ => return,
 +    }; // has semi
 +
 +    let g: usize;
 +    if true {
 +        g = 5;
 +    } else {
 +        panic!();
 +    }
 +
 +    // Drop order only matters if both are significant
 +    let x;
 +    let y = SignificantDrop;
 +    x = 1;
 +
 +    let x;
 +    let y = 1;
 +    x = SignificantDrop;
 +
 +    let x;
 +    // types that should be considered insignificant
 +    let y = 1;
 +    let y = "2";
 +    let y = String::new();
 +    let y = vec![3.0];
 +    let y = HashMap::<usize, usize>::new();
 +    let y = BTreeMap::<usize, usize>::new();
 +    let y = HashSet::<usize>::new();
 +    let y = BTreeSet::<usize>::new();
 +    let y = Box::new(4);
 +    x = SignificantDrop;
 +}
 +
 +async fn in_async() -> &'static str {
 +    async fn f() -> &'static str {
 +        "one"
 +    }
 +
 +    let a;
 +    let n = 1;
 +    match n {
 +        1 => a = f().await,
 +        _ => {
 +            a = "two";
 +        },
 +    }
 +
 +    a
 +}
 +
 +const fn in_const() -> &'static str {
 +    const fn f() -> &'static str {
 +        "one"
 +    }
 +
 +    let a;
 +    let n = 1;
 +    match n {
 +        1 => a = f(),
 +        _ => {
 +            a = "two";
 +        },
 +    }
 +
 +    a
 +}
 +
 +fn does_not_lint() {
 +    let z;
 +    if false {
 +        z = 1;
 +    }
 +
 +    let x;
 +    let y;
 +    if true {
 +        x = 1;
 +    } else {
 +        y = 1;
 +    }
 +
 +    let mut x;
 +    if true {
 +        x = 5;
 +        x = 10 / x;
 +    } else {
 +        x = 2;
 +    }
 +
 +    let x;
 +    let _ = match 1 {
 +        1 => x = 10,
 +        _ => x = 20,
 +    };
 +
 +    // using tuples would be possible, but not always preferable
 +    let x;
 +    let y;
 +    if true {
 +        x = 1;
 +        y = 2;
 +    } else {
 +        x = 3;
 +        y = 4;
 +    }
 +
 +    // could match with a smarter heuristic to avoid multiple assignments
 +    let x;
 +    if true {
 +        let mut y = 5;
 +        y = 6;
 +        x = y;
 +    } else {
 +        x = 2;
 +    }
 +
 +    let (x, y);
 +    if true {
 +        x = 1;
 +    } else {
 +        x = 2;
 +    }
 +    y = 3;
 +
 +    macro_rules! assign {
 +        ($i:ident) => {
 +            $i = 1;
 +        };
 +    }
 +    let x;
 +    assign!(x);
 +
 +    let x;
 +    if true {
 +        assign!(x);
 +    } else {
 +        x = 2;
 +    }
 +
 +    macro_rules! in_macro {
 +        () => {
 +            let x;
 +            x = 1;
 +
 +            let x;
 +            if true {
 +                x = 1;
 +            } else {
 +                x = 2;
 +            }
 +        };
 +    }
 +    in_macro!();
 +
 +    // ignore if-lets - https://github.com/rust-lang/rust-clippy/issues/8613
 +    let x;
 +    if let Some(n) = Some("v") {
 +        x = 1;
 +    } else {
 +        x = 2;
 +    }
 +
 +    let x;
 +    if true && let Some(n) = Some("let chains too") {
 +        x = 1;
 +    } else {
 +        x = 2;
 +    }
 +
 +    // ignore mut bindings
 +    // https://github.com/shepmaster/twox-hash/blob/b169c16d86eb8ea4a296b0acb9d00ca7e3c3005f/src/sixty_four.rs#L88-L93
 +    // https://github.com/dtolnay/thiserror/blob/21c26903e29cb92ba1a7ff11e82ae2001646b60d/tests/test_generics.rs#L91-L100
 +    let mut x: usize;
 +    x = 1;
 +    x = 2;
 +    x = 3;
 +
 +    // should not move the declaration if `x` has a significant drop, and there
 +    // is another binding with a significant drop between it and the first usage
 +    let x;
 +    let y = SignificantDrop;
 +    x = SignificantDrop;
 +}
++
++#[rustfmt::skip]
++fn issue8911() -> u32 {
++    let x;
++    match 1 {
++        _ if { x = 1; false } => return 1,
++        _ => return 2,
++    }
++
++    let x;
++    if { x = 1; true } {
++        return 1;
++    } else {
++        return 2;
++    }
++
++    3
++}
index d33a117b288cb5482a5bd0cc87dc446e3f091109,0000000000000000000000000000000000000000..f320b5b9cbb378c2b14212b751c7f811fb640909
mode 100644,000000..100644
--- /dev/null
@@@ -1,207 -1,0 +1,274 @@@
-   --> $DIR/needless_late_init.rs:15:5
 +error: unneeded late initialization
-    |     ^^^^^^
++  --> $DIR/needless_late_init.rs:23:5
 +   |
 +LL |     let a;
-   --> $DIR/needless_late_init.rs:24:5
++   |     ^^^^^^ created here
++LL |     a = "zero";
++   |     ^^^^^^^^^^ initialised here
 +   |
 +   = note: `-D clippy::needless-late-init` implied by `-D warnings`
 +help: declare `a` here
 +   |
++LL |     let a = "zero";
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:26:5
++   |
++LL |     let b;
++   |     ^^^^^^ created here
++LL |     let c;
++LL |     b = 1;
++   |     ^^^^^ initialised here
++   |
++help: declare `b` here
++   |
++LL |     let b = 1;
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:27:5
++   |
++LL |     let c;
++   |     ^^^^^^ created here
++LL |     b = 1;
++LL |     c = 2;
++   |     ^^^^^ initialised here
++   |
++help: declare `c` here
++   |
++LL |     let c = 2;
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:31:5
++   |
++LL |     let d: usize;
++   |     ^^^^^^^^^^^^^ created here
++LL |     d = 1;
++   |     ^^^^^ initialised here
++   |
++help: declare `d` here
++   |
++LL |     let d: usize = 1;
++   |     ~~~~~~~~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:34:5
++   |
++LL |     let e;
++   |     ^^^^^^ created here
++LL |     e = format!("{}", d);
++   |     ^^^^^^^^^^^^^^^^^^^^ initialised here
++   |
++help: declare `e` here
++   |
++LL |     let e = format!("{}", d);
++   |     ~~~~~
++
++error: unneeded late initialization
++  --> $DIR/needless_late_init.rs:39:5
++   |
++LL |     let a;
++   |     ^^^^^^
++   |
++help: declare `a` here
++   |
 +LL |     let a = match n {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL ~         1 => "one",
 +LL |         _ => {
 +LL ~             "two"
 +   |
 +help: add a semicolon after the `match` expression
 +   |
 +LL |     };
 +   |      +
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:31:5
++  --> $DIR/needless_late_init.rs:48:5
 +   |
 +LL |     let b;
 +   |     ^^^^^^
 +   |
 +help: declare `b` here
 +   |
 +LL |     let b = if n == 3 {
 +   |     +++++++
 +help: remove the assignments from the branches
 +   |
 +LL ~         "four"
 +LL |     } else {
 +LL ~         "five"
 +   |
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:39:5
++  --> $DIR/needless_late_init.rs:55:5
 +   |
 +LL |     let d;
 +   |     ^^^^^^
 +   |
 +help: declare `d` here
 +   |
 +LL |     let d = if true {
 +   |     +++++++
 +help: remove the assignments from the branches
 +   |
 +LL ~         temp
 +LL |     } else {
 +LL ~         15
 +   |
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:46:5
++  --> $DIR/needless_late_init.rs:63:5
 +   |
 +LL |     let e;
 +   |     ^^^^^^
 +   |
 +help: declare `e` here
 +   |
 +LL |     let e = if true {
 +   |     +++++++
 +help: remove the assignments from the branches
 +   |
 +LL ~         format!("{} {}", a, b)
 +LL |     } else {
 +LL ~         format!("{}", n)
 +   |
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:52:5
++  --> $DIR/needless_late_init.rs:70:5
 +   |
 +LL |     let f;
 +   |     ^^^^^^
 +   |
 +help: declare `f` here
 +   |
 +LL |     let f = match 1 {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL -         1 => f = "three",
 +LL +         1 => "three",
 +   | 
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:60:5
++  --> $DIR/needless_late_init.rs:76:5
 +   |
 +LL |     let g: usize;
 +   |     ^^^^^^^^^^^^^
 +   |
 +help: declare `g` here
 +   |
 +LL |     let g: usize = if true {
 +   |     ++++++++++++++
 +help: remove the assignments from the branches
 +   |
 +LL -         g = 5;
 +LL +         5
 +   | 
 +help: add a semicolon after the `if` expression
 +   |
 +LL |     };
 +   |      +
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:64:5
++  --> $DIR/needless_late_init.rs:84:5
 +   |
 +LL |     let x;
 +   |     ^^^^^^ created here
 +LL |     let y = SignificantDrop;
 +LL |     x = 1;
 +   |     ^^^^^ initialised here
 +   |
 +help: declare `x` here
 +   |
 +LL |     let x = 1;
 +   |     ~~~~~
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:68:5
++  --> $DIR/needless_late_init.rs:88:5
 +   |
 +LL |     let x;
 +   |     ^^^^^^ created here
 +LL |     let y = 1;
 +LL |     x = SignificantDrop;
 +   |     ^^^^^^^^^^^^^^^^^^^ initialised here
 +   |
 +help: declare `x` here
 +   |
 +LL |     let x = SignificantDrop;
 +   |     ~~~~~
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:87:5
++  --> $DIR/needless_late_init.rs:92:5
 +   |
 +LL |     let x;
 +   |     ^^^^^^ created here
 +...
 +LL |     x = SignificantDrop;
 +   |     ^^^^^^^^^^^^^^^^^^^ initialised here
 +   |
 +help: declare `x` here
 +   |
 +LL |     let x = SignificantDrop;
 +   |     ~~~~~
 +
 +error: unneeded late initialization
-   --> $DIR/needless_late_init.rs:104:5
++  --> $DIR/needless_late_init.rs:111:5
 +   |
 +LL |     let a;
 +   |     ^^^^^^
 +   |
 +help: declare `a` here
 +   |
 +LL |     let a = match n {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL ~         1 => f().await,
 +LL |         _ => {
 +LL ~             "two"
 +   |
 +help: add a semicolon after the `match` expression
 +   |
 +LL |     };
 +   |      +
 +
 +error: unneeded late initialization
- error: aborting due to 11 previous errors
++  --> $DIR/needless_late_init.rs:128:5
 +   |
 +LL |     let a;
 +   |     ^^^^^^
 +   |
 +help: declare `a` here
 +   |
 +LL |     let a = match n {
 +   |     +++++++
 +help: remove the assignments from the `match` arms
 +   |
 +LL ~         1 => f(),
 +LL |         _ => {
 +LL ~             "two"
 +   |
 +help: add a semicolon after the `match` expression
 +   |
 +LL |     };
 +   |      +
 +
++error: aborting due to 16 previous errors
 +
index 1456204ca8692fdd79f65e4540946674c2d9f7e2,0000000000000000000000000000000000000000..fc686b1dac0e135900213be13f191040abcd10fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,421 -1,0 +1,422 @@@
-     dyn_drop
 +#![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; multiple input refs.
 +fn multiple_in_and_out_2<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 {
 +    x
 +}
 +
 +// 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.
 +fn deep_reference_1<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> {
 +    Ok(x)
 +}
 +
 +// 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 error; multiple input refs.
 +    fn self_and_in_out<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 {
 +        &self.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 struct_with_lt4<'a, 'b>(_foo: &'a Foo<'b>) -> &'a 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!()
 +}
 +
 +// No warning; two input lifetimes.
 +fn alias_with_lt4<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a 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 () {
 +            &()
 +        }
 +    }
 +}
 +
 +fn main() {}
index a488bc01fffa289257f8cd7032c32eacf32ab3f3,0000000000000000000000000000000000000000..3c428fd4674ce754b9ebc30f36961d0959a0923a
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,190 @@@
-   --> $DIR/needless_lifetimes.rs:10:1
 +error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
-   --> $DIR/needless_lifetimes.rs:12:1
++  --> $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)
-   --> $DIR/needless_lifetimes.rs:22:1
++  --> $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)
-   --> $DIR/needless_lifetimes.rs:56:1
++  --> $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:61:1
++  --> $DIR/needless_lifetimes.rs:57: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:73:1
++  --> $DIR/needless_lifetimes.rs:62: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:97:1
++  --> $DIR/needless_lifetimes.rs:74:1
 +   |
 +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:127:5
++  --> $DIR/needless_lifetimes.rs:98:1
 +   |
 +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:136:5
++  --> $DIR/needless_lifetimes.rs:128: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:155:1
++  --> $DIR/needless_lifetimes.rs:137: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:185:1
++  --> $DIR/needless_lifetimes.rs:156:1
 +   |
 +LL | fn struct_with_lt<'a>(_foo: Foo<'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:191:1
++  --> $DIR/needless_lifetimes.rs:186: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:210:1
++  --> $DIR/needless_lifetimes.rs:192:1
 +   |
 +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:218:1
++  --> $DIR/needless_lifetimes.rs:211: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:254:1
++  --> $DIR/needless_lifetimes.rs:219: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:261:9
++  --> $DIR/needless_lifetimes.rs:255:1
 +   |
 +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:265:9
++  --> $DIR/needless_lifetimes.rs:262: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:278:9
++  --> $DIR/needless_lifetimes.rs:266: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:310:5
++  --> $DIR/needless_lifetimes.rs:279: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:319:5
++  --> $DIR/needless_lifetimes.rs:311: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:331:5
++  --> $DIR/needless_lifetimes.rs:320: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:346:5
++  --> $DIR/needless_lifetimes.rs:332: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:359:5
++  --> $DIR/needless_lifetimes.rs:347: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:362:5
++  --> $DIR/needless_lifetimes.rs:360: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:384:9
++  --> $DIR/needless_lifetimes.rs:363: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:387:9
++  --> $DIR/needless_lifetimes.rs:385: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:398:9
++  --> $DIR/needless_lifetimes.rs:388: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:404:9
++  --> $DIR/needless_lifetimes.rs:399: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
++  --> $DIR/needless_lifetimes.rs:405: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:414:9
++  --> $DIR/needless_lifetimes.rs:406: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
++  --> $DIR/needless_lifetimes.rs:415: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
 +   |
 +LL |         fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: aborting due to 31 previous errors
 +
index 603d438d55889a8f72cb339beadf5031de43dfba,0000000000000000000000000000000000000000..7c828430b78533301ed3bd325a6787eca61de872
mode 100644,000000..100644
--- /dev/null
@@@ -1,226 -1,0 +1,232 @@@
-     return the_answer!();
 +// run-rustfix
 +
 +#![feature(let_else)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::if_same_then_else,
 +    clippy::single_match,
 +    clippy::needless_bool,
 +    clippy::equatable_if_let
 +)]
 +#![warn(clippy::needless_return)]
 +
 +macro_rules! the_answer {
 +    () => {
 +        42
 +    };
 +}
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    true
 +}
 +
 +fn test_no_semicolon() -> bool {
 +    true
 +}
 +
 +fn test_if_block() -> bool {
 +    if true {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => {
 +            true
 +        },
 +    }
 +}
 +
 +fn test_closure() {
 +    let _ = || {
 +        true
 +    };
 +    let _ = || true;
 +}
 +
 +fn test_macro_call() -> i32 {
-     return the_answer!();
++    the_answer!()
 +}
 +
 +fn test_void_fun() {
 +    
 +}
 +
 +fn test_void_if_fun(b: bool) {
 +    if b {
 +        
 +    } else {
 +        
 +    }
 +}
 +
 +fn test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => (),
 +    }
 +}
 +
 +fn test_nested_match(x: u32) {
 +    match x {
 +        0 => (),
 +        1 => {
 +            let _ = 42;
 +            
 +        },
 +        _ => (),
 +    }
 +}
 +
 +fn read_line() -> String {
 +    use std::io::BufRead;
 +    let stdin = ::std::io::stdin();
 +    return stdin.lock().lines().next().unwrap().unwrap();
 +}
 +
 +fn borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        use std::io::BufRead;
 +        let stdin = ::std::io::stdin();
 +        let _a = stdin.lock().lines().next().unwrap().unwrap();
 +        String::from("test")
 +    } else {
 +        String::new()
 +    }
 +}
 +
 +macro_rules! needed_return {
 +    ($e:expr) => {
 +        if $e > 3 {
 +            return;
 +        }
 +    };
 +}
 +
 +fn test_return_in_macro() {
 +    // This will return and the macro below won't be executed. Removing the `return` from the macro
 +    // will change semantics.
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +mod issue6501 {
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn foo(bar: Result<(), ()>) {
 +        bar.unwrap_or_else(|_| {})
 +    }
 +
 +    fn test_closure() {
 +        let _ = || {
 +            
 +        };
 +        let _ = || {};
 +    }
 +
 +    struct Foo;
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn bar(res: Result<Foo, u8>) -> Foo {
 +        res.unwrap_or_else(|_| Foo)
 +    }
 +}
 +
 +async fn async_test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    true
 +}
 +
 +async fn async_test_no_semicolon() -> bool {
 +    true
 +}
 +
 +async fn async_test_if_block() -> bool {
 +    if true {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +async fn async_test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => {
 +            true
 +        },
 +    }
 +}
 +
 +async fn async_test_closure() {
 +    let _ = || {
 +        true
 +    };
 +    let _ = || true;
 +}
 +
 +async fn async_test_macro_call() -> i32 {
++    the_answer!()
 +}
 +
 +async fn async_test_void_fun() {
 +    
 +}
 +
 +async fn async_test_void_if_fun(b: bool) {
 +    if b {
 +        
 +    } else {
 +        
 +    }
 +}
 +
 +async fn async_test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => (),
 +    }
 +}
 +
 +async fn async_read_line() -> String {
 +    use std::io::BufRead;
 +    let stdin = ::std::io::stdin();
 +    return stdin.lock().lines().next().unwrap().unwrap();
 +}
 +
 +async fn async_borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        use std::io::BufRead;
 +        let stdin = ::std::io::stdin();
 +        let _a = stdin.lock().lines().next().unwrap().unwrap();
 +        String::from("test")
 +    } else {
 +        String::new()
 +    }
 +}
 +
 +async fn async_test_return_in_macro() {
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +fn let_else() {
 +    let Some(1) = Some(1) else { return };
 +}
 +
++fn needless_return_macro() -> String {
++    let _ = "foo";
++    let _ = "bar";
++    format!("Hello {}", "world!")
++}
++
 +fn main() {}
index c6c8cb9ec152049e19929ab16092c4633f3b6112,0000000000000000000000000000000000000000..fe82af00e67503c9591cb4bc338cc20f49f03b98
mode 100644,000000..100644
--- /dev/null
@@@ -1,226 -1,0 +1,232 @@@
 +// run-rustfix
 +
 +#![feature(let_else)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::if_same_then_else,
 +    clippy::single_match,
 +    clippy::needless_bool,
 +    clippy::equatable_if_let
 +)]
 +#![warn(clippy::needless_return)]
 +
 +macro_rules! the_answer {
 +    () => {
 +        42
 +    };
 +}
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    return true;
 +}
 +
 +fn test_no_semicolon() -> bool {
 +    return true;
 +}
 +
 +fn test_if_block() -> bool {
 +    if true {
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => {
 +            return true;
 +        },
 +    }
 +}
 +
 +fn test_closure() {
 +    let _ = || {
 +        return true;
 +    };
 +    let _ = || return true;
 +}
 +
 +fn test_macro_call() -> i32 {
 +    return the_answer!();
 +}
 +
 +fn test_void_fun() {
 +    return;
 +}
 +
 +fn test_void_if_fun(b: bool) {
 +    if b {
 +        return;
 +    } else {
 +        return;
 +    }
 +}
 +
 +fn test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => return,
 +    }
 +}
 +
 +fn test_nested_match(x: u32) {
 +    match x {
 +        0 => (),
 +        1 => {
 +            let _ = 42;
 +            return;
 +        },
 +        _ => return,
 +    }
 +}
 +
 +fn read_line() -> String {
 +    use std::io::BufRead;
 +    let stdin = ::std::io::stdin();
 +    return stdin.lock().lines().next().unwrap().unwrap();
 +}
 +
 +fn borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        use std::io::BufRead;
 +        let stdin = ::std::io::stdin();
 +        let _a = stdin.lock().lines().next().unwrap().unwrap();
 +        return String::from("test");
 +    } else {
 +        return String::new();
 +    }
 +}
 +
 +macro_rules! needed_return {
 +    ($e:expr) => {
 +        if $e > 3 {
 +            return;
 +        }
 +    };
 +}
 +
 +fn test_return_in_macro() {
 +    // This will return and the macro below won't be executed. Removing the `return` from the macro
 +    // will change semantics.
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +mod issue6501 {
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn foo(bar: Result<(), ()>) {
 +        bar.unwrap_or_else(|_| return)
 +    }
 +
 +    fn test_closure() {
 +        let _ = || {
 +            return;
 +        };
 +        let _ = || return;
 +    }
 +
 +    struct Foo;
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn bar(res: Result<Foo, u8>) -> Foo {
 +        res.unwrap_or_else(|_| return Foo)
 +    }
 +}
 +
 +async fn async_test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    return true;
 +}
 +
 +async fn async_test_no_semicolon() -> bool {
 +    return true;
 +}
 +
 +async fn async_test_if_block() -> bool {
 +    if true {
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +async fn async_test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => {
 +            return true;
 +        },
 +    }
 +}
 +
 +async fn async_test_closure() {
 +    let _ = || {
 +        return true;
 +    };
 +    let _ = || return true;
 +}
 +
 +async fn async_test_macro_call() -> i32 {
 +    return the_answer!();
 +}
 +
 +async fn async_test_void_fun() {
 +    return;
 +}
 +
 +async fn async_test_void_if_fun(b: bool) {
 +    if b {
 +        return;
 +    } else {
 +        return;
 +    }
 +}
 +
 +async fn async_test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => return,
 +    }
 +}
 +
 +async fn async_read_line() -> String {
 +    use std::io::BufRead;
 +    let stdin = ::std::io::stdin();
 +    return stdin.lock().lines().next().unwrap().unwrap();
 +}
 +
 +async fn async_borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        use std::io::BufRead;
 +        let stdin = ::std::io::stdin();
 +        let _a = stdin.lock().lines().next().unwrap().unwrap();
 +        return String::from("test");
 +    } else {
 +        return String::new();
 +    }
 +}
 +
 +async fn async_test_return_in_macro() {
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +fn let_else() {
 +    let Some(1) = Some(1) else { return };
 +}
 +
++fn needless_return_macro() -> String {
++    let _ = "foo";
++    let _ = "bar";
++    return format!("Hello {}", "world!");
++}
++
 +fn main() {}
index 5bc787c56a65bca559178a498e846a0ce6392942,0000000000000000000000000000000000000000..4c8be47b025e21b60c8d348d4d908ce842175b6c
mode 100644,000000..100644
--- /dev/null
@@@ -1,208 -1,0 +1,226 @@@
- error: aborting due to 34 previous errors
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:24:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^^ help: remove `return`: `true`
 +   |
 +   = note: `-D clippy::needless-return` implied by `-D warnings`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:28:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:33:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:35:9
 +   |
 +LL |         return false;
 +   |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:41:17
 +   |
 +LL |         true => return false,
 +   |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:43:13
 +   |
 +LL |             return true;
 +   |             ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:50:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:52:16
 +   |
 +LL |     let _ = || return true;
 +   |                ^^^^^^^^^^^ help: remove `return`: `true`
 +
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:56:5
++   |
++LL |     return the_answer!();
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
++
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:60:5
 +   |
 +LL |     return;
 +   |     ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:65:9
 +   |
 +LL |         return;
 +   |         ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:67:9
 +   |
 +LL |         return;
 +   |         ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:74:14
 +   |
 +LL |         _ => return,
 +   |              ^^^^^^ help: replace `return` with a unit value: `()`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:83:13
 +   |
 +LL |             return;
 +   |             ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:85:14
 +   |
 +LL |         _ => return,
 +   |              ^^^^^^ help: replace `return` with a unit value: `()`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:100:9
 +   |
 +LL |         return String::from("test");
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:102:9
 +   |
 +LL |         return String::new();
 +   |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:124:32
 +   |
 +LL |         bar.unwrap_or_else(|_| return)
 +   |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:129:13
 +   |
 +LL |             return;
 +   |             ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:131:20
 +   |
 +LL |         let _ = || return;
 +   |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:137:32
 +   |
 +LL |         res.unwrap_or_else(|_| return Foo)
 +   |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:146:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:150:5
 +   |
 +LL |     return true;
 +   |     ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:155:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:157:9
 +   |
 +LL |         return false;
 +   |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:163:17
 +   |
 +LL |         true => return false,
 +   |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:165:13
 +   |
 +LL |             return true;
 +   |             ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:172:9
 +   |
 +LL |         return true;
 +   |         ^^^^^^^^^^^^ help: remove `return`: `true`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:174:16
 +   |
 +LL |     let _ = || return true;
 +   |                ^^^^^^^^^^^ help: remove `return`: `true`
 +
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:178:5
++   |
++LL |     return the_answer!();
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
++
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:182:5
 +   |
 +LL |     return;
 +   |     ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:187:9
 +   |
 +LL |         return;
 +   |         ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:189:9
 +   |
 +LL |         return;
 +   |         ^^^^^^^ help: remove `return`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:196:14
 +   |
 +LL |         _ => return,
 +   |              ^^^^^^ help: replace `return` with a unit value: `()`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:211:9
 +   |
 +LL |         return String::from("test");
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 +
 +error: unneeded `return` statement
 +  --> $DIR/needless_return.rs:213:9
 +   |
 +LL |         return String::new();
 +   |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 +
++error: unneeded `return` statement
++  --> $DIR/needless_return.rs:229:5
++   |
++LL |     return format!("Hello {}", "world!");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")`
++
++error: aborting due to 37 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ad17d53f78906d5c4cb309f53216d6866441a2d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,51 @@@
++#![warn(clippy::no_effect_replace)]
++
++fn main() {
++    let _ = "12345".replace('1', "1");
++    let _ = "12345".replace("12", "12");
++    let _ = String::new().replace("12", "12");
++
++    let _ = "12345".replacen('1', "1", 1);
++    let _ = "12345".replacen("12", "12", 1);
++    let _ = String::new().replacen("12", "12", 1);
++
++    let _ = "12345".replace("12", "22");
++    let _ = "12345".replacen("12", "22", 1);
++
++    let mut x = X::default();
++    let _ = "hello".replace(&x.f(), &x.f());
++    let _ = "hello".replace(&x.f(), &x.ff());
++
++    let _ = "hello".replace(&y(), &y());
++    let _ = "hello".replace(&y(), &z());
++
++    let _ = Replaceme.replace("a", "a");
++}
++
++#[derive(Default)]
++struct X {}
++
++impl X {
++    fn f(&mut self) -> String {
++        "he".to_string()
++    }
++
++    fn ff(&mut self) -> String {
++        "hh".to_string()
++    }
++}
++
++fn y() -> String {
++    "he".to_string()
++}
++
++fn z() -> String {
++    "hh".to_string()
++}
++
++struct Replaceme;
++impl Replaceme {
++    pub fn replace(&mut self, a: &str, b: &str) -> Self {
++        Self
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..53a28aa73b707e46ecae822e00e266bdace39e8a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:4:13
++   |
++LL |     let _ = "12345".replace('1', "1");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::no-effect-replace` implied by `-D warnings`
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:5:13
++   |
++LL |     let _ = "12345".replace("12", "12");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:6:13
++   |
++LL |     let _ = String::new().replace("12", "12");
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:8:13
++   |
++LL |     let _ = "12345".replacen('1', "1", 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:9:13
++   |
++LL |     let _ = "12345".replacen("12", "12", 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:10:13
++   |
++LL |     let _ = String::new().replacen("12", "12", 1);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:16:13
++   |
++LL |     let _ = "hello".replace(&x.f(), &x.f());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: replacing text with itself
++  --> $DIR/no_effect_replace.rs:19:13
++   |
++LL |     let _ = "hello".replace(&y(), &y());
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 8 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..aad44089de499507c09078348682efd8aad2a29b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,111 @@@
++// run-rustfix
++#![allow(unused, clippy::diverging_sub_expression)]
++#![warn(clippy::nonminimal_bool)]
++
++fn methods_with_negation() {
++    let a: Option<i32> = unimplemented!();
++    let b: Result<i32, i32> = unimplemented!();
++    let _ = a.is_some();
++    let _ = a.is_none();
++    let _ = a.is_none();
++    let _ = a.is_some();
++    let _ = b.is_err();
++    let _ = b.is_ok();
++    let _ = b.is_ok();
++    let _ = b.is_err();
++    let c = false;
++    let _ = a.is_none() || c;
++    let _ = a.is_none() && c;
++    let _ = !(!c ^ c) || a.is_none();
++    let _ = (!c ^ c) || a.is_none();
++    let _ = !c ^ c || a.is_none();
++}
++
++// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
++// clippy::nonminimal_bool should only check the built-in Result and Some type, not
++// any other types like the following.
++enum CustomResultOk<E> {
++    Ok,
++    Err(E),
++}
++enum CustomResultErr<E> {
++    Ok,
++    Err(E),
++}
++enum CustomSomeSome<T> {
++    Some(T),
++    None,
++}
++enum CustomSomeNone<T> {
++    Some(T),
++    None,
++}
++
++impl<E> CustomResultOk<E> {
++    pub fn is_ok(&self) -> bool {
++        true
++    }
++}
++
++impl<E> CustomResultErr<E> {
++    pub fn is_err(&self) -> bool {
++        true
++    }
++}
++
++impl<T> CustomSomeSome<T> {
++    pub fn is_some(&self) -> bool {
++        true
++    }
++}
++
++impl<T> CustomSomeNone<T> {
++    pub fn is_none(&self) -> bool {
++        true
++    }
++}
++
++fn dont_warn_for_custom_methods_with_negation() {
++    let res = CustomResultOk::Err("Error");
++    // Should not warn and suggest 'is_err()' because the type does not
++    // implement is_err().
++    if !res.is_ok() {}
++
++    let res = CustomResultErr::Err("Error");
++    // Should not warn and suggest 'is_ok()' because the type does not
++    // implement is_ok().
++    if !res.is_err() {}
++
++    let res = CustomSomeSome::Some("thing");
++    // Should not warn and suggest 'is_none()' because the type does not
++    // implement is_none().
++    if !res.is_some() {}
++
++    let res = CustomSomeNone::Some("thing");
++    // Should not warn and suggest 'is_some()' because the type does not
++    // implement is_some().
++    if !res.is_none() {}
++}
++
++// Only Built-in Result and Some types should suggest the negated alternative
++fn warn_for_built_in_methods_with_negation() {
++    let res: Result<usize, usize> = Ok(1);
++    if res.is_err() {}
++    if res.is_ok() {}
++
++    let res = Some(1);
++    if res.is_none() {}
++    if res.is_some() {}
++}
++
++#[allow(clippy::neg_cmp_op_on_partial_ord)]
++fn dont_warn_for_negated_partial_ord_comparison() {
++    let a: f64 = unimplemented!();
++    let b: f64 = unimplemented!();
++    let _ = !(a < b);
++    let _ = !(a <= b);
++    let _ = !(a > b);
++    let _ = !(a >= b);
++}
++
++fn main() {}
index d0a289b7ea43e1f88ec324c33cdbd9f11c61a406,0000000000000000000000000000000000000000..b9074da842706c6f3f80d484baaa7013ea893ae5
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,111 @@@
++// run-rustfix
 +#![allow(unused, clippy::diverging_sub_expression)]
 +#![warn(clippy::nonminimal_bool)]
 +
 +fn methods_with_negation() {
 +    let a: Option<i32> = unimplemented!();
 +    let b: Result<i32, i32> = unimplemented!();
 +    let _ = a.is_some();
 +    let _ = !a.is_some();
 +    let _ = a.is_none();
 +    let _ = !a.is_none();
 +    let _ = b.is_err();
 +    let _ = !b.is_err();
 +    let _ = b.is_ok();
 +    let _ = !b.is_ok();
 +    let c = false;
 +    let _ = !(a.is_some() && !c);
 +    let _ = !(a.is_some() || !c);
 +    let _ = !(!c ^ c) || !a.is_some();
 +    let _ = (!c ^ c) || !a.is_some();
 +    let _ = !c ^ c || !a.is_some();
 +}
 +
 +// Simplified versions of https://github.com/rust-lang/rust-clippy/issues/2638
 +// clippy::nonminimal_bool should only check the built-in Result and Some type, not
 +// any other types like the following.
 +enum CustomResultOk<E> {
 +    Ok,
 +    Err(E),
 +}
 +enum CustomResultErr<E> {
 +    Ok,
 +    Err(E),
 +}
 +enum CustomSomeSome<T> {
 +    Some(T),
 +    None,
 +}
 +enum CustomSomeNone<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<E> CustomResultOk<E> {
 +    pub fn is_ok(&self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<E> CustomResultErr<E> {
 +    pub fn is_err(&self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<T> CustomSomeSome<T> {
 +    pub fn is_some(&self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<T> CustomSomeNone<T> {
 +    pub fn is_none(&self) -> bool {
 +        true
 +    }
 +}
 +
 +fn dont_warn_for_custom_methods_with_negation() {
 +    let res = CustomResultOk::Err("Error");
 +    // Should not warn and suggest 'is_err()' because the type does not
 +    // implement is_err().
 +    if !res.is_ok() {}
 +
 +    let res = CustomResultErr::Err("Error");
 +    // Should not warn and suggest 'is_ok()' because the type does not
 +    // implement is_ok().
 +    if !res.is_err() {}
 +
 +    let res = CustomSomeSome::Some("thing");
 +    // Should not warn and suggest 'is_none()' because the type does not
 +    // implement is_none().
 +    if !res.is_some() {}
 +
 +    let res = CustomSomeNone::Some("thing");
 +    // Should not warn and suggest 'is_some()' because the type does not
 +    // implement is_some().
 +    if !res.is_none() {}
 +}
 +
 +// Only Built-in Result and Some types should suggest the negated alternative
 +fn warn_for_built_in_methods_with_negation() {
 +    let res: Result<usize, usize> = Ok(1);
 +    if !res.is_ok() {}
 +    if !res.is_err() {}
 +
 +    let res = Some(1);
 +    if !res.is_some() {}
 +    if !res.is_none() {}
 +}
 +
 +#[allow(clippy::neg_cmp_op_on_partial_ord)]
 +fn dont_warn_for_negated_partial_ord_comparison() {
 +    let a: f64 = unimplemented!();
 +    let b: f64 = unimplemented!();
 +    let _ = !(a < b);
 +    let _ = !(a <= b);
 +    let _ = !(a > b);
 +    let _ = !(a >= b);
 +}
 +
 +fn main() {}
index a2df889d6230215dd44547e788991a47e61f6782,0000000000000000000000000000000000000000..21b84db85890964b9dec175edbe00ae81a7a8b9b
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,82 @@@
-   --> $DIR/nonminimal_bool_methods.rs:8:13
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:10:13
++  --> $DIR/nonminimal_bool_methods.rs:9:13
 +   |
 +LL |     let _ = !a.is_some();
 +   |             ^^^^^^^^^^^^ help: try: `a.is_none()`
 +   |
 +   = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:12:13
++  --> $DIR/nonminimal_bool_methods.rs:11:13
 +   |
 +LL |     let _ = !a.is_none();
 +   |             ^^^^^^^^^^^^ help: try: `a.is_some()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:14:13
++  --> $DIR/nonminimal_bool_methods.rs:13:13
 +   |
 +LL |     let _ = !b.is_err();
 +   |             ^^^^^^^^^^^ help: try: `b.is_ok()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:16:13
++  --> $DIR/nonminimal_bool_methods.rs:15:13
 +   |
 +LL |     let _ = !b.is_ok();
 +   |             ^^^^^^^^^^ help: try: `b.is_err()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:17:13
++  --> $DIR/nonminimal_bool_methods.rs:17:13
 +   |
 +LL |     let _ = !(a.is_some() && !c);
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() || c`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:18:26
++  --> $DIR/nonminimal_bool_methods.rs:18:13
 +   |
 +LL |     let _ = !(a.is_some() || !c);
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: try: `a.is_none() && c`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:19:25
++  --> $DIR/nonminimal_bool_methods.rs:19:26
 +   |
 +LL |     let _ = !(!c ^ c) || !a.is_some();
 +   |                          ^^^^^^^^^^^^ help: try: `a.is_none()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:20:23
++  --> $DIR/nonminimal_bool_methods.rs:20:25
 +   |
 +LL |     let _ = (!c ^ c) || !a.is_some();
 +   |                         ^^^^^^^^^^^^ help: try: `a.is_none()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:92:8
++  --> $DIR/nonminimal_bool_methods.rs:21:23
 +   |
 +LL |     let _ = !c ^ c || !a.is_some();
 +   |                       ^^^^^^^^^^^^ help: try: `a.is_none()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:93:8
++  --> $DIR/nonminimal_bool_methods.rs:93:8
 +   |
 +LL |     if !res.is_ok() {}
 +   |        ^^^^^^^^^^^^ help: try: `res.is_err()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:96:8
++  --> $DIR/nonminimal_bool_methods.rs:94:8
 +   |
 +LL |     if !res.is_err() {}
 +   |        ^^^^^^^^^^^^^ help: try: `res.is_ok()`
 +
 +error: this boolean expression can be simplified
-   --> $DIR/nonminimal_bool_methods.rs:97:8
++  --> $DIR/nonminimal_bool_methods.rs:97:8
 +   |
 +LL |     if !res.is_some() {}
 +   |        ^^^^^^^^^^^^^^ help: try: `res.is_none()`
 +
 +error: this boolean expression can be simplified
++  --> $DIR/nonminimal_bool_methods.rs:98:8
 +   |
 +LL |     if !res.is_none() {}
 +   |        ^^^^^^^^^^^^^^ help: try: `res.is_some()`
 +
 +error: aborting due to 13 previous errors
 +
index f497719971174a14bf03a97cf96734bbe12d168d,0000000000000000000000000000000000000000..85d021b2f25e2094435df55fc9e40bcaa39caa34
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,64 @@@
 +// run-rustfix
 +
 +#[warn(clippy::manual_range_contains)]
 +#[allow(unused)]
 +#[allow(clippy::no_effect)]
 +#[allow(clippy::short_circuit_statement)]
 +#[allow(clippy::unnecessary_operation)]
 +fn main() {
 +    let x = 9_i32;
 +
 +    // order shouldn't matter
 +    (8..12).contains(&x);
 +    (21..42).contains(&x);
 +    (1..100).contains(&x);
 +
 +    // also with inclusive ranges
 +    (9..=99).contains(&x);
 +    (1..=33).contains(&x);
 +    (1..=999).contains(&x);
 +
 +    // and the outside
 +    !(8..12).contains(&x);
 +    !(21..42).contains(&x);
 +    !(1..100).contains(&x);
 +
 +    // also with the outside of inclusive ranges
 +    !(9..=99).contains(&x);
 +    !(1..=33).contains(&x);
 +    !(1..=999).contains(&x);
 +
 +    // not a range.contains
 +    x > 8 && x < 12; // lower bound not inclusive
 +    x < 8 && x <= 12; // same direction
 +    x >= 12 && 12 >= x; // same bounds
 +    x < 8 && x > 12; // wrong direction
 +
 +    x <= 8 || x >= 12;
 +    x >= 8 || x >= 12;
 +    x < 12 || 12 < x;
 +    x >= 8 || x <= 12;
 +
 +    // Fix #6315
 +    let y = 3.;
 +    (0. ..1.).contains(&y);
 +    !(0. ..=1.).contains(&y);
 +
 +    // handle negatives #8721
 +    (-10..=10).contains(&x);
 +    x >= 10 && x <= -10;
 +    (-3. ..=3.).contains(&y);
 +    y >= 3. && y <= -3.;
++
++    // Fix #8745
++    let z = 42;
++    (0..=10).contains(&x) && (0..=10).contains(&z);
++    !(0..10).contains(&x) || !(0..10).contains(&z);
++    // Make sure operators in parens don't give a breaking suggestion
++    ((x % 2 == 0) || (x < 0)) || (x >= 10);
 +}
 +
 +// Fix #6373
 +pub const fn in_range(a: i32) -> bool {
 +    3 <= a && a <= 20
 +}
index 9e2180b0c9944290a4fd872c8e5d7692f2680d23,0000000000000000000000000000000000000000..9a7a75dc13254ed2883943eabbd0c350fd991b6a
mode 100644,000000..100644
--- /dev/null
@@@ -1,57 -1,0 +1,64 @@@
 +// run-rustfix
 +
 +#[warn(clippy::manual_range_contains)]
 +#[allow(unused)]
 +#[allow(clippy::no_effect)]
 +#[allow(clippy::short_circuit_statement)]
 +#[allow(clippy::unnecessary_operation)]
 +fn main() {
 +    let x = 9_i32;
 +
 +    // order shouldn't matter
 +    x >= 8 && x < 12;
 +    x < 42 && x >= 21;
 +    100 > x && 1 <= x;
 +
 +    // also with inclusive ranges
 +    x >= 9 && x <= 99;
 +    x <= 33 && x >= 1;
 +    999 >= x && 1 <= x;
 +
 +    // and the outside
 +    x < 8 || x >= 12;
 +    x >= 42 || x < 21;
 +    100 <= x || 1 > x;
 +
 +    // also with the outside of inclusive ranges
 +    x < 9 || x > 99;
 +    x > 33 || x < 1;
 +    999 < x || 1 > x;
 +
 +    // not a range.contains
 +    x > 8 && x < 12; // lower bound not inclusive
 +    x < 8 && x <= 12; // same direction
 +    x >= 12 && 12 >= x; // same bounds
 +    x < 8 && x > 12; // wrong direction
 +
 +    x <= 8 || x >= 12;
 +    x >= 8 || x >= 12;
 +    x < 12 || 12 < x;
 +    x >= 8 || x <= 12;
 +
 +    // Fix #6315
 +    let y = 3.;
 +    y >= 0. && y < 1.;
 +    y < 0. || y > 1.;
 +
 +    // handle negatives #8721
 +    x >= -10 && x <= 10;
 +    x >= 10 && x <= -10;
 +    y >= -3. && y <= 3.;
 +    y >= 3. && y <= -3.;
++
++    // Fix #8745
++    let z = 42;
++    (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
++    (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
++    // Make sure operators in parens don't give a breaking suggestion
++    ((x % 2 == 0) || (x < 0)) || (x >= 10);
 +}
 +
 +// Fix #6373
 +pub const fn in_range(a: i32) -> bool {
 +    3 <= a && a <= 20
 +}
index 1817ee1715d1779c463c7c549147ba91bea489f8,0000000000000000000000000000000000000000..936859db5a1260ba217bcb91b31ef91e90fa9a2c
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,124 @@@
- error: aborting due to 16 previous errors
 +error: manual `Range::contains` implementation
 +  --> $DIR/range_contains.rs:12:5
 +   |
 +LL |     x >= 8 && x < 12;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
 +   |
 +   = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 +
 +error: manual `Range::contains` implementation
 +  --> $DIR/range_contains.rs:13:5
 +   |
 +LL |     x < 42 && x >= 21;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
 +
 +error: manual `Range::contains` implementation
 +  --> $DIR/range_contains.rs:14:5
 +   |
 +LL |     100 > x && 1 <= x;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:17:5
 +   |
 +LL |     x >= 9 && x <= 99;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:18:5
 +   |
 +LL |     x <= 33 && x >= 1;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:19:5
 +   |
 +LL |     999 >= x && 1 <= x;
 +   |     ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
 +  --> $DIR/range_contains.rs:22:5
 +   |
 +LL |     x < 8 || x >= 12;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
 +  --> $DIR/range_contains.rs:23:5
 +   |
 +LL |     x >= 42 || x < 21;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
 +
 +error: manual `!Range::contains` implementation
 +  --> $DIR/range_contains.rs:24:5
 +   |
 +LL |     100 <= x || 1 > x;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
 +
 +error: manual `!RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:27:5
 +   |
 +LL |     x < 9 || x > 99;
 +   |     ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
 +
 +error: manual `!RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:28:5
 +   |
 +LL |     x > 33 || x < 1;
 +   |     ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
 +
 +error: manual `!RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:29:5
 +   |
 +LL |     999 < x || 1 > x;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
 +
 +error: manual `Range::contains` implementation
 +  --> $DIR/range_contains.rs:44:5
 +   |
 +LL |     y >= 0. && y < 1.;
 +   |     ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)`
 +
 +error: manual `!RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:45:5
 +   |
 +LL |     y < 0. || y > 1.;
 +   |     ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)`
 +
 +error: manual `RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:48:5
 +   |
 +LL |     x >= -10 && x <= 10;
 +   |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)`
 +
 +error: manual `RangeInclusive::contains` implementation
 +  --> $DIR/range_contains.rs:50:5
 +   |
 +LL |     y >= -3. && y <= 3.;
 +   |     ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)`
 +
++error: manual `RangeInclusive::contains` implementation
++  --> $DIR/range_contains.rs:55:30
++   |
++LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
++   |                              ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)`
++
++error: manual `RangeInclusive::contains` implementation
++  --> $DIR/range_contains.rs:55:5
++   |
++LL |     (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10);
++   |     ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)`
++
++error: manual `!Range::contains` implementation
++  --> $DIR/range_contains.rs:56:29
++   |
++LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
++   |                             ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)`
++
++error: manual `!Range::contains` implementation
++  --> $DIR/range_contains.rs:56:5
++   |
++LL |     (x < 0) || (x >= 10) || (z < 0) || (z >= 10);
++   |     ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)`
++
++error: aborting due to 20 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8910c01b1fcf9ebdee2d0eff39a0e864466172f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++// run-rustfix
++#![warn(clippy::rc_buffer)]
++#![allow(dead_code, unused_imports)]
++
++use std::cell::RefCell;
++use std::ffi::OsString;
++use std::path::PathBuf;
++use std::rc::Rc;
++
++struct S {
++    // triggers lint
++    bad1: Rc<str>,
++    bad2: Rc<std::path::Path>,
++    bad3: Rc<[u8]>,
++    bad4: Rc<std::ffi::OsStr>,
++    // does not trigger lint
++    good1: Rc<RefCell<String>>,
++}
++
++// triggers lint
++fn func_bad1(_: Rc<str>) {}
++fn func_bad2(_: Rc<std::path::Path>) {}
++fn func_bad3(_: Rc<[u8]>) {}
++fn func_bad4(_: Rc<std::ffi::OsStr>) {}
++// does not trigger lint
++fn func_good1(_: Rc<RefCell<String>>) {}
++
++fn main() {}
index 1fa986439368782b94c053be0a0b850e2e4fac9a,0000000000000000000000000000000000000000..1e63a43262ec197156ecace133090bf58a70c197
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,28 @@@
++// run-rustfix
 +#![warn(clippy::rc_buffer)]
++#![allow(dead_code, unused_imports)]
 +
 +use std::cell::RefCell;
 +use std::ffi::OsString;
 +use std::path::PathBuf;
 +use std::rc::Rc;
 +
 +struct S {
 +    // triggers lint
 +    bad1: Rc<String>,
 +    bad2: Rc<PathBuf>,
 +    bad3: Rc<Vec<u8>>,
 +    bad4: Rc<OsString>,
 +    // does not trigger lint
 +    good1: Rc<RefCell<String>>,
 +}
 +
 +// triggers lint
 +fn func_bad1(_: Rc<String>) {}
 +fn func_bad2(_: Rc<PathBuf>) {}
 +fn func_bad3(_: Rc<Vec<u8>>) {}
 +fn func_bad4(_: Rc<OsString>) {}
 +// does not trigger lint
 +fn func_good1(_: Rc<RefCell<String>>) {}
 +
 +fn main() {}
index e4cc169af07b96f0dbbbf1490e153881d77526ca,0000000000000000000000000000000000000000..9ed028e3df41bf78e3f7c4d7f75cfe098ffa4b19
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
-   --> $DIR/rc_buffer.rs:10:11
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:11:11
++  --> $DIR/rc_buffer.rs:12:11
 +   |
 +LL |     bad1: Rc<String>,
 +   |           ^^^^^^^^^^ help: try: `Rc<str>`
 +   |
 +   = note: `-D clippy::rc-buffer` implied by `-D warnings`
 +
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:12:11
++  --> $DIR/rc_buffer.rs:13:11
 +   |
 +LL |     bad2: Rc<PathBuf>,
 +   |           ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
 +
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:13:11
++  --> $DIR/rc_buffer.rs:14:11
 +   |
 +LL |     bad3: Rc<Vec<u8>>,
 +   |           ^^^^^^^^^^^ help: try: `Rc<[u8]>`
 +
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:19:17
++  --> $DIR/rc_buffer.rs:15:11
 +   |
 +LL |     bad4: Rc<OsString>,
 +   |           ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
 +
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:20:17
++  --> $DIR/rc_buffer.rs:21:17
 +   |
 +LL | fn func_bad1(_: Rc<String>) {}
 +   |                 ^^^^^^^^^^ help: try: `Rc<str>`
 +
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:21:17
++  --> $DIR/rc_buffer.rs:22:17
 +   |
 +LL | fn func_bad2(_: Rc<PathBuf>) {}
 +   |                 ^^^^^^^^^^^ help: try: `Rc<std::path::Path>`
 +
 +error: usage of `Rc<T>` when T is a buffer type
-   --> $DIR/rc_buffer.rs:22:17
++  --> $DIR/rc_buffer.rs:23:17
 +   |
 +LL | fn func_bad3(_: Rc<Vec<u8>>) {}
 +   |                 ^^^^^^^^^^^ help: try: `Rc<[u8]>`
 +
 +error: usage of `Rc<T>` when T is a buffer type
++  --> $DIR/rc_buffer.rs:24:17
 +   |
 +LL | fn func_bad4(_: Rc<OsString>) {}
 +   |                 ^^^^^^^^^^^^ help: try: `Rc<std::ffi::OsStr>`
 +
 +error: aborting due to 8 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..13dd6f5fcd186a4d18d9ac6228d0b369be37d9cf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++// run-rustfix
++#![warn(clippy::rc_buffer)]
++#![allow(dead_code, unused_imports)]
++
++use std::ffi::OsString;
++use std::path::PathBuf;
++use std::sync::{Arc, Mutex};
++
++struct S {
++    // triggers lint
++    bad1: Arc<str>,
++    bad2: Arc<std::path::Path>,
++    bad3: Arc<[u8]>,
++    bad4: Arc<std::ffi::OsStr>,
++    // does not trigger lint
++    good1: Arc<Mutex<String>>,
++}
++
++// triggers lint
++fn func_bad1(_: Arc<str>) {}
++fn func_bad2(_: Arc<std::path::Path>) {}
++fn func_bad3(_: Arc<[u8]>) {}
++fn func_bad4(_: Arc<std::ffi::OsStr>) {}
++// does not trigger lint
++fn func_good1(_: Arc<Mutex<String>>) {}
++
++fn main() {}
index 5d586584817bd26d70e7e6f75a90750242ba17b1,0000000000000000000000000000000000000000..1a521bfeb7c8661f3a7fdabbc6759e38662e235d
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,27 @@@
++// run-rustfix
 +#![warn(clippy::rc_buffer)]
++#![allow(dead_code, unused_imports)]
 +
 +use std::ffi::OsString;
 +use std::path::PathBuf;
 +use std::sync::{Arc, Mutex};
 +
 +struct S {
 +    // triggers lint
 +    bad1: Arc<String>,
 +    bad2: Arc<PathBuf>,
 +    bad3: Arc<Vec<u8>>,
 +    bad4: Arc<OsString>,
 +    // does not trigger lint
 +    good1: Arc<Mutex<String>>,
 +}
 +
 +// triggers lint
 +fn func_bad1(_: Arc<String>) {}
 +fn func_bad2(_: Arc<PathBuf>) {}
 +fn func_bad3(_: Arc<Vec<u8>>) {}
 +fn func_bad4(_: Arc<OsString>) {}
 +// does not trigger lint
 +fn func_good1(_: Arc<Mutex<String>>) {}
 +
 +fn main() {}
index 8252270d2ac7491ba1a7653e85388eb0ce432eea,0000000000000000000000000000000000000000..911feea73529dcdc4f0c44bcad03125c961662ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
-   --> $DIR/rc_buffer_arc.rs:9:11
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:10:11
++  --> $DIR/rc_buffer_arc.rs:11:11
 +   |
 +LL |     bad1: Arc<String>,
 +   |           ^^^^^^^^^^^ help: try: `Arc<str>`
 +   |
 +   = note: `-D clippy::rc-buffer` implied by `-D warnings`
 +
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:11:11
++  --> $DIR/rc_buffer_arc.rs:12:11
 +   |
 +LL |     bad2: Arc<PathBuf>,
 +   |           ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
 +
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:12:11
++  --> $DIR/rc_buffer_arc.rs:13:11
 +   |
 +LL |     bad3: Arc<Vec<u8>>,
 +   |           ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
 +
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:18:17
++  --> $DIR/rc_buffer_arc.rs:14:11
 +   |
 +LL |     bad4: Arc<OsString>,
 +   |           ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
 +
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:19:17
++  --> $DIR/rc_buffer_arc.rs:20:17
 +   |
 +LL | fn func_bad1(_: Arc<String>) {}
 +   |                 ^^^^^^^^^^^ help: try: `Arc<str>`
 +
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:20:17
++  --> $DIR/rc_buffer_arc.rs:21:17
 +   |
 +LL | fn func_bad2(_: Arc<PathBuf>) {}
 +   |                 ^^^^^^^^^^^^ help: try: `Arc<std::path::Path>`
 +
 +error: usage of `Arc<T>` when T is a buffer type
-   --> $DIR/rc_buffer_arc.rs:21:17
++  --> $DIR/rc_buffer_arc.rs:22:17
 +   |
 +LL | fn func_bad3(_: Arc<Vec<u8>>) {}
 +   |                 ^^^^^^^^^^^^ help: try: `Arc<[u8]>`
 +
 +error: usage of `Arc<T>` when T is a buffer type
++  --> $DIR/rc_buffer_arc.rs:23:17
 +   |
 +LL | fn func_bad4(_: Arc<OsString>) {}
 +   |                 ^^^^^^^^^^^^^ help: try: `Arc<std::ffi::OsStr>`
 +
 +error: aborting due to 8 previous errors
 +
index ce84186c8e300e15c988d76870740f40e13673db,0000000000000000000000000000000000000000..cd7d91e120650490baffe5d139617c71c851f723
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,109 @@@
- error: calling `Arc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/arc.rs:7:13
 +   |
 +LL |     let v = vec![Arc::new("x".to_string()); 2];
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
 +   = note: each element will point to the same `Arc` instance
 +help: consider initializing each `Arc` element individually
 +   |
 +LL ~     let v = {
 +LL +         let mut v = Vec::with_capacity(2);
- LL +         (0..2).for_each(|_| v.push(Arc::new("x".to_string())));
++LL +         (0..2).for_each(|_| v.push(Arc::new(..)));
 +LL +         v
 +LL ~     };
 +   |
 +help: or if this is intentional, consider extracting the `Arc` initialization to a variable
 +   |
 +LL ~     let v = {
- LL +         let data = Arc::new("x".to_string());
++LL +         let data = Arc::new(..);
 +LL +         vec![data; 2]
 +LL ~     };
 +   |
 +
- error: calling `Arc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/arc.rs:15:21
 +   |
 +LL |             let v = vec![Arc::new("x".to_string()); 2];
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: each element will point to the same `Arc` instance
 +help: consider initializing each `Arc` element individually
 +   |
 +LL ~             let v = {
 +LL +                 let mut v = Vec::with_capacity(2);
- LL +                 (0..2).for_each(|_| v.push(Arc::new("x".to_string())));
++LL +                 (0..2).for_each(|_| v.push(Arc::new(..)));
 +LL +                 v
 +LL ~             };
 +   |
 +help: or if this is intentional, consider extracting the `Arc` initialization to a variable
 +   |
 +LL ~             let v = {
- LL +                 let data = Arc::new("x".to_string());
++LL +                 let data = Arc::new(..);
 +LL +                 vec![data; 2]
 +LL ~             };
 +   |
 +
- error: calling `Arc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/arc.rs:21:13
 +   |
 +LL |       let v = vec![
 +   |  _____________^
 +LL | |         std::sync::Arc::new(Mutex::new({
 +LL | |             let x = 1;
 +LL | |             dbg!(x);
 +...  |
 +LL | |         2
 +LL | |     ];
 +   | |_____^
 +   |
 +   = note: each element will point to the same `Arc` instance
 +help: consider initializing each `Arc` element individually
 +   |
 +LL ~     let v = {
 +LL +         let mut v = Vec::with_capacity(2);
 +LL +         (0..2).for_each(|_| v.push(std::sync::Arc::new(..)));
 +LL +         v
 +LL ~     };
 +   |
 +help: or if this is intentional, consider extracting the `Arc` initialization to a variable
 +   |
 +LL ~     let v = {
 +LL +         let data = std::sync::Arc::new(..);
 +LL +         vec![data; 2]
 +LL ~     };
 +   |
 +
- error: calling `Arc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/arc.rs:30:14
 +   |
 +LL |       let v1 = vec![
 +   |  ______________^
 +LL | |         Arc::new(Mutex::new({
 +LL | |             let x = 1;
 +LL | |             dbg!(x);
 +...  |
 +LL | |         2
 +LL | |     ];
 +   | |_____^
 +   |
 +   = note: each element will point to the same `Arc` instance
 +help: consider initializing each `Arc` element individually
 +   |
 +LL ~     let v1 = {
 +LL +         let mut v = Vec::with_capacity(2);
 +LL +         (0..2).for_each(|_| v.push(Arc::new(..)));
 +LL +         v
 +LL ~     };
 +   |
 +help: or if this is intentional, consider extracting the `Arc` initialization to a variable
 +   |
 +LL ~     let v1 = {
 +LL +         let data = Arc::new(..);
 +LL +         vec![data; 2]
 +LL ~     };
 +   |
 +
 +error: aborting due to 4 previous errors
 +
index 0f5cc0cf98feaa415efc44841647f1847b7d9bfc,0000000000000000000000000000000000000000..fe861afe05491fff24dce5fd21623278fe882e61
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,109 @@@
- error: calling `Rc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/rc.rs:8:13
 +   |
 +LL |     let v = vec![Rc::new("x".to_string()); 2];
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
 +   = note: each element will point to the same `Rc` instance
 +help: consider initializing each `Rc` element individually
 +   |
 +LL ~     let v = {
 +LL +         let mut v = Vec::with_capacity(2);
- LL +         (0..2).for_each(|_| v.push(Rc::new("x".to_string())));
++LL +         (0..2).for_each(|_| v.push(Rc::new(..)));
 +LL +         v
 +LL ~     };
 +   |
 +help: or if this is intentional, consider extracting the `Rc` initialization to a variable
 +   |
 +LL ~     let v = {
- LL +         let data = Rc::new("x".to_string());
++LL +         let data = Rc::new(..);
 +LL +         vec![data; 2]
 +LL ~     };
 +   |
 +
- error: calling `Rc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/rc.rs:16:21
 +   |
 +LL |             let v = vec![Rc::new("x".to_string()); 2];
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: each element will point to the same `Rc` instance
 +help: consider initializing each `Rc` element individually
 +   |
 +LL ~             let v = {
 +LL +                 let mut v = Vec::with_capacity(2);
- LL +                 (0..2).for_each(|_| v.push(Rc::new("x".to_string())));
++LL +                 (0..2).for_each(|_| v.push(Rc::new(..)));
 +LL +                 v
 +LL ~             };
 +   |
 +help: or if this is intentional, consider extracting the `Rc` initialization to a variable
 +   |
 +LL ~             let v = {
- LL +                 let data = Rc::new("x".to_string());
++LL +                 let data = Rc::new(..);
 +LL +                 vec![data; 2]
 +LL ~             };
 +   |
 +
- error: calling `Rc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/rc.rs:22:13
 +   |
 +LL |       let v = vec![
 +   |  _____________^
 +LL | |         std::rc::Rc::new(Mutex::new({
 +LL | |             let x = 1;
 +LL | |             dbg!(x);
 +...  |
 +LL | |         2
 +LL | |     ];
 +   | |_____^
 +   |
 +   = note: each element will point to the same `Rc` instance
 +help: consider initializing each `Rc` element individually
 +   |
 +LL ~     let v = {
 +LL +         let mut v = Vec::with_capacity(2);
 +LL +         (0..2).for_each(|_| v.push(std::rc::Rc::new(..)));
 +LL +         v
 +LL ~     };
 +   |
 +help: or if this is intentional, consider extracting the `Rc` initialization to a variable
 +   |
 +LL ~     let v = {
 +LL +         let data = std::rc::Rc::new(..);
 +LL +         vec![data; 2]
 +LL ~     };
 +   |
 +
- error: calling `Rc::new` in `vec![elem; len]`
++error: initializing a reference-counted pointer in `vec![elem; len]`
 +  --> $DIR/rc.rs:31:14
 +   |
 +LL |       let v1 = vec![
 +   |  ______________^
 +LL | |         Rc::new(Mutex::new({
 +LL | |             let x = 1;
 +LL | |             dbg!(x);
 +...  |
 +LL | |         2
 +LL | |     ];
 +   | |_____^
 +   |
 +   = note: each element will point to the same `Rc` instance
 +help: consider initializing each `Rc` element individually
 +   |
 +LL ~     let v1 = {
 +LL +         let mut v = Vec::with_capacity(2);
 +LL +         (0..2).for_each(|_| v.push(Rc::new(..)));
 +LL +         v
 +LL ~     };
 +   |
 +help: or if this is intentional, consider extracting the `Rc` initialization to a variable
 +   |
 +LL ~     let v1 = {
 +LL +         let data = Rc::new(..);
 +LL +         vec![data; 2]
 +LL ~     };
 +   |
 +
 +error: aborting due to 4 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..693c9b553c562fdeaf28629f2ae269b08f7e68bd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,83 @@@
++#![warn(clippy::rc_clone_in_vec_init)]
++use std::rc::{Rc, Weak as UnSyncWeak};
++use std::sync::{Arc, Mutex, Weak as SyncWeak};
++
++fn main() {}
++
++fn should_warn_simple_case() {
++    let v = vec![SyncWeak::<u32>::new(); 2];
++    let v2 = vec![UnSyncWeak::<u32>::new(); 2];
++
++    let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
++    let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
++}
++
++fn should_warn_simple_case_with_big_indentation() {
++    if true {
++        let k = 1;
++        dbg!(k);
++        if true {
++            let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
++            let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
++        }
++    }
++}
++
++fn should_warn_complex_case() {
++    let v = vec![
++        Arc::downgrade(&Arc::new(Mutex::new({
++            let x = 1;
++            dbg!(x);
++            x
++        })));
++        2
++    ];
++
++    let v1 = vec![
++        Rc::downgrade(&Rc::new(Mutex::new({
++            let x = 1;
++            dbg!(x);
++            x
++        })));
++        2
++    ];
++}
++
++fn should_not_warn_custom_weak() {
++    #[derive(Clone)]
++    struct Weak;
++
++    impl Weak {
++        fn new() -> Self {
++            Weak
++        }
++    }
++
++    let v = vec![Weak::new(); 2];
++}
++
++fn should_not_warn_vec_from_elem_but_not_weak() {
++    let v = vec![String::new(); 2];
++    let v1 = vec![1; 2];
++    let v2 = vec![
++        Box::new(Arc::downgrade(&Arc::new({
++            let y = 3;
++            dbg!(y);
++            y
++        })));
++        2
++    ];
++    let v3 = vec![
++        Box::new(Rc::downgrade(&Rc::new({
++            let y = 3;
++            dbg!(y);
++            y
++        })));
++        2
++    ];
++}
++
++fn should_not_warn_vec_macro_but_not_from_elem() {
++    let v = vec![Arc::downgrade(&Arc::new("x".to_string()))];
++    let v = vec![Rc::downgrade(&Rc::new("x".to_string()))];
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a21946ccdfa8dd74f25b2889055ae66c4a7366a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,201 @@@
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:8:13
++   |
++LL |     let v = vec![SyncWeak::<u32>::new(); 2];
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::rc-clone-in-vec-init` implied by `-D warnings`
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~     let v = {
++LL +         let mut v = Vec::with_capacity(2);
++LL +         (0..2).for_each(|_| v.push(SyncWeak::<u32>::new(..)));
++LL +         v
++LL ~     };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~     let v = {
++LL +         let data = SyncWeak::<u32>::new(..);
++LL +         vec![data; 2]
++LL ~     };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:9:14
++   |
++LL |     let v2 = vec![UnSyncWeak::<u32>::new(); 2];
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~     let v2 = {
++LL +         let mut v = Vec::with_capacity(2);
++LL +         (0..2).for_each(|_| v.push(UnSyncWeak::<u32>::new(..)));
++LL +         v
++LL ~     };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~     let v2 = {
++LL +         let data = UnSyncWeak::<u32>::new(..);
++LL +         vec![data; 2]
++LL ~     };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:11:13
++   |
++LL |     let v = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~     let v = {
++LL +         let mut v = Vec::with_capacity(2);
++LL +         (0..2).for_each(|_| v.push(Rc::downgrade(..)));
++LL +         v
++LL ~     };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~     let v = {
++LL +         let data = Rc::downgrade(..);
++LL +         vec![data; 2]
++LL ~     };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:12:13
++   |
++LL |     let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~     let v = {
++LL +         let mut v = Vec::with_capacity(2);
++LL +         (0..2).for_each(|_| v.push(Arc::downgrade(..)));
++LL +         v
++LL ~     };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~     let v = {
++LL +         let data = Arc::downgrade(..);
++LL +         vec![data; 2]
++LL ~     };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:20:21
++   |
++LL |             let v = vec![Arc::downgrade(&Arc::new("x".to_string())); 2];
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~             let v = {
++LL +                 let mut v = Vec::with_capacity(2);
++LL +                 (0..2).for_each(|_| v.push(Arc::downgrade(..)));
++LL +                 v
++LL ~             };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~             let v = {
++LL +                 let data = Arc::downgrade(..);
++LL +                 vec![data; 2]
++LL ~             };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:21:22
++   |
++LL |             let v2 = vec![Rc::downgrade(&Rc::new("x".to_string())); 2];
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~             let v2 = {
++LL +                 let mut v = Vec::with_capacity(2);
++LL +                 (0..2).for_each(|_| v.push(Rc::downgrade(..)));
++LL +                 v
++LL ~             };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~             let v2 = {
++LL +                 let data = Rc::downgrade(..);
++LL +                 vec![data; 2]
++LL ~             };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:27:13
++   |
++LL |       let v = vec![
++   |  _____________^
++LL | |         Arc::downgrade(&Arc::new(Mutex::new({
++LL | |             let x = 1;
++LL | |             dbg!(x);
++...  |
++LL | |         2
++LL | |     ];
++   | |_____^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~     let v = {
++LL +         let mut v = Vec::with_capacity(2);
++LL +         (0..2).for_each(|_| v.push(Arc::downgrade(..)));
++LL +         v
++LL ~     };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~     let v = {
++LL +         let data = Arc::downgrade(..);
++LL +         vec![data; 2]
++LL ~     };
++   |
++
++error: initializing a reference-counted pointer in `vec![elem; len]`
++  --> $DIR/weak.rs:36:14
++   |
++LL |       let v1 = vec![
++   |  ______________^
++LL | |         Rc::downgrade(&Rc::new(Mutex::new({
++LL | |             let x = 1;
++LL | |             dbg!(x);
++...  |
++LL | |         2
++LL | |     ];
++   | |_____^
++   |
++   = note: each element will point to the same `Weak` instance
++help: consider initializing each `Weak` element individually
++   |
++LL ~     let v1 = {
++LL +         let mut v = Vec::with_capacity(2);
++LL +         (0..2).for_each(|_| v.push(Rc::downgrade(..)));
++LL +         v
++LL ~     };
++   |
++help: or if this is intentional, consider extracting the `Weak` initialization to a variable
++   |
++LL ~     let v1 = {
++LL +         let data = Rc::downgrade(..);
++LL +         vec![data; 2]
++LL ~     };
++   |
++
++error: aborting due to 8 previous errors
++
index f72fc77ab9977dda9e161eb1714b2f912647ca88,0000000000000000000000000000000000000000..cb6ba36b14c809df2179f8f74f5d957de36c5dfe
mode 100644,000000..100644
--- /dev/null
@@@ -1,321 -1,0 +1,322 @@@
-     clippy::deref_addrof
 +#![warn(clippy::recursive_format_impl)]
 +#![allow(
 +    clippy::inherent_to_string_shadow_display,
 +    clippy::to_string_in_format_args,
++    clippy::deref_addrof,
++    clippy::borrow_deref_ref
 +)]
 +
 +use std::fmt;
 +
 +struct A;
 +impl A {
 +    fn fmt(&self) {
 +        self.to_string();
 +    }
 +}
 +
 +trait B {
 +    fn fmt(&self) {}
 +}
 +
 +impl B for A {
 +    fn fmt(&self) {
 +        self.to_string();
 +    }
 +}
 +
 +impl fmt::Display for A {
 +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +        write!(f, "{}", self.to_string())
 +    }
 +}
 +
 +fn fmt(a: A) {
 +    a.to_string();
 +}
 +
 +struct C;
 +
 +impl C {
 +    // Doesn't trigger if to_string defined separately
 +    // i.e. not using ToString trait (from Display)
 +    fn to_string(&self) -> String {
 +        String::from("I am C")
 +    }
 +}
 +
 +impl fmt::Display for C {
 +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +        write!(f, "{}", self.to_string())
 +    }
 +}
 +
 +enum D {
 +    E(String),
 +    F,
 +}
 +
 +impl std::fmt::Display for D {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        match &self {
 +            Self::E(string) => write!(f, "E {}", string.to_string()),
 +            Self::F => write!(f, "F"),
 +        }
 +    }
 +}
 +
 +// Check for use of self as Display, in Display impl
 +// Triggers on direct use of self
 +struct G;
 +
 +impl std::fmt::Display for G {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}", self)
 +    }
 +}
 +
 +// Triggers on reference to self
 +struct H;
 +
 +impl std::fmt::Display for H {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}", &self)
 +    }
 +}
 +
 +impl std::fmt::Debug for H {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{:?}", &self)
 +    }
 +}
 +
 +// Triggers on multiple reference to self
 +struct H2;
 +
 +impl std::fmt::Display for H2 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(f, "{}", &&&self)
 +    }
 +}
 +
 +// Doesn't trigger on correct deref
 +struct I;
 +
 +impl std::ops::Deref for I {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for I {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", &**self)
 +    }
 +}
 +
 +impl std::fmt::Debug for I {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{:?}", &**self)
 +    }
 +}
 +
 +// Doesn't trigger on multiple correct deref
 +struct I2;
 +
 +impl std::ops::Deref for I2 {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for I2 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", **&&&**self)
 +    }
 +}
 +
 +// Doesn't trigger on multiple correct deref
 +struct I3;
 +
 +impl std::ops::Deref for I3 {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for I3 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", &&**&&&**self)
 +    }
 +}
 +
 +// Does trigger when deref resolves to self
 +struct J;
 +
 +impl std::ops::Deref for J {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for J {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", &*self)
 +    }
 +}
 +
 +impl std::fmt::Debug for J {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{:?}", &*self)
 +    }
 +}
 +
 +struct J2;
 +
 +impl std::ops::Deref for J2 {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for J2 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", *self)
 +    }
 +}
 +
 +struct J3;
 +
 +impl std::ops::Deref for J3 {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for J3 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", **&&*self)
 +    }
 +}
 +
 +struct J4;
 +
 +impl std::ops::Deref for J4 {
 +    type Target = str;
 +
 +    fn deref(&self) -> &Self::Target {
 +        "test"
 +    }
 +}
 +
 +impl std::fmt::Display for J4 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", &&**&&*self)
 +    }
 +}
 +
 +// Doesn't trigger on Debug from Display
 +struct K;
 +
 +impl std::fmt::Debug for K {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "test")
 +    }
 +}
 +
 +impl std::fmt::Display for K {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{:?}", self)
 +    }
 +}
 +
 +// Doesn't trigger on Display from Debug
 +struct K2;
 +
 +impl std::fmt::Debug for K2 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{}", self)
 +    }
 +}
 +
 +impl std::fmt::Display for K2 {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "test")
 +    }
 +}
 +
 +// Doesn't trigger on struct fields
 +struct L {
 +    field1: u32,
 +    field2: i32,
 +}
 +
 +impl std::fmt::Display for L {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{},{}", self.field1, self.field2)
 +    }
 +}
 +
 +impl std::fmt::Debug for L {
 +    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 +        write!(f, "{:?},{:?}", self.field1, self.field2)
 +    }
 +}
 +
 +// Doesn't trigger on nested enum matching
 +enum Tree {
 +    Leaf,
 +    Node(Vec<Tree>),
 +}
 +
 +impl std::fmt::Display for Tree {
 +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +        match self {
 +            Tree::Leaf => write!(f, "*"),
 +            Tree::Node(children) => {
 +                write!(f, "(")?;
 +                for child in children.iter() {
 +                    write!(f, "{},", child)?;
 +                }
 +                write!(f, ")")
 +            },
 +        }
 +    }
 +}
 +
 +impl std::fmt::Debug for Tree {
 +    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +        match self {
 +            Tree::Leaf => write!(f, "*"),
 +            Tree::Node(children) => {
 +                write!(f, "(")?;
 +                for child in children.iter() {
 +                    write!(f, "{:?},", child)?;
 +                }
 +                write!(f, ")")
 +            },
 +        }
 +    }
 +}
 +
 +fn main() {
 +    let a = A;
 +    a.to_string();
 +    a.fmt();
 +    fmt(a);
 +
 +    let c = C;
 +    c.to_string();
 +}
index 1a717ac92d8b7dac1dc6e256b39154ff42cec043,0000000000000000000000000000000000000000..84ce69df566968125334727af37af854056bf43e
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,82 @@@
-   --> $DIR/recursive_format_impl.rs:29:25
 +error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:73:9
++  --> $DIR/recursive_format_impl.rs:30:25
 +   |
 +LL |         write!(f, "{}", self.to_string())
 +   |                         ^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::recursive-format-impl` implied by `-D warnings`
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:82:9
++  --> $DIR/recursive_format_impl.rs:74:9
 +   |
 +LL |         write!(f, "{}", self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:88:9
++  --> $DIR/recursive_format_impl.rs:83:9
 +   |
 +LL |         write!(f, "{}", &self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Debug` in `impl Debug` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:97:9
++  --> $DIR/recursive_format_impl.rs:89:9
 +   |
 +LL |         write!(f, "{:?}", &self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:171:9
++  --> $DIR/recursive_format_impl.rs:98:9
 +   |
 +LL |         write!(f, "{}", &&&self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:177:9
++  --> $DIR/recursive_format_impl.rs:172:9
 +   |
 +LL |         write!(f, "{}", &*self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Debug` in `impl Debug` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:193:9
++  --> $DIR/recursive_format_impl.rs:178:9
 +   |
 +LL |         write!(f, "{:?}", &*self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:209:9
++  --> $DIR/recursive_format_impl.rs:194:9
 +   |
 +LL |         write!(f, "{}", *self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
-   --> $DIR/recursive_format_impl.rs:225:9
++  --> $DIR/recursive_format_impl.rs:210:9
 +   |
 +LL |         write!(f, "{}", **&&*self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: using `self` as `Display` in `impl Display` will cause infinite recursion
++  --> $DIR/recursive_format_impl.rs:226:9
 +   |
 +LL |         write!(f, "{}", &&**&&*self)
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: this error originates in the macro `write` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: aborting due to 10 previous errors
 +
index a394ef8f25c675b529db3c275eb97308fe835b5b,0000000000000000000000000000000000000000..1fa9fc749a96a1fb1be91ef95e5fbddda482ae30
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,98 @@@
 +#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
 +#![allow(clippy::let_unit_value)]
 +
 +fn shadow_same() {
 +    let x = 1;
 +    let x = x;
 +    let mut x = &x;
 +    let x = &mut x;
 +    let x = *x;
 +}
 +
 +fn shadow_reuse() -> Option<()> {
 +    let x = ([[0]], ());
 +    let x = x.0;
 +    let x = x[0];
 +    let [x] = x;
 +    let x = Some(x);
 +    let x = foo(x);
 +    let x = || x;
 +    let x = Some(1).map(|_| x)?;
 +    let y = 1;
 +    let y = match y {
 +        1 => 2,
 +        _ => 3,
 +    };
 +    None
 +}
 +
 +fn shadow_unrelated() {
 +    let x = 1;
 +    let x = 2;
 +}
 +
 +fn syntax() {
 +    fn f(x: u32) {
 +        let x = 1;
 +    }
 +    let x = 1;
 +    match Some(1) {
 +        Some(1) => {},
 +        Some(x) => {
 +            let x = 1;
 +        },
 +        _ => {},
 +    }
 +    if let Some(x) = Some(1) {}
 +    while let Some(x) = Some(1) {}
 +    let _ = |[x]: [u32; 1]| {
 +        let x = 1;
 +    };
 +    let y = Some(1);
 +    if let Some(y) = y {}
 +}
 +
 +fn negative() {
 +    match Some(1) {
 +        Some(x) if x == 1 => {},
 +        Some(x) => {},
 +        None => {},
 +    }
 +    match [None, Some(1)] {
 +        [Some(x), None] | [None, Some(x)] => {},
 +        _ => {},
 +    }
 +    if let Some(x) = Some(1) {
 +        let y = 1;
 +    } else {
 +        let x = 1;
 +        let y = 1;
 +    }
 +    let x = 1;
 +    #[allow(clippy::shadow_unrelated)]
 +    let x = 1;
 +}
 +
 +fn foo<T>(_: T) {}
 +
 +fn question_mark() -> Option<()> {
 +    let val = 1;
 +    // `?` expands with a `val` binding
 +    None?;
 +    None
 +}
 +
 +pub async fn foo1(_a: i32) {}
 +
 +pub async fn foo2(_a: i32, _b: i64) {
 +    let _b = _a;
 +}
 +
++fn ice_8748() {
++    let _ = [0; {
++        let x = 1;
++        if let Some(x) = Some(1) { x } else { 1 }
++    }];
++}
++
 +fn main() {}
index f83a6dd0eb288bab30a619f79f40770bb5942a3e,0000000000000000000000000000000000000000..4347610f393f38678303b3f9684708369fc559ab
mode 100644,000000..100644
--- /dev/null
@@@ -1,555 -1,0 +1,594 @@@
 +// FIXME: Ideally these suggestions would be fixed via rustfix. Blocked by rust-lang/rust#53934
 +// // run-rustfix
 +
 +#![warn(clippy::significant_drop_in_scrutinee)]
 +#![allow(clippy::single_match)]
 +#![allow(clippy::match_single_binding)]
 +#![allow(unused_assignments)]
 +#![allow(dead_code)]
 +
++use std::num::ParseIntError;
 +use std::ops::Deref;
 +use std::sync::atomic::{AtomicU64, Ordering};
++use std::sync::RwLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +struct State {}
 +
 +impl State {
 +    fn foo(&self) -> bool {
 +        true
 +    }
 +
 +    fn bar(&self) {}
 +}
 +
 +fn should_not_trigger_lint_with_mutex_guard_outside_match() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should not trigger lint because the temporary should drop at the `;` on line before the match
 +    let is_foo = mutex.lock().unwrap().foo();
 +    match is_foo {
 +        true => {
 +            mutex.lock().unwrap().bar();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_with_mutex_guard_when_taking_ownership_in_match() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should not trigger lint because the scrutinee is explicitly returning the MutexGuard,
 +    // so its lifetime should not be surprising.
 +    match mutex.lock() {
 +        Ok(guard) => {
 +            guard.foo();
 +            mutex.lock().unwrap().bar();
 +        },
 +        _ => {},
 +    };
 +}
 +
 +fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
 +    let mutex = Mutex::new(State {});
 +
 +    // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
 +    // is preserved until the end of the match, but there is no clear indication that this is the
 +    // case.
 +    match mutex.lock().unwrap().foo() {
 +        true => {
 +            mutex.lock().unwrap().bar();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_for_insignificant_drop() {
 +    // Should not trigger lint because there are no temporaries whose drops have a significant
 +    // side effect.
 +    match 1u64.to_string().is_empty() {
 +        true => {
 +            println!("It was empty")
 +        },
 +        false => {
 +            println!("It was not empty")
 +        },
 +    }
 +}
 +
 +struct StateWithMutex {
 +    m: Mutex<u64>,
 +}
 +
 +struct MutexGuardWrapper<'a> {
 +    mg: MutexGuard<'a, u64>,
 +}
 +
 +impl<'a> MutexGuardWrapper<'a> {
 +    fn get_the_value(&self) -> u64 {
 +        *self.mg.deref()
 +    }
 +}
 +
 +struct MutexGuardWrapperWrapper<'a> {
 +    mg: MutexGuardWrapper<'a>,
 +}
 +
 +impl<'a> MutexGuardWrapperWrapper<'a> {
 +    fn get_the_value(&self) -> u64 {
 +        *self.mg.mg.deref()
 +    }
 +}
 +
 +impl StateWithMutex {
 +    fn lock_m(&self) -> MutexGuardWrapper<'_> {
 +        MutexGuardWrapper {
 +            mg: self.m.lock().unwrap(),
 +        }
 +    }
 +
 +    fn lock_m_m(&self) -> MutexGuardWrapperWrapper<'_> {
 +        MutexGuardWrapperWrapper {
 +            mg: MutexGuardWrapper {
 +                mg: self.m.lock().unwrap(),
 +            },
 +        }
 +    }
 +
 +    fn foo(&self) -> bool {
 +        true
 +    }
 +
 +    fn bar(&self) {}
 +}
 +
 +fn should_trigger_lint_with_wrapped_mutex() {
 +    let s = StateWithMutex { m: Mutex::new(1) };
 +
 +    // Should trigger lint because a temporary contains a type with a significant drop and its
 +    // lifetime is not obvious. Additionally, it is not obvious from looking at the scrutinee that
 +    // the temporary contains such a type, making it potentially even more surprising.
 +    match s.lock_m().get_the_value() {
 +        1 => {
 +            println!("Got 1. Is it still 1?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        2 => {
 +            println!("Got 2. Is it still 2?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        _ => {},
 +    }
 +    println!("All done!");
 +}
 +
 +fn should_trigger_lint_with_double_wrapped_mutex() {
 +    let s = StateWithMutex { m: Mutex::new(1) };
 +
 +    // Should trigger lint because a temporary contains a type which further contains a type with a
 +    // significant drop and its lifetime is not obvious. Additionally, it is not obvious from
 +    // looking at the scrutinee that the temporary contains such a type, making it potentially even
 +    // more surprising.
 +    match s.lock_m_m().get_the_value() {
 +        1 => {
 +            println!("Got 1. Is it still 1?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        2 => {
 +            println!("Got 2. Is it still 2?");
 +            println!("{}", s.lock_m().get_the_value());
 +        },
 +        _ => {},
 +    }
 +    println!("All done!");
 +}
 +
 +struct Counter {
 +    i: AtomicU64,
 +}
 +
 +#[clippy::has_significant_drop]
 +struct CounterWrapper<'a> {
 +    counter: &'a Counter,
 +}
 +
 +impl<'a> CounterWrapper<'a> {
 +    fn new(counter: &Counter) -> CounterWrapper {
 +        counter.i.fetch_add(1, Ordering::Relaxed);
 +        CounterWrapper { counter }
 +    }
 +}
 +
 +impl<'a> Drop for CounterWrapper<'a> {
 +    fn drop(&mut self) {
 +        self.counter.i.fetch_sub(1, Ordering::Relaxed);
 +    }
 +}
 +
 +impl Counter {
 +    fn temp_increment(&self) -> Vec<CounterWrapper> {
 +        vec![CounterWrapper::new(self), CounterWrapper::new(self)]
 +    }
 +}
 +
 +fn should_trigger_lint_for_vec() {
 +    let counter = Counter { i: AtomicU64::new(0) };
 +
 +    // Should trigger lint because the temporary in the scrutinee returns a collection of types
 +    // which have significant drops. The types with significant drops are also non-obvious when
 +    // reading the expression in the scrutinee.
 +    match counter.temp_increment().len() {
 +        2 => {
 +            let current_count = counter.i.load(Ordering::Relaxed);
 +            println!("Current count {}", current_count);
 +            assert_eq!(current_count, 0);
 +        },
 +        1 => {},
 +        3 => {},
 +        _ => {},
 +    };
 +}
 +
 +struct StateWithField {
 +    s: String,
 +}
 +
 +// Should trigger lint only on the type in the tuple which is created using a temporary
 +// with a significant drop. Additionally, this test ensures that the format of the tuple
 +// is preserved correctly in the suggestion.
 +fn should_trigger_lint_for_tuple_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +
 +    {
 +        match (mutex1.lock().unwrap().s.len(), true) {
 +            (3, _) => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _) => {},
 +        };
 +
 +        match (true, mutex1.lock().unwrap().s.len(), true) {
 +            (_, 3, _) => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _, _) => {},
 +        };
 +
 +        let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
 +        match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
 +            (3, _, 3) => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                mutex2.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _, _) => {},
 +        };
 +
 +        let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
 +        match mutex3.lock().unwrap().s.as_str() {
 +            "three" => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                mutex2.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            _ => {},
 +        };
 +
 +        match (true, mutex3.lock().unwrap().s.as_str()) {
 +            (_, "three") => {
 +                println!("started");
 +                mutex1.lock().unwrap().s.len();
 +                mutex2.lock().unwrap().s.len();
 +                println!("done");
 +            },
 +            (_, _) => {},
 +        };
 +    }
 +}
 +
 +// Should trigger lint when either side of a binary operation creates a temporary with a
 +// significant drop.
 +// To avoid potential unnecessary copies or creating references that would trigger the significant
 +// drop problem, the lint recommends moving the entire binary operation.
 +fn should_trigger_lint_for_accessing_field_in_mutex_in_one_side_of_binary_op() {
 +    let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
 +
 +    match mutex.lock().unwrap().s.len() > 1 {
 +        true => {
 +            mutex.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +
 +    match 1 < mutex.lock().unwrap().s.len() {
 +        true => {
 +            mutex.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +// Should trigger lint when both sides of a binary operation creates a temporary with a
 +// significant drop.
 +// To avoid potential unnecessary copies or creating references that would trigger the significant
 +// drop problem, the lint recommends moving the entire binary operation.
 +fn should_trigger_lint_for_accessing_fields_in_mutex_in_both_sides_of_binary_op() {
 +    let mutex1 = Mutex::new(StateWithField { s: "state".to_owned() });
 +    let mutex2 = Mutex::new(StateWithField {
 +        s: "statewithfield".to_owned(),
 +    });
 +
 +    match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
 +        true => {
 +            println!(
 +                "{} < {}",
 +                mutex1.lock().unwrap().s.len(),
 +                mutex2.lock().unwrap().s.len()
 +            );
 +        },
 +        false => {},
 +    };
 +
 +    match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
 +        true => {
 +            println!(
 +                "{} >= {}",
 +                mutex1.lock().unwrap().s.len(),
 +                mutex2.lock().unwrap().s.len()
 +            );
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_for_closure_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +
 +    let get_mutex_guard = || mutex1.lock().unwrap().s.len();
 +
 +    // Should not trigger lint because the temporary with a significant drop will be dropped
 +    // at the end of the closure, so the MutexGuard will be unlocked and not have a potentially
 +    // surprising lifetime.
 +    match get_mutex_guard() > 1 {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_trigger_lint_for_return_from_closure_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +
 +    let get_mutex_guard = || mutex1.lock().unwrap();
 +
 +    // Should trigger lint because the temporary with a significant drop is returned from the
 +    // closure but not used directly in any match arms, so it has a potentially surprising lifetime.
 +    match get_mutex_guard().s.len() > 1 {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_trigger_lint_for_return_from_match_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +    let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
 +
 +    let i = 100;
 +
 +    // Should trigger lint because the nested match within the scrutinee returns a temporary with a
 +    // significant drop is but not used directly in any match arms, so it has a potentially
 +    // surprising lifetime.
 +    match match i {
 +        100 => mutex1.lock().unwrap(),
 +        _ => mutex2.lock().unwrap(),
 +    }
 +    .s
 +    .len()
 +        > 1
 +    {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {
 +            println!("nothing to do here");
 +        },
 +    };
 +}
 +
 +fn should_trigger_lint_for_return_from_if_in_scrutinee() {
 +    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 +    let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
 +
 +    let i = 100;
 +
 +    // Should trigger lint because the nested if-expression within the scrutinee returns a temporary
 +    // with a significant drop is but not used directly in any match arms, so it has a potentially
 +    // surprising lifetime.
 +    match if i > 1 {
 +        mutex1.lock().unwrap()
 +    } else {
 +        mutex2.lock().unwrap()
 +    }
 +    .s
 +    .len()
 +        > 1
 +    {
 +        true => {
 +            mutex1.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +fn should_not_trigger_lint_for_if_in_scrutinee() {
 +    let mutex = Mutex::new(StateWithField { s: "state".to_owned() });
 +
 +    let i = 100;
 +
 +    // Should not trigger the lint because the temporary with a significant drop *is* dropped within
 +    // the body of the if-expression nested within the match scrutinee, and therefore does not have
 +    // a potentially surprising lifetime.
 +    match if i > 1 {
 +        mutex.lock().unwrap().s.len() > 1
 +    } else {
 +        false
 +    } {
 +        true => {
 +            mutex.lock().unwrap().s.len();
 +        },
 +        false => {},
 +    };
 +}
 +
 +struct StateWithBoxedMutexGuard {
 +    u: Mutex<u64>,
 +}
 +
 +impl StateWithBoxedMutexGuard {
 +    fn new() -> StateWithBoxedMutexGuard {
 +        StateWithBoxedMutexGuard { u: Mutex::new(42) }
 +    }
 +    fn lock(&self) -> Box<MutexGuard<u64>> {
 +        Box::new(self.u.lock().unwrap())
 +    }
 +}
 +
 +fn should_trigger_lint_for_boxed_mutex_guard() {
 +    let s = StateWithBoxedMutexGuard::new();
 +
 +    // Should trigger lint because a temporary Box holding a type with a significant drop in a match
 +    // scrutinee may have a potentially surprising lifetime.
 +    match s.lock().deref().deref() {
 +        0 | 1 => println!("Value was less than 2"),
 +        _ => println!("Value is {}", s.lock().deref()),
 +    };
 +}
 +
 +struct StateStringWithBoxedMutexGuard {
 +    s: Mutex<String>,
 +}
 +
 +impl StateStringWithBoxedMutexGuard {
 +    fn new() -> StateStringWithBoxedMutexGuard {
 +        StateStringWithBoxedMutexGuard {
 +            s: Mutex::new("A String".to_owned()),
 +        }
 +    }
 +    fn lock(&self) -> Box<MutexGuard<String>> {
 +        Box::new(self.s.lock().unwrap())
 +    }
 +}
 +
 +fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
 +    let s = StateStringWithBoxedMutexGuard::new();
 +
 +    let matcher = String::from("A String");
 +
 +    // Should trigger lint because a temporary Box holding a type with a significant drop in a match
 +    // scrutinee may have a potentially surprising lifetime.
 +    match s.lock().deref().deref() {
 +        matcher => println!("Value is {}", s.lock().deref()),
 +        _ => println!("Value was not a match"),
 +    };
 +}
 +
 +struct StateWithIntField {
 +    i: u64,
 +}
 +
 +// Should trigger lint when either side of an assign expression contains a temporary with a
 +// significant drop, because the temporary's lifetime will be extended to the end of the match.
 +// To avoid potential unnecessary copies or creating references that would trigger the significant
 +// drop problem, the lint recommends moving the entire binary operation.
 +fn should_trigger_lint_in_assign_expr() {
 +    let mutex = Mutex::new(StateWithIntField { i: 10 });
 +
 +    let mut i = 100;
 +
 +    match mutex.lock().unwrap().i = i {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +
 +    match i = mutex.lock().unwrap().i {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +
 +    match mutex.lock().unwrap().i += 1 {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +
 +    match i += mutex.lock().unwrap().i {
 +        _ => {
 +            println!("{}", mutex.lock().unwrap().i);
 +        },
 +    };
 +}
 +
 +#[derive(Debug)]
 +enum RecursiveEnum {
 +    Foo(Option<Box<RecursiveEnum>>),
 +}
 +
 +#[derive(Debug)]
 +enum GenericRecursiveEnum<T> {
 +    Foo(T, Option<Box<GenericRecursiveEnum<T>>>),
 +}
 +
 +fn should_not_cause_stack_overflow() {
 +    // Test that when a type recursively contains itself, a stack overflow does not occur when
 +    // checking sub-types for significant drops.
 +    let f = RecursiveEnum::Foo(Some(Box::new(RecursiveEnum::Foo(None))));
 +    match f {
 +        RecursiveEnum::Foo(Some(f)) => {
 +            println!("{:?}", f)
 +        },
 +        RecursiveEnum::Foo(f) => {
 +            println!("{:?}", f)
 +        },
 +    }
 +
 +    let f = GenericRecursiveEnum::Foo(1u64, Some(Box::new(GenericRecursiveEnum::Foo(2u64, None))));
 +    match f {
 +        GenericRecursiveEnum::Foo(i, Some(f)) => {
 +            println!("{} {:?}", i, f)
 +        },
 +        GenericRecursiveEnum::Foo(i, f) => {
 +            println!("{} {:?}", i, f)
 +        },
 +    }
 +}
 +
++fn should_not_produce_lint_for_try_desugar() -> Result<u64, ParseIntError> {
++    // TryDesugar (i.e. using `?` for a Result type) will turn into a match but is out of scope
++    // for this lint
++    let rwlock = RwLock::new("1".to_string());
++    let result = rwlock.read().unwrap().parse::<u64>()?;
++    println!("{}", result);
++    rwlock.write().unwrap().push('2');
++    Ok(result)
++}
++
++struct ResultReturner {
++    s: String,
++}
++
++impl ResultReturner {
++    fn to_number(&self) -> Result<i64, ParseIntError> {
++        self.s.parse::<i64>()
++    }
++}
++
++fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
++    let rwlock = RwLock::<ResultReturner>::new(ResultReturner { s: "1".to_string() });
++    match rwlock.read().unwrap().to_number() {
++        Ok(n) => println!("Converted to number: {}", n),
++        Err(e) => println!("Could not convert {} to number", e),
++    };
++}
++
++fn should_trigger_lint_for_read_write_lock_for_loop() {
++    // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
++    // designed to look for.
++    let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
++    for s in rwlock.read().unwrap().iter() {
++        println!("{}", s);
++    }
++}
++
 +fn main() {}
index af160564985051bdc8008dce745abecd7d99d497,0000000000000000000000000000000000000000..303f3c1df033cfcd1100747da442bccd82ef5ab3
mode 100644,000000..100644
--- /dev/null
@@@ -1,283 -1,0 +1,295 @@@
-   --> $DIR/significant_drop_in_scrutinee.rs:57:11
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:130:11
++  --> $DIR/significant_drop_in_scrutinee.rs:59:11
 +   |
 +LL |     match mutex.lock().unwrap().foo() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings`
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = mutex.lock().unwrap().foo();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:151:11
++  --> $DIR/significant_drop_in_scrutinee.rs:132:11
 +   |
 +LL |     match s.lock_m().get_the_value() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = s.lock_m().get_the_value();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:199:11
++  --> $DIR/significant_drop_in_scrutinee.rs:153:11
 +   |
 +LL |     match s.lock_m_m().get_the_value() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = s.lock_m_m().get_the_value();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:222:16
++  --> $DIR/significant_drop_in_scrutinee.rs:201:11
 +   |
 +LL |     match counter.temp_increment().len() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = counter.temp_increment().len();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:231:22
++  --> $DIR/significant_drop_in_scrutinee.rs:224:16
 +   |
 +LL |         match (mutex1.lock().unwrap().s.len(), true) {
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~         let value = mutex1.lock().unwrap().s.len();
 +LL ~         match (value, true) {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:241:16
++  --> $DIR/significant_drop_in_scrutinee.rs:233:22
 +   |
 +LL |         match (true, mutex1.lock().unwrap().s.len(), true) {
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~         let value = mutex1.lock().unwrap().s.len();
 +LL ~         match (true, value, true) {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:241:54
++  --> $DIR/significant_drop_in_scrutinee.rs:243:16
 +   |
 +LL |         match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~         let value = mutex1.lock().unwrap().s.len();
 +LL ~         match (value, true, mutex2.lock().unwrap().s.len()) {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:252:15
++  --> $DIR/significant_drop_in_scrutinee.rs:243:54
 +   |
 +LL |         match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
 +   |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~         let value = mutex2.lock().unwrap().s.len();
 +LL ~         match (mutex1.lock().unwrap().s.len(), true, value) {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:262:22
++  --> $DIR/significant_drop_in_scrutinee.rs:254:15
 +   |
 +LL |         match mutex3.lock().unwrap().s.as_str() {
 +   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:281:11
++  --> $DIR/significant_drop_in_scrutinee.rs:264:22
 +   |
 +LL |         match (true, mutex3.lock().unwrap().s.as_str()) {
 +   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:288:11
++  --> $DIR/significant_drop_in_scrutinee.rs:283:11
 +   |
 +LL |     match mutex.lock().unwrap().s.len() > 1 {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = mutex.lock().unwrap().s.len() > 1;
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:306:11
++  --> $DIR/significant_drop_in_scrutinee.rs:290:11
 +   |
 +LL |     match 1 < mutex.lock().unwrap().s.len() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = 1 < mutex.lock().unwrap().s.len();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:317:11
++  --> $DIR/significant_drop_in_scrutinee.rs:308:11
 +   |
 +LL |     match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:352:11
++  --> $DIR/significant_drop_in_scrutinee.rs:319:11
 +   |
 +LL |     match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:369:11
++  --> $DIR/significant_drop_in_scrutinee.rs:354:11
 +   |
 +LL |     match get_mutex_guard().s.len() > 1 {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = get_mutex_guard().s.len() > 1;
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:395:11
++  --> $DIR/significant_drop_in_scrutinee.rs:371:11
 +   |
 +LL |       match match i {
 +   |  ___________^
 +LL | |         100 => mutex1.lock().unwrap(),
 +LL | |         _ => mutex2.lock().unwrap(),
 +LL | |     }
 +LL | |     .s
 +LL | |     .len()
 +LL | |         > 1
 +   | |___________^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = match i {
 +LL +         100 => mutex1.lock().unwrap(),
 +LL +         _ => mutex2.lock().unwrap(),
 +LL +     }
 +LL +     .s
 +LL +     .len()
 + ...
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:449:11
++  --> $DIR/significant_drop_in_scrutinee.rs:397:11
 +   |
 +LL |       match if i > 1 {
 +   |  ___________^
 +LL | |         mutex1.lock().unwrap()
 +LL | |     } else {
 +LL | |         mutex2.lock().unwrap()
 +...  |
 +LL | |     .len()
 +LL | |         > 1
 +   | |___________^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     let value = if i > 1 {
 +LL +         mutex1.lock().unwrap()
 +LL +     } else {
 +LL +         mutex2.lock().unwrap()
 +LL +     }
 +LL +     .s
 + ...
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:477:11
++  --> $DIR/significant_drop_in_scrutinee.rs:451:11
 +   |
 +LL |     match s.lock().deref().deref() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match and create a copy
 +   |
 +LL ~     let value = *s.lock().deref().deref();
 +LL ~     match value {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:496:11
++  --> $DIR/significant_drop_in_scrutinee.rs:479:11
 +   |
 +LL |     match s.lock().deref().deref() {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:502:11
++  --> $DIR/significant_drop_in_scrutinee.rs:498:11
 +   |
 +LL |     match mutex.lock().unwrap().i = i {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     mutex.lock().unwrap().i = i;
 +LL ~     match () {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:508:11
++  --> $DIR/significant_drop_in_scrutinee.rs:504:11
 +   |
 +LL |     match i = mutex.lock().unwrap().i {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     i = mutex.lock().unwrap().i;
 +LL ~     match () {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
-   --> $DIR/significant_drop_in_scrutinee.rs:514:11
++  --> $DIR/significant_drop_in_scrutinee.rs:510:11
 +   |
 +LL |     match mutex.lock().unwrap().i += 1 {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     mutex.lock().unwrap().i += 1;
 +LL ~     match () {
 +   |
 +
 +error: temporary with significant drop in match scrutinee
- error: aborting due to 23 previous errors
++  --> $DIR/significant_drop_in_scrutinee.rs:516:11
 +   |
 +LL |     match i += mutex.lock().unwrap().i {
 +   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: try moving the temporary above the match
 +   |
 +LL ~     i += mutex.lock().unwrap().i;
 +LL ~     match () {
 +   |
 +
++error: temporary with significant drop in match scrutinee
++  --> $DIR/significant_drop_in_scrutinee.rs:579:11
++   |
++LL |     match rwlock.read().unwrap().to_number() {
++   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: temporary with significant drop in for loop
++  --> $DIR/significant_drop_in_scrutinee.rs:589:14
++   |
++LL |     for s in rwlock.read().unwrap().iter() {
++   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 25 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ede8a39fed718f9143223ebe1c1f89aa5363fbde
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,209 @@@
++// run-rustfix
++#![warn(clippy::suspicious_operation_groupings)]
++#![allow(dead_code, unused_parens, clippy::eq_op)]
++
++struct Vec3 {
++    x: f64,
++    y: f64,
++    z: f64,
++}
++
++impl Eq for Vec3 {}
++
++impl PartialEq for Vec3 {
++    fn eq(&self, other: &Self) -> bool {
++        // This should trigger the lint because `self.x` is compared to `other.y`
++        self.x == other.x && self.y == other.y && self.z == other.z
++    }
++}
++
++struct S {
++    a: i32,
++    b: i32,
++    c: i32,
++    d: i32,
++}
++
++fn buggy_ab_cmp(s1: &S, s2: &S) -> bool {
++    // There's no `s1.b`
++    s1.a < s2.a && s1.b < s2.b
++}
++
++struct SaOnly {
++    a: i32,
++}
++
++impl S {
++    fn a(&self) -> i32 {
++        0
++    }
++}
++
++fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool {
++    // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
++    // `s2.b` since that is invalid.
++    s1.a < s2.a && s1.a() < s1.b
++}
++
++fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool {
++    macro_rules! s1 {
++        () => {
++            S {
++                a: 1,
++                b: 1,
++                c: 1,
++                d: 1,
++            }
++        };
++    }
++
++    // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
++    // `s2.b` since that is invalid.
++    s1.a < s2.a && s1!().a < s1.b
++}
++
++fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool {
++    // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid
++    s1.a < s2.a && s1.b < s1.b
++}
++
++fn permissable(s1: &S, s2: &S) -> bool {
++    // Something like this seems like it might actually be what is desired.
++    s1.a == s2.b
++}
++
++fn non_boolean_operators(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d
++}
++
++fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.b`
++    s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
++}
++
++fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 {
++    // There's no `s1.b`
++    s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
++}
++
++fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.b`
++    s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
++}
++
++fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.a`
++    s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
++}
++
++fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    s1.a * s2.a + s1.b * s2.b + s1.c * s2.c
++}
++
++fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) {
++    (
++        s1.b * s2.c - s1.c * s2.b,
++        s1.c * s2.a - s1.a * s2.c,
++        s1.a * s2.b - s1.b * s2.a,
++    )
++}
++
++fn outer_parens_simple(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.b`
++    (s1.a * s2.a + s1.b * s2.b)
++}
++
++fn outer_parens(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    (s1.a * s2.a + s1.b * s2.b + s1.c * s2.c + s1.d * s2.d)
++}
++
++fn inner_parens(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)
++}
++
++fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d))
++}
++
++fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.c) + (s1.d * s2.d)))
++}
++
++fn all_parens_left_tree(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.c)) + (s1.d * s2.d))
++}
++
++fn all_parens_right_tree(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.c) + (s1.d * s2.d)))
++}
++
++fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 {
++    // There's no `s1.b`
++    (s1.a * s2.a + s1.b * s2.b) / 2
++}
++
++fn inside_function_call(s1: &S, s2: &S) -> i32 {
++    // There's no `s1.b`
++    i32::swap_bytes(s1.a * s2.a + s1.b * s2.b)
++}
++
++fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool {
++    // There's no `s1.c`
++    s1.a > 0 && s1.b > 0 && s1.c == s2.c && s1.d == s2.d
++}
++
++fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool {
++    // There's no `s1.c`
++    s1.a > 0 && s1.c == s2.c && s1.b > 0 && s1.d == s2.d
++}
++
++struct Nested {
++    inner: ((i32,), (i32,), (i32,)),
++}
++
++fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool {
++    // There's no `n2.inner.2.0`
++    (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.2).0
++}
++
++// `eq_op` should catch this one.
++fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool {
++    // There's no `n2.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
++}
++
++fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool {
++    if strict {
++        s1.a < s2.a && s1.b < s2.b
++    } else {
++        // There's no `s1.b` in this subexpression
++        s1.a <= s2.a && s1.b <= s2.b
++    }
++}
++
++fn inside_an_if_statement(s1: &mut S, s2: &S) {
++    // There's no `s1.b`
++    if s1.a < s2.a && s1.b < s2.b {
++        s1.c = s2.c;
++    }
++}
++
++fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 {
++    // There's no `s2.c`
++    -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.c) + -(-s1.d * -s2.d)))
++}
++
++fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 {
++    // There's no `s1.b`
++    -(if -s1.a < -s2.a && -s1.b < -s2.b { s1.c } else { s2.a })
++}
++
++fn main() {}
index 3201d5de0f35620663dea65c8903b481d8ac9eb6,0000000000000000000000000000000000000000..26ce97bb37f84b5bf07ed1dfd52721e83a92517e
mode 100644,000000..100644
--- /dev/null
@@@ -1,208 -1,0 +1,209 @@@
- #![allow(clippy::eq_op)]
++// run-rustfix
 +#![warn(clippy::suspicious_operation_groupings)]
++#![allow(dead_code, unused_parens, clippy::eq_op)]
 +
 +struct Vec3 {
 +    x: f64,
 +    y: f64,
 +    z: f64,
 +}
 +
 +impl Eq for Vec3 {}
 +
 +impl PartialEq for Vec3 {
 +    fn eq(&self, other: &Self) -> bool {
 +        // This should trigger the lint because `self.x` is compared to `other.y`
 +        self.x == other.y && self.y == other.y && self.z == other.z
 +    }
 +}
 +
 +struct S {
 +    a: i32,
 +    b: i32,
 +    c: i32,
 +    d: i32,
 +}
 +
 +fn buggy_ab_cmp(s1: &S, s2: &S) -> bool {
 +    // There's no `s1.b`
 +    s1.a < s2.a && s1.a < s2.b
 +}
 +
 +struct SaOnly {
 +    a: i32,
 +}
 +
 +impl S {
 +    fn a(&self) -> i32 {
 +        0
 +    }
 +}
 +
 +fn do_not_give_bad_suggestions_for_this_unusual_expr(s1: &S, s2: &SaOnly) -> bool {
 +    // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
 +    // `s2.b` since that is invalid.
 +    s1.a < s2.a && s1.a() < s1.b
 +}
 +
 +fn do_not_give_bad_suggestions_for_this_macro_expr(s1: &S, s2: &SaOnly) -> bool {
 +    macro_rules! s1 {
 +        () => {
 +            S {
 +                a: 1,
 +                b: 1,
 +                c: 1,
 +                d: 1,
 +            }
 +        };
 +    }
 +
 +    // This is superficially similar to `buggy_ab_cmp`, but we should not suggest
 +    // `s2.b` since that is invalid.
 +    s1.a < s2.a && s1!().a < s1.b
 +}
 +
 +fn do_not_give_bad_suggestions_for_this_incorrect_expr(s1: &S, s2: &SaOnly) -> bool {
 +    // There's two `s1.b`, but we should not suggest `s2.b` since that is invalid
 +    s1.a < s2.a && s1.b < s1.b
 +}
 +
 +fn permissable(s1: &S, s2: &S) -> bool {
 +    // Something like this seems like it might actually be what is desired.
 +    s1.a == s2.b
 +}
 +
 +fn non_boolean_operators(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d
 +}
 +
 +fn odd_number_of_pairs(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.b`
 +    s1.a * s2.a + s1.b * s2.c + s1.c * s2.c
 +}
 +
 +fn not_caught_by_eq_op_middle_change_left(s1: &S, s2: &S) -> i32 {
 +    // There's no `s1.b`
 +    s1.a * s2.a + s2.b * s2.b + s1.c * s2.c
 +}
 +
 +fn not_caught_by_eq_op_middle_change_right(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.b`
 +    s1.a * s2.a + s1.b * s1.b + s1.c * s2.c
 +}
 +
 +fn not_caught_by_eq_op_start(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.a`
 +    s1.a * s1.a + s1.b * s2.b + s1.c * s2.c
 +}
 +
 +fn not_caught_by_eq_op_end(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    s1.a * s2.a + s1.b * s2.b + s1.c * s1.c
 +}
 +
 +fn the_cross_product_should_not_lint(s1: &S, s2: &S) -> (i32, i32, i32) {
 +    (
 +        s1.b * s2.c - s1.c * s2.b,
 +        s1.c * s2.a - s1.a * s2.c,
 +        s1.a * s2.b - s1.b * s2.a,
 +    )
 +}
 +
 +fn outer_parens_simple(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.b`
 +    (s1.a * s2.a + s1.b * s1.b)
 +}
 +
 +fn outer_parens(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d)
 +}
 +
 +fn inner_parens(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)
 +}
 +
 +fn outer_and_some_inner_parens(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))
 +}
 +
 +fn all_parens_balanced_tree(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d)))
 +}
 +
 +fn all_parens_left_tree(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d))
 +}
 +
 +fn all_parens_right_tree(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)))
 +}
 +
 +fn inside_other_binop_expression(s1: &S, s2: &S) -> i32 {
 +    // There's no `s1.b`
 +    (s1.a * s2.a + s2.b * s2.b) / 2
 +}
 +
 +fn inside_function_call(s1: &S, s2: &S) -> i32 {
 +    // There's no `s1.b`
 +    i32::swap_bytes(s1.a * s2.a + s2.b * s2.b)
 +}
 +
 +fn inside_larger_boolean_expression(s1: &S, s2: &S) -> bool {
 +    // There's no `s1.c`
 +    s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d
 +}
 +
 +fn inside_larger_boolean_expression_with_unsorted_ops(s1: &S, s2: &S) -> bool {
 +    // There's no `s1.c`
 +    s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d
 +}
 +
 +struct Nested {
 +    inner: ((i32,), (i32,), (i32,)),
 +}
 +
 +fn changed_middle_ident(n1: &Nested, n2: &Nested) -> bool {
 +    // There's no `n2.inner.2.0`
 +    (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0
 +}
 +
 +// `eq_op` should catch this one.
 +fn changed_initial_ident(n1: &Nested, n2: &Nested) -> bool {
 +    // There's no `n2.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
 +}
 +
 +fn inside_fn_with_similar_expression(s1: &S, s2: &S, strict: bool) -> bool {
 +    if strict {
 +        s1.a < s2.a && s1.b < s2.b
 +    } else {
 +        // There's no `s1.b` in this subexpression
 +        s1.a <= s2.a && s1.a <= s2.b
 +    }
 +}
 +
 +fn inside_an_if_statement(s1: &mut S, s2: &S) {
 +    // There's no `s1.b`
 +    if s1.a < s2.a && s1.a < s2.b {
 +        s1.c = s2.c;
 +    }
 +}
 +
 +fn maximum_unary_minus_right_tree(s1: &S, s2: &S) -> i32 {
 +    // There's no `s2.c`
 +    -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d)))
 +}
 +
 +fn unary_minus_and_an_if_expression(s1: &S, s2: &S) -> i32 {
 +    // There's no `s1.b`
 +    -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a })
 +}
 +
 +fn main() {}
index baf9bc74b000ec30468a9e08112d1decaed61346,0000000000000000000000000000000000000000..29f229245fed305d0477825641094deff7363c40
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,160 @@@
-   --> $DIR/suspicious_operation_groupings.rs:15:9
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:28:20
++  --> $DIR/suspicious_operation_groupings.rs:16:9
 +   |
 +LL |         self.x == other.y && self.y == other.y && self.z == other.z
 +   |         ^^^^^^^^^^^^^^^^^ help: did you mean: `self.x == other.x`
 +   |
 +   = note: `-D clippy::suspicious-operation-groupings` implied by `-D warnings`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:76:33
++  --> $DIR/suspicious_operation_groupings.rs:29:20
 +   |
 +LL |     s1.a < s2.a && s1.a < s2.b
 +   |                    ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:81:19
++  --> $DIR/suspicious_operation_groupings.rs:77:33
 +   |
 +LL |     s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d
 +   |                                 ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:81:19
++  --> $DIR/suspicious_operation_groupings.rs:82:19
 +   |
 +LL |     s1.a * s2.a + s1.b * s2.c + s1.c * s2.c
 +   |                   ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:86:19
++  --> $DIR/suspicious_operation_groupings.rs:82:19
 +   |
 +LL |     s1.a * s2.a + s1.b * s2.c + s1.c * s2.c
 +   |                   ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:91:19
++  --> $DIR/suspicious_operation_groupings.rs:87:19
 +   |
 +LL |     s1.a * s2.a + s2.b * s2.b + s1.c * s2.c
 +   |                   ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:96:5
++  --> $DIR/suspicious_operation_groupings.rs:92:19
 +   |
 +LL |     s1.a * s2.a + s1.b * s1.b + s1.c * s2.c
 +   |                   ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:101:33
++  --> $DIR/suspicious_operation_groupings.rs:97:5
 +   |
 +LL |     s1.a * s1.a + s1.b * s2.b + s1.c * s2.c
 +   |     ^^^^^^^^^^^ help: did you mean: `s1.a * s2.a`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:114:20
++  --> $DIR/suspicious_operation_groupings.rs:102:33
 +   |
 +LL |     s1.a * s2.a + s1.b * s2.b + s1.c * s1.c
 +   |                                 ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:119:34
++  --> $DIR/suspicious_operation_groupings.rs:115:20
 +   |
 +LL |     (s1.a * s2.a + s1.b * s1.b)
 +   |                    ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:124:38
++  --> $DIR/suspicious_operation_groupings.rs:120:34
 +   |
 +LL |     (s1.a * s2.a + s1.b * s2.b + s1.c * s2.b + s1.d * s2.d)
 +   |                                  ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:129:39
++  --> $DIR/suspicious_operation_groupings.rs:125:38
 +   |
 +LL |     (s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)
 +   |                                      ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:134:42
++  --> $DIR/suspicious_operation_groupings.rs:130:39
 +   |
 +LL |     ((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d))
 +   |                                       ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:134:42
++  --> $DIR/suspicious_operation_groupings.rs:135:42
 +   |
 +LL |     (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d)))
 +   |                                          ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:139:40
++  --> $DIR/suspicious_operation_groupings.rs:135:42
 +   |
 +LL |     (((s1.a * s2.a) + (s1.b * s2.b)) + ((s1.c * s2.b) + (s1.d * s2.d)))
 +   |                                          ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:144:40
++  --> $DIR/suspicious_operation_groupings.rs:140:40
 +   |
 +LL |     (((s1.a * s2.a) + (s1.b * s2.b) + (s1.c * s2.b)) + (s1.d * s2.d))
 +   |                                        ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:149:20
++  --> $DIR/suspicious_operation_groupings.rs:145:40
 +   |
 +LL |     ((s1.a * s2.a) + ((s1.b * s2.b) + (s1.c * s2.b) + (s1.d * s2.d)))
 +   |                                        ^^^^^^^^^^^ help: did you mean: `s1.c * s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:154:35
++  --> $DIR/suspicious_operation_groupings.rs:150:20
 +   |
 +LL |     (s1.a * s2.a + s2.b * s2.b) / 2
 +   |                    ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:159:29
++  --> $DIR/suspicious_operation_groupings.rs:155:35
 +   |
 +LL |     i32::swap_bytes(s1.a * s2.a + s2.b * s2.b)
 +   |                                   ^^^^^^^^^^^ help: did you mean: `s1.b * s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:164:17
++  --> $DIR/suspicious_operation_groupings.rs:160:29
 +   |
 +LL |     s1.a > 0 && s1.b > 0 && s1.d == s2.c && s1.d == s2.d
 +   |                             ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:173:77
++  --> $DIR/suspicious_operation_groupings.rs:165:17
 +   |
 +LL |     s1.a > 0 && s1.d == s2.c && s1.b > 0 && s1.d == s2.d
 +   |                 ^^^^^^^^^^^^ help: did you mean: `s1.c == s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:187:25
++  --> $DIR/suspicious_operation_groupings.rs:174:77
 +   |
 +LL |     (n1.inner.0).0 == (n2.inner.0).0 && (n1.inner.1).0 == (n2.inner.1).0 && (n1.inner.2).0 == (n2.inner.1).0
 +   |                                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `(n1.inner.2).0 == (n2.inner.2).0`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:193:23
++  --> $DIR/suspicious_operation_groupings.rs:188:25
 +   |
 +LL |         s1.a <= s2.a && s1.a <= s2.b
 +   |                         ^^^^^^^^^^^^ help: did you mean: `s1.b <= s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:200:48
++  --> $DIR/suspicious_operation_groupings.rs:194:23
 +   |
 +LL |     if s1.a < s2.a && s1.a < s2.b {
 +   |                       ^^^^^^^^^^^ help: did you mean: `s1.b < s2.b`
 +
 +error: this sequence of operators looks suspiciously like a bug
-   --> $DIR/suspicious_operation_groupings.rs:205:27
++  --> $DIR/suspicious_operation_groupings.rs:201:48
 +   |
 +LL |     -(-(-s1.a * -s2.a) + (-(-s1.b * -s2.b) + -(-s1.c * -s2.b) + -(-s1.d * -s2.d)))
 +   |                                                ^^^^^^^^^^^^^ help: did you mean: `-s1.c * -s2.c`
 +
 +error: this sequence of operators looks suspiciously like a bug
++  --> $DIR/suspicious_operation_groupings.rs:206:27
 +   |
 +LL |     -(if -s1.a < -s2.a && -s1.a < -s2.b { s1.c } else { s2.a })
 +   |                           ^^^^^^^^^^^^^ help: did you mean: `-s1.b < -s2.b`
 +
 +error: aborting due to 26 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..596b6ee919bb458c2ef71a9fdd8415bdfc6b1071
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++
++#![warn(clippy::swap_ptr_to_ref)]
++
++use core::ptr::addr_of_mut;
++
++fn main() {
++    let mut x = 0u32;
++    let y: *mut _ = &mut x;
++    let z: *mut _ = &mut x;
++
++    unsafe {
++        core::ptr::swap(y, z);
++        core::ptr::swap(y, &mut x);
++        core::ptr::swap(&mut x, y);
++        core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x));
++    }
++
++    let y = &mut x;
++    let mut z = 0u32;
++    let z = &mut z;
++
++    core::mem::swap(y, z);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..282f571211d957cbddfd5c2dc525f1713bbcbaad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++
++#![warn(clippy::swap_ptr_to_ref)]
++
++use core::ptr::addr_of_mut;
++
++fn main() {
++    let mut x = 0u32;
++    let y: *mut _ = &mut x;
++    let z: *mut _ = &mut x;
++
++    unsafe {
++        core::mem::swap(&mut *y, &mut *z);
++        core::mem::swap(&mut *y, &mut x);
++        core::mem::swap(&mut x, &mut *y);
++        core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x));
++    }
++
++    let y = &mut x;
++    let mut z = 0u32;
++    let z = &mut z;
++
++    core::mem::swap(y, z);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..401ce070869a230c434fe7ed6d4ca594550df348
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref.rs:13:9
++   |
++LL |         core::mem::swap(&mut *y, &mut *z);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, z)`
++   |
++   = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings`
++
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref.rs:14:9
++   |
++LL |         core::mem::swap(&mut *y, &mut x);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(y, &mut x)`
++
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref.rs:15:9
++   |
++LL |         core::mem::swap(&mut x, &mut *y);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(&mut x, y)`
++
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref.rs:16:9
++   |
++LL |         core::mem::swap(&mut *addr_of_mut!(x), &mut *addr_of_mut!(x));
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use ptr::swap: `core::ptr::swap(addr_of_mut!(x), addr_of_mut!(x))`
++
++error: aborting due to 4 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..66ea7c6529bd2b2bc6170b400351b4bc8692bc56
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,18 @@@
++#![warn(clippy::swap_ptr_to_ref)]
++
++macro_rules! addr_of_mut_to_ref {
++    ($e:expr) => {
++        &mut *core::ptr::addr_of_mut!($e)
++    };
++}
++
++fn main() {
++    let mut x = 0u32;
++    let y: *mut _ = &mut x;
++
++    unsafe {
++        core::mem::swap(addr_of_mut_to_ref!(x), &mut *y);
++        core::mem::swap(&mut *y, addr_of_mut_to_ref!(x));
++        core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x));
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c261205d556e49225122c8be41e7a46a2e6832a5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref_unfixable.rs:14:9
++   |
++LL |         core::mem::swap(addr_of_mut_to_ref!(x), &mut *y);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::swap-ptr-to-ref` implied by `-D warnings`
++
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref_unfixable.rs:15:9
++   |
++LL |         core::mem::swap(&mut *y, addr_of_mut_to_ref!(x));
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: call to `core::mem::swap` with a parameter derived from a raw pointer
++  --> $DIR/swap_ptr_to_ref_unfixable.rs:16:9
++   |
++LL |         core::mem::swap(addr_of_mut_to_ref!(x), addr_of_mut_to_ref!(x));
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 3 previous errors
++
index 5b688ce4d644f766881cf36b74e3648df9c2f061,0000000000000000000000000000000000000000..001c910239affa60e9476ea9c0ed71844340db86
mode 100644,000000..100644
--- /dev/null
@@@ -1,145 -1,0 +1,162 @@@
-     let _: &'a T = core::intrinsics::transmute(t);
 +#![allow(dead_code, clippy::borrow_as_ptr)]
 +
 +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 72a386e8fa618f2fe96c5b7f7fc188759c07c161,0000000000000000000000000000000000000000..008b4a981d7e68126a9d96ed3742a454acba11be
mode 100644,000000..100644
--- /dev/null
@@@ -1,250 -1,0 +1,244 @@@
- error: transmute from a type (`&T`) to itself
-   --> $DIR/transmute.rs:19:20
-    |
- LL |     let _: &'a T = core::intrinsics::transmute(t);
-    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    |
-    = note: `-D clippy::useless-transmute` implied by `-D warnings`
 +error: transmute from a reference to a pointer
-   --> $DIR/transmute.rs:23:23
++  --> $DIR/transmute.rs:24:23
 +   |
 +LL |     let _: *const T = core::intrinsics::transmute(t);
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
++   |
++   = note: `-D clippy::useless-transmute` implied by `-D warnings`
 +
 +error: transmute from a reference to a pointer
-   --> $DIR/transmute.rs:25:21
++  --> $DIR/transmute.rs:26:21
 +   |
 +LL |     let _: *mut T = core::intrinsics::transmute(t);
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
 +
 +error: transmute from a reference to a pointer
-   --> $DIR/transmute.rs:27:23
++  --> $DIR/transmute.rs:28:23
 +   |
 +LL |     let _: *const U = core::intrinsics::transmute(t);
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
-   --> $DIR/transmute.rs:33:27
++  --> $DIR/transmute.rs:34:27
 +   |
 +LL |         let _: Vec<i32> = core::intrinsics::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
-   --> $DIR/transmute.rs:35:27
++  --> $DIR/transmute.rs:36:27
 +   |
 +LL |         let _: Vec<i32> = core::mem::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
-   --> $DIR/transmute.rs:37:27
++  --> $DIR/transmute.rs:38:27
 +   |
 +LL |         let _: Vec<i32> = std::intrinsics::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
-   --> $DIR/transmute.rs:39:27
++  --> $DIR/transmute.rs:40:27
 +   |
 +LL |         let _: Vec<i32> = std::mem::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
-   --> $DIR/transmute.rs:41:27
++  --> $DIR/transmute.rs:42:27
 +   |
 +LL |         let _: Vec<i32> = my_transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from an integer to a pointer
-   --> $DIR/transmute.rs:43:31
++  --> $DIR/transmute.rs:44:31
 +   |
 +LL |         let _: *const usize = std::mem::transmute(5_isize);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
 +
 +error: transmute from an integer to a pointer
-   --> $DIR/transmute.rs:47:31
++  --> $DIR/transmute.rs:48:31
 +   |
 +LL |         let _: *const usize = std::mem::transmute(1 + 1usize);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
 +
 +error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
-   --> $DIR/transmute.rs:62:24
++  --> $DIR/transmute.rs:79:24
 +   |
 +LL |         let _: Usize = core::intrinsics::transmute(int_const_ptr);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
 +
 +error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
-   --> $DIR/transmute.rs:64:24
++  --> $DIR/transmute.rs:81:24
 +   |
 +LL |         let _: Usize = core::intrinsics::transmute(int_mut_ptr);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
-   --> $DIR/transmute.rs:66:31
++  --> $DIR/transmute.rs:83:31
 +   |
 +LL |         let _: *const Usize = core::intrinsics::transmute(my_int());
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
-   --> $DIR/transmute.rs:68:29
++  --> $DIR/transmute.rs:85:29
 +   |
 +LL |         let _: *mut Usize = core::intrinsics::transmute(my_int());
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a `u32` to a `char`
-   --> $DIR/transmute.rs:74:28
++  --> $DIR/transmute.rs:91:28
 +   |
 +LL |     let _: char = unsafe { std::mem::transmute(0_u32) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
 +   |
 +   = note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
 +
 +error: transmute from a `i32` to a `char`
-   --> $DIR/transmute.rs:75:28
++  --> $DIR/transmute.rs:92:28
 +   |
 +LL |     let _: char = unsafe { std::mem::transmute(0_i32) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
 +
 +error: transmute from a `u8` to a `bool`
-   --> $DIR/transmute.rs:84:28
++  --> $DIR/transmute.rs:101:28
 +   |
 +LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
 +   |
 +   = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
 +
 +error: transmute from a `u32` to a `f32`
-   --> $DIR/transmute.rs:90:31
++  --> $DIR/transmute.rs:107:31
 +   |
 +LL |         let _: f32 = unsafe { std::mem::transmute(0_u32) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
 +   |
 +   = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
 +
 +error: transmute from a `i32` to a `f32`
-   --> $DIR/transmute.rs:91:31
++  --> $DIR/transmute.rs:108:31
 +   |
 +LL |         let _: f32 = unsafe { std::mem::transmute(0_i32) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
 +
 +error: transmute from a `u64` to a `f64`
-   --> $DIR/transmute.rs:92:31
++  --> $DIR/transmute.rs:109:31
 +   |
 +LL |         let _: f64 = unsafe { std::mem::transmute(0_u64) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
 +
 +error: transmute from a `i64` to a `f64`
-   --> $DIR/transmute.rs:93:31
++  --> $DIR/transmute.rs:110:31
 +   |
 +LL |         let _: f64 = unsafe { std::mem::transmute(0_i64) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
 +
 +error: transmute from a `u8` to a `[u8; 1]`
-   --> $DIR/transmute.rs:113:30
++  --> $DIR/transmute.rs:130:30
 +   |
 +LL |             let _: [u8; 1] = std::mem::transmute(0u8);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
 +   |
 +   = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings`
 +
 +error: transmute from a `u32` to a `[u8; 4]`
-   --> $DIR/transmute.rs:114:30
++  --> $DIR/transmute.rs:131:30
 +   |
 +LL |             let _: [u8; 4] = std::mem::transmute(0u32);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
 +
 +error: transmute from a `u128` to a `[u8; 16]`
-   --> $DIR/transmute.rs:115:31
++  --> $DIR/transmute.rs:132:31
 +   |
 +LL |             let _: [u8; 16] = std::mem::transmute(0u128);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
 +
 +error: transmute from a `i8` to a `[u8; 1]`
-   --> $DIR/transmute.rs:116:30
++  --> $DIR/transmute.rs:133:30
 +   |
 +LL |             let _: [u8; 1] = std::mem::transmute(0i8);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
 +
 +error: transmute from a `i32` to a `[u8; 4]`
-   --> $DIR/transmute.rs:117:30
++  --> $DIR/transmute.rs:134:30
 +   |
 +LL |             let _: [u8; 4] = std::mem::transmute(0i32);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
 +
 +error: transmute from a `i128` to a `[u8; 16]`
-   --> $DIR/transmute.rs:118:31
++  --> $DIR/transmute.rs:135:31
 +   |
 +LL |             let _: [u8; 16] = std::mem::transmute(0i128);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
 +
 +error: transmute from a `f32` to a `[u8; 4]`
-   --> $DIR/transmute.rs:119:30
++  --> $DIR/transmute.rs:136:30
 +   |
 +LL |             let _: [u8; 4] = std::mem::transmute(0.0f32);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()`
 +
 +error: transmute from a `f64` to a `[u8; 8]`
-   --> $DIR/transmute.rs:120:30
++  --> $DIR/transmute.rs:137:30
 +   |
 +LL |             let _: [u8; 8] = std::mem::transmute(0.0f64);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()`
 +
 +error: transmute from a `u8` to a `[u8; 1]`
-   --> $DIR/transmute.rs:125:30
++  --> $DIR/transmute.rs:142:30
 +   |
 +LL |             let _: [u8; 1] = std::mem::transmute(0u8);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
 +
 +error: transmute from a `u32` to a `[u8; 4]`
-   --> $DIR/transmute.rs:126:30
++  --> $DIR/transmute.rs:143:30
 +   |
 +LL |             let _: [u8; 4] = std::mem::transmute(0u32);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
 +
 +error: transmute from a `u128` to a `[u8; 16]`
-   --> $DIR/transmute.rs:127:31
++  --> $DIR/transmute.rs:144:31
 +   |
 +LL |             let _: [u8; 16] = std::mem::transmute(0u128);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
 +
 +error: transmute from a `i8` to a `[u8; 1]`
-   --> $DIR/transmute.rs:128:30
++  --> $DIR/transmute.rs:145:30
 +   |
 +LL |             let _: [u8; 1] = std::mem::transmute(0i8);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
 +
 +error: transmute from a `i32` to a `[u8; 4]`
-   --> $DIR/transmute.rs:129:30
++  --> $DIR/transmute.rs:146:30
 +   |
 +LL |             let _: [u8; 4] = std::mem::transmute(0i32);
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
 +
 +error: transmute from a `i128` to a `[u8; 16]`
-   --> $DIR/transmute.rs:130:31
++  --> $DIR/transmute.rs:147:31
 +   |
 +LL |             let _: [u8; 16] = std::mem::transmute(0i128);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
 +
 +error: transmute from a `&[u8]` to a `&str`
-   --> $DIR/transmute.rs:140:28
++  --> $DIR/transmute.rs:157:28
 +   |
 +LL |     let _: &str = unsafe { std::mem::transmute(B) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()`
 +   |
 +   = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
 +
 +error: transmute from a `&mut [u8]` to a `&mut str`
-   --> $DIR/transmute.rs:141:32
++  --> $DIR/transmute.rs:158:32
 +   |
 +LL |     let _: &mut str = unsafe { std::mem::transmute(mb) };
 +   |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
 +
 +error: transmute from a `&[u8]` to a `&str`
-   --> $DIR/transmute.rs:142:30
++  --> $DIR/transmute.rs:159:30
 +   |
 +LL |     const _: &str = unsafe { std::mem::transmute(B) };
 +   |                              ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)`
 +
- error: aborting due to 39 previous errors
++error: aborting due to 38 previous errors
 +
index b06ed4a917376f63775d02dd60f3d4afc4d2ac75,0000000000000000000000000000000000000000..ebcaa7a84cfb1e9efc1601d6ac0691c4ad89f790
mode 100644,000000..100644
--- /dev/null
@@@ -1,144 -1,0 +1,144 @@@
- #![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]
 +#![warn(clippy::transmute_undefined_repr)]
++#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)]
 +
 +use core::any::TypeId;
 +use core::ffi::c_void;
 +use core::mem::{size_of, transmute, MaybeUninit};
 +
 +fn value<T>() -> T {
 +    unimplemented!()
 +}
 +
 +struct Empty;
 +struct Ty<T>(T);
 +struct Ty2<T, U>(T, U);
 +
 +#[repr(C)]
 +struct Ty2C<T, U>(T, U);
 +
 +fn main() {
 +    unsafe {
 +        let _: () = transmute(value::<Empty>());
 +        let _: Empty = transmute(value::<()>());
 +
 +        let _: Ty<u32> = transmute(value::<u32>());
 +        let _: Ty<u32> = transmute(value::<u32>());
 +
 +        let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
 +        let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
 +
 +        let _: Ty2<u32, i32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
 +        let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
 +
 +        let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
 +        let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +
 +        let _: Ty<&()> = transmute(value::<&()>());
 +        let _: &() = transmute(value::<Ty<&()>>());
 +
 +        let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
 +        let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +
 +        let _: Ty<usize> = transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
 +        let _: &Ty2<u32, i32> = transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
 +
 +        let _: Ty<[u8; 8]> = transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
 +        let _: Ty2<u32, i32> = transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
 +
 +        // issue #8417
 +        let _: Ty2C<Ty2<u32, i32>, ()> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
 +        let _: Ty2<u32, i32> = transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same
 +
 +        let _: &'static mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
 +        let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
 +        let _: *mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
 +        let _: Box<Ty2<u32, u32>> = transmute(value::<*mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
 +
 +        let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
 +        let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +
 +        let _: *const () = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const ()>()); // Ok, reverse type erasure
 +
 +        let _: *const c_void = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const c_void>()); // Ok, reverse type erasure
 +
 +        enum Erase {}
 +        let _: *const Erase = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase>()); // Ok, reverse type erasure
 +
 +        struct Erase2(
 +            [u8; 0],
 +            core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
 +        );
 +        let _: *const Erase2 = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase2>()); // Ok, reverse type erasure
 +
 +        let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure
 +        let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure
 +
 +        let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure
 +        let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure
 +
 +        let _: [u8; size_of::<&[u8]>()] = transmute(value::<&[u8]>()); // Ok, transmute to byte array
 +        let _: &[u8] = transmute(value::<[u8; size_of::<&[u8]>()]>()); // Ok, transmute from byte array
 +
 +        let _: [usize; 2] = transmute(value::<&[u8]>()); // Ok, transmute to int array
 +        let _: &[u8] = transmute(value::<[usize; 2]>()); // Ok, transmute from int array
 +
 +        let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
 +        let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
 +
 +        let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>,)>()); // Ok
 +        let _: (Ty2<u32, u32>,) = transmute(value::<Ty2<u32, u32>>()); // Ok
 +
 +        let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>, ())>()); // Ok
 +        let _: (Ty2<u32, u32>, ()) = transmute(value::<Ty2<u32, u32>>()); // Ok
 +
 +        let _: Ty2<u32, u32> = transmute(value::<((), Ty2<u32, u32>)>()); // Ok
 +        let _: ((), Ty2<u32, u32>) = transmute(value::<Ty2<u32, u32>>()); // Ok
 +
 +        let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok
 +        let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok
 +
 +        trait Trait {}
 +        let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok
 +        let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok
 +
 +        let _: MaybeUninit<Ty2<u32, u32>> = transmute(value::<Ty2<u32, u32>>()); // Ok
 +        let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
 +
 +        let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
 +    }
 +}
 +
 +fn _with_generics<T: 'static, U: 'static>() {
 +    if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() {
 +        return;
 +    }
 +    unsafe {
 +        let _: &u32 = transmute(value::<&T>()); // Ok
 +        let _: &T = transmute(value::<&u32>()); // Ok
 +
 +        let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok
 +        let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok
 +
 +        let _: Ty<&u32> = transmute(value::<&T>()); // Ok
 +        let _: Ty<&T> = transmute(value::<&u32>()); // Ok
 +
 +        let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok
 +        let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok
 +
 +        let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok
 +        let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok
 +
 +        let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok
 +        let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok
 +
 +        let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
 +        let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
 +
 +        let _: *const u32 = transmute(value::<Box<T>>()); // Ok
 +        let _: Box<T> = transmute(value::<*const u32>()); // Ok
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..328cda369e11bb89a59a5db907abb14deefbf91e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++// run-rustfix
++#[warn(clippy::invisible_characters)]
++fn zero() {
++    print!("Here >\u{200B}< is a ZWS, and \u{200B}another");
++    print!("This\u{200B}is\u{200B}fine");
++    print!("Here >\u{AD}< is a SHY, and \u{AD}another");
++    print!("This\u{ad}is\u{ad}fine");
++    print!("Here >\u{2060}< is a WJ, and \u{2060}another");
++    print!("This\u{2060}is\u{2060}fine");
++}
++
++#[warn(clippy::unicode_not_nfc)]
++fn canon() {
++    print!("̀àh?");
++    print!("a\u{0300}h?"); // also ok
++}
++
++#[warn(clippy::non_ascii_literal)]
++fn uni() {
++    print!("\u{dc}ben!");
++    print!("\u{DC}ben!"); // this is ok
++}
++
++// issue 8013
++#[warn(clippy::non_ascii_literal)]
++fn single_quote() {
++    const _EMPTY_BLOCK: char = '\u{25b1}';
++    const _FULL_BLOCK: char = '\u{25b0}';
++}
++
++fn main() {
++    zero();
++    uni();
++    canon();
++    single_quote();
++}
index e0a4eadce33bd109bfd67f1ac8212f363a896811,0000000000000000000000000000000000000000..7828d6bcbea7ad39e4a68c82ef69b9c9ede16015
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,36 @@@
++// run-rustfix
 +#[warn(clippy::invisible_characters)]
 +fn zero() {
 +    print!("Here >​< is a ZWS, and ​another");
 +    print!("This\u{200B}is\u{200B}fine");
 +    print!("Here >­< is a SHY, and ­another");
 +    print!("This\u{ad}is\u{ad}fine");
 +    print!("Here >⁠< is a WJ, and ⁠another");
 +    print!("This\u{2060}is\u{2060}fine");
 +}
 +
 +#[warn(clippy::unicode_not_nfc)]
 +fn canon() {
 +    print!("̀àh?");
 +    print!("a\u{0300}h?"); // also ok
 +}
 +
 +#[warn(clippy::non_ascii_literal)]
 +fn uni() {
 +    print!("Üben!");
 +    print!("\u{DC}ben!"); // this is ok
 +}
 +
 +// issue 8013
 +#[warn(clippy::non_ascii_literal)]
 +fn single_quote() {
 +    const _EMPTY_BLOCK: char = '▱';
 +    const _FULL_BLOCK: char = '▰';
 +}
 +
 +fn main() {
 +    zero();
 +    uni();
 +    canon();
 +    single_quote();
 +}
index 3f54e3880e7470a46c61d2b1b0b422eb8b0d604b,0000000000000000000000000000000000000000..01d3f3c0296799cc15b4cabcbc811b786e8da427
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,50 @@@
-   --> $DIR/unicode.rs:3:12
 +error: invisible character detected
-   --> $DIR/unicode.rs:5:12
++  --> $DIR/unicode.rs:4:12
 +   |
 +LL |     print!("Here >​< is a ZWS, and ​another");
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
 +   |
 +   = note: `-D clippy::invisible-characters` implied by `-D warnings`
 +
 +error: invisible character detected
-   --> $DIR/unicode.rs:7:12
++  --> $DIR/unicode.rs:6:12
 +   |
 +LL |     print!("Here >­< is a SHY, and ­another");
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
 +
 +error: invisible character detected
-   --> $DIR/unicode.rs:13:12
++  --> $DIR/unicode.rs:8:12
 +   |
 +LL |     print!("Here >⁠< is a WJ, and ⁠another");
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
 +
 +error: non-NFC Unicode sequence detected
-   --> $DIR/unicode.rs:19:12
++  --> $DIR/unicode.rs:14:12
 +   |
 +LL |     print!("̀àh?");
 +   |            ^^^^^ help: consider replacing the string with: `"̀àh?"`
 +   |
 +   = note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
 +
 +error: literal non-ASCII character detected
-   --> $DIR/unicode.rs:26:32
++  --> $DIR/unicode.rs:20:12
 +   |
 +LL |     print!("Üben!");
 +   |            ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
 +   |
 +   = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
 +
 +error: literal non-ASCII character detected
-   --> $DIR/unicode.rs:27:31
++  --> $DIR/unicode.rs:27:32
 +   |
 +LL |     const _EMPTY_BLOCK: char = '▱';
 +   |                                ^^^ help: consider replacing the string with: `'/u{25b1}'`
 +
 +error: literal non-ASCII character detected
++  --> $DIR/unicode.rs:28:31
 +   |
 +LL |     const _FULL_BLOCK: char = '▰';
 +   |                               ^^^ help: consider replacing the string with: `'/u{25b0}'`
 +
 +error: aborting due to 7 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9400e93cac8390e52974d503356910c93bf22e83
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++// run-rustfix
++#![warn(clippy::unit_arg)]
++#![allow(clippy::no_effect, unused_must_use, unused_variables)]
++
++use std::fmt::Debug;
++
++fn foo<T: Debug>(t: T) {
++    println!("{:?}", t);
++}
++
++fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
++    println!("{:?}, {:?}, {:?}", t1, t2, t3);
++}
++
++fn bad() {
++    foo(());
++    foo3((), 2, 2);
++    foo(0);
++    taking_two_units((), ());
++    foo(0);
++    foo(1);
++    taking_three_units((), (), ());
++}
++
++fn taking_two_units(a: (), b: ()) {}
++fn taking_three_units(a: (), b: (), c: ()) {}
++
++fn main() {
++    bad();
++}
index 18a31eb3deee26f400f9266c8bb6741017826d15,0000000000000000000000000000000000000000..5f52b6c5315fdded584a3e0266ff99ea2be7da64
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,27 @@@
++// run-rustfix
 +#![warn(clippy::unit_arg)]
 +#![allow(clippy::no_effect, unused_must_use, unused_variables)]
 +
 +use std::fmt::Debug;
 +
 +fn foo<T: Debug>(t: T) {
 +    println!("{:?}", t);
 +}
 +
 +fn foo3<T1: Debug, T2: Debug, T3: Debug>(t1: T1, t2: T2, t3: T3) {
 +    println!("{:?}, {:?}, {:?}", t1, t2, t3);
 +}
 +
 +fn bad() {
 +    foo({});
 +    foo3({}, 2, 2);
 +    taking_two_units({}, foo(0));
 +    taking_three_units({}, foo(0), foo(1));
 +}
 +
 +fn taking_two_units(a: (), b: ()) {}
 +fn taking_three_units(a: (), b: (), c: ()) {}
 +
 +fn main() {
 +    bad();
 +}
index 39072c9a8cc0f0825756e4051d8988b0aaa639c5,0000000000000000000000000000000000000000..d35e931697d211fcfbf6ea6ac742a47f22456b8e
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,45 @@@
-   --> $DIR/unit_arg_empty_blocks.rs:15:5
 +error: passing a unit value to a function
-   --> $DIR/unit_arg_empty_blocks.rs:16:5
++  --> $DIR/unit_arg_empty_blocks.rs:16:5
 +   |
 +LL |     foo({});
 +   |     ^^^^--^
 +   |         |
 +   |         help: use a unit literal instead: `()`
 +   |
 +   = note: `-D clippy::unit-arg` implied by `-D warnings`
 +
 +error: passing a unit value to a function
-   --> $DIR/unit_arg_empty_blocks.rs:17:5
++  --> $DIR/unit_arg_empty_blocks.rs:17:5
 +   |
 +LL |     foo3({}, 2, 2);
 +   |     ^^^^^--^^^^^^^
 +   |          |
 +   |          help: use a unit literal instead: `()`
 +
 +error: passing unit values to a function
-   --> $DIR/unit_arg_empty_blocks.rs:18:5
++  --> $DIR/unit_arg_empty_blocks.rs:18:5
 +   |
 +LL |     taking_two_units({}, foo(0));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: move the expression in front of the call and replace it with the unit literal `()`
 +   |
 +LL ~     foo(0);
 +LL ~     taking_two_units((), ());
 +   |
 +
 +error: passing unit values to a function
++  --> $DIR/unit_arg_empty_blocks.rs:19:5
 +   |
 +LL |     taking_three_units({}, foo(0), foo(1));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +help: move the expressions in front of the call and replace them with the unit literal `()`
 +   |
 +LL ~     foo(0);
 +LL +     foo(1);
 +LL ~     taking_three_units((), (), ());
 +   |
 +
 +error: aborting due to 4 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b352b285c86267f42fed1f4be2ce85d51c544afd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++// run-rustfix
++#![warn(clippy::unnecessary_cast)]
++#![allow(
++    unused_must_use,
++    clippy::borrow_as_ptr,
++    clippy::no_effect,
++    clippy::nonstandard_macro_braces,
++    clippy::unnecessary_operation
++)]
++
++#[rustfmt::skip]
++fn main() {
++    // Test cast_unnecessary
++    1_i32;
++    1_f32;
++    false;
++    &1i32 as &i32;
++
++    -1_i32;
++    - 1_i32;
++    -1_f32;
++    1_i32;
++    1_f32;
++
++    // macro version
++    macro_rules! foo {
++        ($a:ident, $b:ident) => {
++            #[allow(unused)]
++            pub fn $a() -> $b {
++                1 as $b
++            }
++        };
++    }
++    foo!(a, i32);
++    foo!(b, f32);
++    foo!(c, f64);
++
++    // do not lint cast to cfg-dependant type
++    1 as std::os::raw::c_char;
++
++    // do not lint cast to alias type
++    1 as I32Alias;
++    &1 as &I32Alias;
++}
++
++type I32Alias = i32;
++
++mod fixable {
++    #![allow(dead_code)]
++
++    fn main() {
++        // casting integer literal to float is unnecessary
++        100_f32;
++        100_f64;
++        100_f64;
++        let _ = -100_f32;
++        let _ = -100_f64;
++        let _ = -100_f64;
++        100_f32;
++        100_f64;
++        // Should not trigger
++        #[rustfmt::skip]
++        let v = vec!(1);
++        &v as &[i32];
++        0x10 as f32;
++        0o10 as f32;
++        0b10 as f32;
++        0x11 as f64;
++        0o11 as f64;
++        0b11 as f64;
++
++        1_u32;
++        0x10_i32;
++        0b10_usize;
++        0o73_u16;
++        1_000_000_000_u32;
++
++        1.0_f64;
++        0.5_f32;
++
++        1.0 as u16;
++
++        let _ = -1_i32;
++        let _ = -1.0_f32;
++
++        let _ = 1 as I32Alias;
++        let _ = &1 as &I32Alias;
++    }
++
++    type I32Alias = i32;
++}
index 62c3e9636866a5a67492f339ca706b97aea73c31,0000000000000000000000000000000000000000..6c8cc3effe8fefcc9ca0cf5c806549bd0037b0f2
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,91 @@@
- #![allow(clippy::no_effect)]
++// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
++#![allow(
++    unused_must_use,
++    clippy::borrow_as_ptr,
++    clippy::no_effect,
++    clippy::nonstandard_macro_braces,
++    clippy::unnecessary_operation
++)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1i32 as i32;
 +    1f32 as f32;
 +    false as bool;
 +    &1i32 as &i32;
 +
 +    -1_i32 as i32;
 +    - 1_i32 as i32;
 +    -1f32 as f32;
 +    1_i32 as i32;
 +    1_f32 as f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // do not lint cast to alias type
 +    1 as I32Alias;
 +    &1 as &I32Alias;
 +}
 +
 +type I32Alias = i32;
++
++mod fixable {
++    #![allow(dead_code)]
++
++    fn main() {
++        // casting integer literal to float is unnecessary
++        100 as f32;
++        100 as f64;
++        100_i32 as f64;
++        let _ = -100 as f32;
++        let _ = -100 as f64;
++        let _ = -100_i32 as f64;
++        100. as f32;
++        100. as f64;
++        // Should not trigger
++        #[rustfmt::skip]
++        let v = vec!(1);
++        &v as &[i32];
++        0x10 as f32;
++        0o10 as f32;
++        0b10 as f32;
++        0x11 as f64;
++        0o11 as f64;
++        0b11 as f64;
++
++        1 as u32;
++        0x10 as i32;
++        0b10 as usize;
++        0o73 as u16;
++        1_000_000_000 as u32;
++
++        1.0 as f64;
++        0.5 as f32;
++
++        1.0 as u16;
++
++        let _ = -1 as i32;
++        let _ = -1.0 as f32;
++
++        let _ = 1 as I32Alias;
++        let _ = &1 as &I32Alias;
++    }
++
++    type I32Alias = i32;
++}
index a5a93c6110c6a0233f7b8304ceaebb730f878b15,0000000000000000000000000000000000000000..bad45f0025b2292fe0360c52ec59effefac4c361
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,154 @@@
-   --> $DIR/unnecessary_cast.rs:7:5
 +error: casting integer literal to `i32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:8:5
++  --> $DIR/unnecessary_cast.rs:14:5
 +   |
 +LL |     1i32 as i32;
 +   |     ^^^^^^^^^^^ help: try: `1_i32`
 +   |
 +   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 +
 +error: casting float literal to `f32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:9:5
++  --> $DIR/unnecessary_cast.rs:15:5
 +   |
 +LL |     1f32 as f32;
 +   |     ^^^^^^^^^^^ help: try: `1_f32`
 +
 +error: casting to the same type is unnecessary (`bool` -> `bool`)
-   --> $DIR/unnecessary_cast.rs:12:5
++  --> $DIR/unnecessary_cast.rs:16:5
 +   |
 +LL |     false as bool;
 +   |     ^^^^^^^^^^^^^ help: try: `false`
 +
 +error: casting integer literal to `i32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:13:5
++  --> $DIR/unnecessary_cast.rs:19:5
 +   |
 +LL |     -1_i32 as i32;
 +   |     ^^^^^^^^^^^^^ help: try: `-1_i32`
 +
 +error: casting integer literal to `i32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:14:5
++  --> $DIR/unnecessary_cast.rs:20:5
 +   |
 +LL |     - 1_i32 as i32;
 +   |     ^^^^^^^^^^^^^^ help: try: `- 1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:15:5
++  --> $DIR/unnecessary_cast.rs:21:5
 +   |
 +LL |     -1f32 as f32;
 +   |     ^^^^^^^^^^^^ help: try: `-1_f32`
 +
 +error: casting integer literal to `i32` is unnecessary
-   --> $DIR/unnecessary_cast.rs:16:5
++  --> $DIR/unnecessary_cast.rs:22:5
 +   |
 +LL |     1_i32 as i32;
 +   |     ^^^^^^^^^^^^ help: try: `1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
- error: aborting due to 8 previous errors
++  --> $DIR/unnecessary_cast.rs:23:5
 +   |
 +LL |     1_f32 as f32;
 +   |     ^^^^^^^^^^^^ help: try: `1_f32`
 +
++error: casting integer literal to `f32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:53:9
++   |
++LL |         100 as f32;
++   |         ^^^^^^^^^^ help: try: `100_f32`
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:54:9
++   |
++LL |         100 as f64;
++   |         ^^^^^^^^^^ help: try: `100_f64`
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:55:9
++   |
++LL |         100_i32 as f64;
++   |         ^^^^^^^^^^^^^^ help: try: `100_f64`
++
++error: casting integer literal to `f32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:56:17
++   |
++LL |         let _ = -100 as f32;
++   |                 ^^^^^^^^^^^ help: try: `-100_f32`
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:57:17
++   |
++LL |         let _ = -100 as f64;
++   |                 ^^^^^^^^^^^ help: try: `-100_f64`
++
++error: casting integer literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:58:17
++   |
++LL |         let _ = -100_i32 as f64;
++   |                 ^^^^^^^^^^^^^^^ help: try: `-100_f64`
++
++error: casting float literal to `f32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:59:9
++   |
++LL |         100. as f32;
++   |         ^^^^^^^^^^^ help: try: `100_f32`
++
++error: casting float literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:60:9
++   |
++LL |         100. as f64;
++   |         ^^^^^^^^^^^ help: try: `100_f64`
++
++error: casting integer literal to `u32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:72:9
++   |
++LL |         1 as u32;
++   |         ^^^^^^^^ help: try: `1_u32`
++
++error: casting integer literal to `i32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:73:9
++   |
++LL |         0x10 as i32;
++   |         ^^^^^^^^^^^ help: try: `0x10_i32`
++
++error: casting integer literal to `usize` is unnecessary
++  --> $DIR/unnecessary_cast.rs:74:9
++   |
++LL |         0b10 as usize;
++   |         ^^^^^^^^^^^^^ help: try: `0b10_usize`
++
++error: casting integer literal to `u16` is unnecessary
++  --> $DIR/unnecessary_cast.rs:75:9
++   |
++LL |         0o73 as u16;
++   |         ^^^^^^^^^^^ help: try: `0o73_u16`
++
++error: casting integer literal to `u32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:76:9
++   |
++LL |         1_000_000_000 as u32;
++   |         ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
++
++error: casting float literal to `f64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:78:9
++   |
++LL |         1.0 as f64;
++   |         ^^^^^^^^^^ help: try: `1.0_f64`
++
++error: casting float literal to `f32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:79:9
++   |
++LL |         0.5 as f32;
++   |         ^^^^^^^^^^ help: try: `0.5_f32`
++
++error: casting integer literal to `i32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:83:17
++   |
++LL |         let _ = -1 as i32;
++   |                 ^^^^^^^^^ help: try: `-1_i32`
++
++error: casting float literal to `f32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:84:17
++   |
++LL |         let _ = -1.0 as f32;
++   |                 ^^^^^^^^^^^ help: try: `-1.0_f32`
++
++error: aborting due to 25 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..54f85806ac3b3e5c402e18a573896fc14aa2acc2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-rustfix
++#![warn(clippy::unused_rounding)]
++
++fn main() {
++    let _ = 1f32;
++    let _ = 1.0f64;
++    let _ = 1.00f32;
++    let _ = 2e-54f64.floor();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8d007bc4a1dc8f3333ab72ac2f230fa56808b011
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++// run-rustfix
++#![warn(clippy::unused_rounding)]
++
++fn main() {
++    let _ = 1f32.ceil();
++    let _ = 1.0f64.floor();
++    let _ = 1.00f32.round();
++    let _ = 2e-54f64.floor();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6cfb02e040283f615f1162618993fe7709dbe05c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,22 @@@
++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: aborting due to 3 previous errors
++
index 3e62ffe74feddb270f358cc17785175353e34495,0000000000000000000000000000000000000000..4f80aaecc902d1d49aa1d2c09ab19ce76787b26d
mode 100644,000000..100644
--- /dev/null
@@@ -1,544 -1,0 +1,610 @@@
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
 +#![warn(clippy::use_self)]
 +#![allow(dead_code, unreachable_code)]
 +#![allow(
 +    clippy::should_implement_trait,
 +    clippy::upper_case_acronyms,
 +    clippy::from_over_into,
 +    clippy::self_named_constructors
 +)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            Self(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo;
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo;
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Self {
 +                    Self { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Self::B(42);
 +            let _ = Self::C { field: true };
 +            let _ = Self::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A;
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            Self::fun_1();
 +            Self::A;
 +
 +            Self {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct;
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            Self::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S;
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> Self {
 +            Self {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[Self::A..Self::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Self {
 +            Self { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo;
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo;
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B;
 +        pub struct C;
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            Self::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod self_is_ty_param {
 +    trait Trait {
 +        type Type;
 +        type Hi;
 +
 +        fn test();
 +    }
 +
 +    impl<I> Trait for I
 +    where
 +        I: Iterator,
 +        I::Item: Trait, // changing this to Self would require <Self as Iterator>
 +    {
 +        type Type = I;
 +        type Hi = I::Item;
 +
 +        fn test() {
 +            let _: I::Item;
 +            let _: I; // this could lint, but is questionable
 +        }
 +    }
 +}
 +
 +mod use_self_in_pat {
 +    enum Foo {
 +        Bar,
 +        Baz,
 +    }
 +
 +    impl Foo {
 +        fn do_stuff(self) {
 +            match self {
 +                Self::Bar => unimplemented!(),
 +                Self::Baz => unimplemented!(),
 +            }
 +            match Some(1) {
 +                Some(_) => unimplemented!(),
 +                None => unimplemented!(),
 +            }
 +            if let Self::Bar = self {
 +                unimplemented!()
 +            }
 +        }
 +    }
 +}
++
++mod issue8845 {
++    pub enum Something {
++        Num(u8),
++        TupleNums(u8, u8),
++        StructNums { one: u8, two: u8 },
++    }
++
++    struct Foo(u8);
++
++    struct Bar {
++        x: u8,
++        y: usize,
++    }
++
++    impl Something {
++        fn get_value(&self) -> u8 {
++            match self {
++                Self::Num(n) => *n,
++                Self::TupleNums(n, _m) => *n,
++                Self::StructNums { one, two: _ } => *one,
++            }
++        }
++
++        fn use_crate(&self) -> u8 {
++            match self {
++                Self::Num(n) => *n,
++                Self::TupleNums(n, _m) => *n,
++                Self::StructNums { one, two: _ } => *one,
++            }
++        }
++
++        fn imported_values(&self) -> u8 {
++            use Something::*;
++            match self {
++                Num(n) => *n,
++                TupleNums(n, _m) => *n,
++                StructNums { one, two: _ } => *one,
++            }
++        }
++    }
++
++    impl Foo {
++        fn get_value(&self) -> u8 {
++            let Self(x) = self;
++            *x
++        }
++
++        fn use_crate(&self) -> u8 {
++            let Self(x) = self;
++            *x
++        }
++    }
++
++    impl Bar {
++        fn get_value(&self) -> u8 {
++            let Self { x, .. } = self;
++            *x
++        }
++
++        fn use_crate(&self) -> u8 {
++            let Self { x, .. } = self;
++            *x
++        }
++    }
++}
index da2faddee12a71327de345e5c7460659853690dc,0000000000000000000000000000000000000000..52da72db53ce3a9706847825d85b8a0fe062eb65
mode 100644,000000..100644
--- /dev/null
@@@ -1,544 -1,0 +1,610 @@@
 +// run-rustfix
 +// aux-build:proc_macro_derive.rs
 +
 +#![warn(clippy::use_self)]
 +#![allow(dead_code, unreachable_code)]
 +#![allow(
 +    clippy::should_implement_trait,
 +    clippy::upper_case_acronyms,
 +    clippy::from_over_into,
 +    clippy::self_named_constructors
 +)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +
 +fn main() {}
 +
 +mod use_self {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +        fn test() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod better {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Self {}
 +        }
 +        fn test() -> Self {
 +            Self::new()
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        fn default() -> Self {
 +            Self::new()
 +        }
 +    }
 +}
 +
 +mod lifetimes {
 +    struct Foo<'a> {
 +        foo_str: &'a str,
 +    }
 +
 +    impl<'a> Foo<'a> {
 +        // Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
 +        // Foo<'b>`
 +        fn foo(s: &str) -> Foo {
 +            Foo { foo_str: s }
 +        }
 +        // cannot replace with `Self`, because that's `Foo<'a>`
 +        fn bar() -> Foo<'static> {
 +            Foo { foo_str: "foo" }
 +        }
 +
 +        // FIXME: the lint does not handle lifetimed struct
 +        // `Self` should be applicable here
 +        fn clone(&self) -> Foo<'a> {
 +            Foo { foo_str: self.foo_str }
 +        }
 +    }
 +}
 +
 +mod issue2894 {
 +    trait IntoBytes {
 +        fn to_bytes(self) -> Vec<u8>;
 +    }
 +
 +    // This should not be linted
 +    impl IntoBytes for u8 {
 +        fn to_bytes(self) -> Vec<u8> {
 +            vec![self]
 +        }
 +    }
 +}
 +
 +mod existential {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +            foos.iter()
 +        }
 +
 +        fn good(foos: &[Self]) -> impl Iterator<Item = &Self> {
 +            foos.iter()
 +        }
 +    }
 +}
 +
 +mod tuple_structs {
 +    pub struct TS(i32);
 +
 +    impl TS {
 +        pub fn ts() -> Self {
 +            TS(0)
 +        }
 +    }
 +}
 +
 +mod macros {
 +    macro_rules! use_self_expand {
 +        () => {
 +            fn new() -> Foo {
 +                Foo {}
 +            }
 +        };
 +    }
 +
 +    struct Foo;
 +
 +    impl Foo {
 +        use_self_expand!(); // Should not lint in local macros
 +    }
 +
 +    #[derive(StructAUseSelf)] // Should not lint in derives
 +    struct A;
 +}
 +
 +mod nesting {
 +    struct Foo;
 +    impl Foo {
 +        fn foo() {
 +            #[allow(unused_imports)]
 +            use self::Foo; // Can't use Self here
 +            struct Bar {
 +                foo: Foo, // Foo != Self
 +            }
 +
 +            impl Bar {
 +                fn bar() -> Bar {
 +                    Bar { foo: Foo {} }
 +                }
 +            }
 +
 +            // Can't use Self here
 +            fn baz() -> Foo {
 +                Foo {}
 +            }
 +        }
 +
 +        // Should lint here
 +        fn baz() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    enum Enum {
 +        A,
 +        B(u64),
 +        C { field: bool },
 +    }
 +    impl Enum {
 +        fn method() {
 +            #[allow(unused_imports)]
 +            use self::Enum::*; // Issue 3425
 +            static STATIC: Enum = Enum::A; // Can't use Self as type
 +        }
 +
 +        fn method2() {
 +            let _ = Enum::B(42);
 +            let _ = Enum::C { field: true };
 +            let _ = Enum::A;
 +        }
 +    }
 +}
 +
 +mod issue3410 {
 +
 +    struct A;
 +    struct B;
 +
 +    trait Trait<T> {
 +        fn a(v: T) -> Self;
 +    }
 +
 +    impl Trait<Vec<A>> for Vec<B> {
 +        fn a(_: Vec<A>) -> Self {
 +            unimplemented!()
 +        }
 +    }
 +
 +    impl<T> Trait<Vec<A>> for Vec<T>
 +    where
 +        T: Trait<B>,
 +    {
 +        fn a(v: Vec<A>) -> Self {
 +            <Vec<B>>::a(v).into_iter().map(Trait::a).collect()
 +        }
 +    }
 +}
 +
 +#[allow(clippy::no_effect, path_statements)]
 +mod rustfix {
 +    mod nested {
 +        pub struct A;
 +    }
 +
 +    impl nested::A {
 +        const A: bool = true;
 +
 +        fn fun_1() {}
 +
 +        fn fun_2() {
 +            nested::A::fun_1();
 +            nested::A::A;
 +
 +            nested::A {};
 +        }
 +    }
 +}
 +
 +mod issue3567 {
 +    struct TestStruct;
 +    impl TestStruct {
 +        fn from_something() -> Self {
 +            Self {}
 +        }
 +    }
 +
 +    trait Test {
 +        fn test() -> TestStruct;
 +    }
 +
 +    impl Test for TestStruct {
 +        fn test() -> TestStruct {
 +            TestStruct::from_something()
 +        }
 +    }
 +}
 +
 +mod paths_created_by_lowering {
 +    use std::ops::Range;
 +
 +    struct S;
 +
 +    impl S {
 +        const A: usize = 0;
 +        const B: usize = 1;
 +
 +        async fn g() -> S {
 +            S {}
 +        }
 +
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[S::A..S::B]
 +        }
 +    }
 +
 +    trait T {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8];
 +    }
 +
 +    impl T for Range<u8> {
 +        fn f<'a>(&self, p: &'a [u8]) -> &'a [u8] {
 +            &p[0..1]
 +        }
 +    }
 +}
 +
 +// reused from #1997
 +mod generics {
 +    struct Foo<T> {
 +        value: T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        // `Self` is applicable here
 +        fn foo(value: T) -> Foo<T> {
 +            Foo::<T> { value }
 +        }
 +
 +        // `Cannot` use `Self` as a return type as the generic types are different
 +        fn bar(value: i32) -> Foo<i32> {
 +            Foo { value }
 +        }
 +    }
 +}
 +
 +mod issue4140 {
 +    pub struct Error<From, To> {
 +        _from: From,
 +        _too: To,
 +    }
 +
 +    pub trait From<T> {
 +        type From;
 +        type To;
 +
 +        fn from(value: T) -> Self;
 +    }
 +
 +    pub trait TryFrom<T>
 +    where
 +        Self: Sized,
 +    {
 +        type From;
 +        type To;
 +
 +        fn try_from(value: T) -> Result<Self, Error<Self::From, Self::To>>;
 +    }
 +
 +    // FIXME: Suggested fix results in infinite recursion.
 +    // impl<F, T> TryFrom<F> for T
 +    // where
 +    //     T: From<F>,
 +    // {
 +    //     type From = Self::From;
 +    //     type To = Self::To;
 +
 +    //     fn try_from(value: F) -> Result<Self, Error<Self::From, Self::To>> {
 +    //         Ok(From::from(value))
 +    //     }
 +    // }
 +
 +    impl From<bool> for i64 {
 +        type From = bool;
 +        type To = Self;
 +
 +        fn from(value: bool) -> Self {
 +            if value { 100 } else { 0 }
 +        }
 +    }
 +}
 +
 +mod issue2843 {
 +    trait Foo {
 +        type Bar;
 +    }
 +
 +    impl Foo for usize {
 +        type Bar = u8;
 +    }
 +
 +    impl<T: Foo> Foo for Option<T> {
 +        type Bar = Option<T::Bar>;
 +    }
 +}
 +
 +mod issue3859 {
 +    pub struct Foo;
 +    pub struct Bar([usize; 3]);
 +
 +    impl Foo {
 +        pub const BAR: usize = 3;
 +
 +        pub fn foo() {
 +            const _X: usize = Foo::BAR;
 +            // const _Y: usize = Self::BAR;
 +        }
 +    }
 +}
 +
 +mod issue4305 {
 +    trait Foo: 'static {}
 +
 +    struct Bar;
 +
 +    impl Foo for Bar {}
 +
 +    impl<T: Foo> From<T> for Box<dyn Foo> {
 +        fn from(t: T) -> Self {
 +            Box::new(t)
 +        }
 +    }
 +}
 +
 +mod lint_at_item_level {
 +    struct Foo;
 +
 +    #[allow(clippy::use_self)]
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    #[allow(clippy::use_self)]
 +    impl Default for Foo {
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod lint_at_impl_item_level {
 +    struct Foo;
 +
 +    impl Foo {
 +        #[allow(clippy::use_self)]
 +        fn new() -> Foo {
 +            Foo {}
 +        }
 +    }
 +
 +    impl Default for Foo {
 +        #[allow(clippy::use_self)]
 +        fn default() -> Foo {
 +            Foo::new()
 +        }
 +    }
 +}
 +
 +mod issue4734 {
 +    #[repr(C, packed)]
 +    pub struct X {
 +        pub x: u32,
 +    }
 +
 +    impl From<X> for u32 {
 +        fn from(c: X) -> Self {
 +            unsafe { core::mem::transmute(c) }
 +        }
 +    }
 +}
 +
 +mod nested_paths {
 +    use std::convert::Into;
 +    mod submod {
 +        pub struct B;
 +        pub struct C;
 +
 +        impl Into<C> for B {
 +            fn into(self) -> C {
 +                C {}
 +            }
 +        }
 +    }
 +
 +    struct A<T> {
 +        t: T,
 +    }
 +
 +    impl<T> A<T> {
 +        fn new<V: Into<T>>(v: V) -> Self {
 +            Self { t: Into::into(v) }
 +        }
 +    }
 +
 +    impl A<submod::C> {
 +        fn test() -> Self {
 +            A::new::<submod::B>(submod::B {})
 +        }
 +    }
 +}
 +
 +mod issue6818 {
 +    #[derive(serde::Deserialize)]
 +    struct A {
 +        a: i32,
 +    }
 +}
 +
 +mod issue7206 {
 +    struct MyStruct<const C: char>;
 +    impl From<MyStruct<'a'>> for MyStruct<'b'> {
 +        fn from(_s: MyStruct<'a'>) -> Self {
 +            Self
 +        }
 +    }
 +
 +    // keep linting non-`Const` generic args
 +    struct S<'a> {
 +        inner: &'a str,
 +    }
 +
 +    struct S2<T> {
 +        inner: T,
 +    }
 +
 +    impl<T> S2<T> {
 +        fn new() -> Self {
 +            unimplemented!();
 +        }
 +    }
 +
 +    impl<'a> S2<S<'a>> {
 +        fn new_again() -> Self {
 +            S2::new()
 +        }
 +    }
 +}
 +
 +mod self_is_ty_param {
 +    trait Trait {
 +        type Type;
 +        type Hi;
 +
 +        fn test();
 +    }
 +
 +    impl<I> Trait for I
 +    where
 +        I: Iterator,
 +        I::Item: Trait, // changing this to Self would require <Self as Iterator>
 +    {
 +        type Type = I;
 +        type Hi = I::Item;
 +
 +        fn test() {
 +            let _: I::Item;
 +            let _: I; // this could lint, but is questionable
 +        }
 +    }
 +}
 +
 +mod use_self_in_pat {
 +    enum Foo {
 +        Bar,
 +        Baz,
 +    }
 +
 +    impl Foo {
 +        fn do_stuff(self) {
 +            match self {
 +                Foo::Bar => unimplemented!(),
 +                Foo::Baz => unimplemented!(),
 +            }
 +            match Some(1) {
 +                Some(_) => unimplemented!(),
 +                None => unimplemented!(),
 +            }
 +            if let Foo::Bar = self {
 +                unimplemented!()
 +            }
 +        }
 +    }
 +}
++
++mod issue8845 {
++    pub enum Something {
++        Num(u8),
++        TupleNums(u8, u8),
++        StructNums { one: u8, two: u8 },
++    }
++
++    struct Foo(u8);
++
++    struct Bar {
++        x: u8,
++        y: usize,
++    }
++
++    impl Something {
++        fn get_value(&self) -> u8 {
++            match self {
++                Something::Num(n) => *n,
++                Something::TupleNums(n, _m) => *n,
++                Something::StructNums { one, two: _ } => *one,
++            }
++        }
++
++        fn use_crate(&self) -> u8 {
++            match self {
++                crate::issue8845::Something::Num(n) => *n,
++                crate::issue8845::Something::TupleNums(n, _m) => *n,
++                crate::issue8845::Something::StructNums { one, two: _ } => *one,
++            }
++        }
++
++        fn imported_values(&self) -> u8 {
++            use Something::*;
++            match self {
++                Num(n) => *n,
++                TupleNums(n, _m) => *n,
++                StructNums { one, two: _ } => *one,
++            }
++        }
++    }
++
++    impl Foo {
++        fn get_value(&self) -> u8 {
++            let Foo(x) = self;
++            *x
++        }
++
++        fn use_crate(&self) -> u8 {
++            let crate::issue8845::Foo(x) = self;
++            *x
++        }
++    }
++
++    impl Bar {
++        fn get_value(&self) -> u8 {
++            let Bar { x, .. } = self;
++            *x
++        }
++
++        fn use_crate(&self) -> u8 {
++            let crate::issue8845::Bar { x, .. } = self;
++            *x
++        }
++    }
++}
index 34d98618253a6ddcff0ab9e603896326f3a07d92,0000000000000000000000000000000000000000..f06bb959b3bdefe38cd776bf016403187e09b1e4
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,250 @@@
- error: aborting due to 31 previous errors
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:22:21
 +   |
 +LL |         fn new() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +   |
 +   = note: `-D clippy::use-self` implied by `-D warnings`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:23:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:25:22
 +   |
 +LL |         fn test() -> Foo {
 +   |                      ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:26:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:31:25
 +   |
 +LL |         fn default() -> Foo {
 +   |                         ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:32:13
 +   |
 +LL |             Foo::new()
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:97:24
 +   |
 +LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +   |                        ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:97:55
 +   |
 +LL |         fn bad(foos: &[Foo]) -> impl Iterator<Item = &Foo> {
 +   |                                                       ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:112:13
 +   |
 +LL |             TS(0)
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:147:29
 +   |
 +LL |                 fn bar() -> Bar {
 +   |                             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:148:21
 +   |
 +LL |                     Bar { foo: Foo {} }
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:159:21
 +   |
 +LL |         fn baz() -> Foo {
 +   |                     ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:160:13
 +   |
 +LL |             Foo {}
 +   |             ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:177:21
 +   |
 +LL |             let _ = Enum::B(42);
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:178:21
 +   |
 +LL |             let _ = Enum::C { field: true };
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:179:21
 +   |
 +LL |             let _ = Enum::A;
 +   |                     ^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:221:13
 +   |
 +LL |             nested::A::fun_1();
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:222:13
 +   |
 +LL |             nested::A::A;
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:224:13
 +   |
 +LL |             nested::A {};
 +   |             ^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:243:13
 +   |
 +LL |             TestStruct::from_something()
 +   |             ^^^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:257:25
 +   |
 +LL |         async fn g() -> S {
 +   |                         ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:258:13
 +   |
 +LL |             S {}
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:262:16
 +   |
 +LL |             &p[S::A..S::B]
 +   |                ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:262:22
 +   |
 +LL |             &p[S::A..S::B]
 +   |                      ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:285:29
 +   |
 +LL |         fn foo(value: T) -> Foo<T> {
 +   |                             ^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:286:13
 +   |
 +LL |             Foo::<T> { value }
 +   |             ^^^^^^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:458:13
 +   |
 +LL |             A::new::<submod::B>(submod::B {})
 +   |             ^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:495:13
 +   |
 +LL |             S2::new()
 +   |             ^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:532:17
 +   |
 +LL |                 Foo::Bar => unimplemented!(),
 +   |                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:533:17
 +   |
 +LL |                 Foo::Baz => unimplemented!(),
 +   |                 ^^^ help: use the applicable keyword: `Self`
 +
 +error: unnecessary structure name repetition
 +  --> $DIR/use_self.rs:539:20
 +   |
 +LL |             if let Foo::Bar = self {
 +   |                    ^^^ help: use the applicable keyword: `Self`
 +
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:563:17
++   |
++LL |                 Something::Num(n) => *n,
++   |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:564:17
++   |
++LL |                 Something::TupleNums(n, _m) => *n,
++   |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:565:17
++   |
++LL |                 Something::StructNums { one, two: _ } => *one,
++   |                 ^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:571:17
++   |
++LL |                 crate::issue8845::Something::Num(n) => *n,
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:572:17
++   |
++LL |                 crate::issue8845::Something::TupleNums(n, _m) => *n,
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:573:17
++   |
++LL |                 crate::issue8845::Something::StructNums { one, two: _ } => *one,
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:589:17
++   |
++LL |             let Foo(x) = self;
++   |                 ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:594:17
++   |
++LL |             let crate::issue8845::Foo(x) = self;
++   |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:601:17
++   |
++LL |             let Bar { x, .. } = self;
++   |                 ^^^ help: use the applicable keyword: `Self`
++
++error: unnecessary structure name repetition
++  --> $DIR/use_self.rs:606:17
++   |
++LL |             let crate::issue8845::Bar { x, .. } = self;
++   |                 ^^^^^^^^^^^^^^^^^^^^^ help: use the applicable keyword: `Self`
++
++error: aborting due to 41 previous errors
 +
index bf4ce79b2cbc90334fbebad3bc1fbe9ddd8288e2,0000000000000000000000000000000000000000..6909fbcae09ff7c2d89fd748d140aee29ac8dbb9
mode 100644,000000..100644
--- /dev/null
@@@ -1,371 -1,0 +1,386 @@@
-     if (theme == "ayu") {
-         enableAyu = true;
-     } else if (theme == "coal" || theme == "navy") {
-         enableNight = true;
-     } else if (theme == "rust") {
-         enableHighlight = true;
-     } else {
-         enableHighlight = true;
-         // this makes sure that an unknown theme request gets set to a known one
-         theme = "light";
 +(function () {
 +    var md = window.markdownit({
 +        html: true,
 +        linkify: true,
 +        typographer: true,
 +        highlight: function (str, lang) {
 +            if (lang && hljs.getLanguage(lang)) {
 +                try {
 +                    return '<pre class="hljs"><code>' +
 +                        hljs.highlight(lang, str, true).value +
 +                        '</code></pre>';
 +                } catch (__) {}
 +            }
 +
 +            return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>';
 +        }
 +    });
 +
 +    function scrollToLint(lintId) {
 +        var target = document.getElementById(lintId);
 +        if (!target) {
 +            return;
 +        }
 +        target.scrollIntoView();
 +    }
 +
 +    function scrollToLintByURL($scope) {
 +        var removeListener = $scope.$on('ngRepeatFinished', function(ngRepeatFinishedEvent) {
 +            scrollToLint(window.location.hash.slice(1));
 +            removeListener();
 +        });
 +    }
 +
 +    function selectGroup($scope, selectedGroup) {
 +        var groups = $scope.groups;
 +        for (var group in groups) {
 +            if (groups.hasOwnProperty(group)) {
 +                if (group === selectedGroup) {
 +                    groups[group] = true;
 +                } else {
 +                    groups[group] = false;
 +                }
 +            }
 +        }
 +    }
 +
 +    angular.module("clippy", [])
 +        .filter('markdown', function ($sce) {
 +            return function (text) {
 +                return $sce.trustAsHtml(
 +                    md.render(text || '')
 +                        // Oh deer, what a hack :O
 +                        .replace('<table', '<table class="table"')
 +                );
 +            };
 +        })
 +        .directive('themeDropdown', function ($document) {
 +            return {
 +                restrict: 'A',
 +                link: function ($scope, $element, $attr) {
 +                    $element.bind('click', function () {
 +                        $element.toggleClass('open');
 +                        $element.addClass('open-recent');
 +                    });
 +
 +                    $document.bind('click', function () {
 +                        if (!$element.hasClass('open-recent')) {
 +                            $element.removeClass('open');
 +                        }
 +                        $element.removeClass('open-recent');
 +                    })
 +                }
 +            }
 +        })
 +        .directive('filterDropdown', function ($document) {
 +            return {
 +                restrict: 'A',
 +                link: function ($scope, $element, $attr) {
 +                    $element.bind('click', function (event) {
 +                        if (event.target.closest('button')) {
 +                            $element.toggleClass('open');
 +                        } else {
 +                            $element.addClass('open');
 +                        }
 +                        $element.addClass('open-recent');
 +                    });
 +
 +                    $document.bind('click', function () {
 +                        if (!$element.hasClass('open-recent')) {
 +                            $element.removeClass('open');
 +                        }
 +                        $element.removeClass('open-recent');
 +                    })
 +                }
 +            }
 +        })
 +        .directive('onFinishRender', function ($timeout) {
 +            return {
 +                restrict: 'A',
 +                link: function (scope, element, attr) {
 +                    if (scope.$last === true) {
 +                        $timeout(function () {
 +                            scope.$emit(attr.onFinishRender);
 +                        });
 +                    }
 +                }
 +            };
 +        })
 +        .controller("lintList", function ($scope, $http, $timeout) {
 +            // Level filter
 +            var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
 +            $scope.levels = LEVEL_FILTERS_DEFAULT;
 +            $scope.byLevels = function (lint) {
 +                return $scope.levels[lint.level];
 +            };
 +
 +            var GROUPS_FILTER_DEFAULT = {
 +                cargo: true,
 +                complexity: true,
 +                correctness: true,
 +                deprecated: false,
 +                nursery: true,
 +                pedantic: true,
 +                perf: true,
 +                restriction: true,
 +                style: true,
 +                suspicious: true,
 +            };
 +            $scope.groups = GROUPS_FILTER_DEFAULT;
 +            const THEMES_DEFAULT = {
 +                light: "Light",
 +                rust: "Rust",
 +                coal: "Coal",
 +                navy: "Navy",
 +                ayu: "Ayu"
 +            };
 +            $scope.themes = THEMES_DEFAULT;
 +
 +            $scope.versionFilters = {
 +                "≥": {enabled: false, minorVersion: null },
 +                "≤": {enabled: false, minorVersion: null },
 +                "=": {enabled: false, minorVersion: null },
 +            };
 +
 +            $scope.selectTheme = function (theme) {
 +                setTheme(theme, true);
 +            }
 +
 +            $scope.toggleLevels = function (value) {
 +                const levels = $scope.levels;
 +                for (const key in levels) {
 +                    if (levels.hasOwnProperty(key)) {
 +                        levels[key] = value;
 +                    }
 +                }
 +            };
 +
 +            $scope.toggleGroups = function (value) {
 +                const groups = $scope.groups;
 +                for (const key in groups) {
 +                    if (groups.hasOwnProperty(key)) {
 +                        groups[key] = value;
 +                    }
 +                }
 +            };
 +
 +            $scope.selectedValuesCount = function (obj) {
 +                return Object.values(obj).filter(x => x).length;
 +            }
 +
 +            $scope.clearVersionFilters = function () {
 +                for (let filter in $scope.versionFilters) {
 +                    $scope.versionFilters[filter] = { enabled: false, minorVersion: null };
 +                }
 +            }
 +
 +            $scope.versionFilterCount = function(obj) {
 +                return Object.values(obj).filter(x => x.enabled).length;
 +            }
 +
 +            $scope.updateVersionFilters = function() {
 +                for (const filter in $scope.versionFilters) {
 +                    let minorVersion = $scope.versionFilters[filter].minorVersion;
 +
 +                    // 1.29.0 and greater
 +                    if (minorVersion && minorVersion > 28) {
 +                        $scope.versionFilters[filter].enabled = true;
 +                        continue;
 +                    }
 +
 +                    $scope.versionFilters[filter].enabled = false;
 +                }
 +            }
 +
 +            $scope.byVersion = function(lint) {
 +                let filters = $scope.versionFilters;
 +                for (const filter in filters) {
 +                    if (filters[filter].enabled) {
 +                        let minorVersion = filters[filter].minorVersion;
 +
 +                        // Strip the "pre " prefix for pre 1.29.0 lints
 +                        let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version;
 +                        let lintMinorVersion = lintVersion.substring(2, 4);
 +
 +                        switch (filter) {
 +                            // "=" gets the highest priority, since all filters are inclusive
 +                            case "=":
 +                                return (lintMinorVersion == minorVersion);
 +                            case "≥":
 +                                if (lintMinorVersion < minorVersion) { return false; }
 +                                break;
 +                            case "≤":
 +                                if (lintMinorVersion > minorVersion) { return false; }
 +                                break;
 +                            default:
 +                                return true
 +                        }
 +                    }
 +                }
 +
 +                return true;
 +            }
 +
 +            $scope.byGroups = function (lint) {
 +                return $scope.groups[lint.group];
 +            };
 +
 +            $scope.bySearch = function (lint, index, array) {
 +                let searchStr = $scope.search;
 +                // It can be `null` I haven't missed this value
 +                if (searchStr == null || searchStr.length < 3) {
 +                    return true;
 +                }
 +                searchStr = searchStr.toLowerCase();
++                if (searchStr.startsWith("clippy::")) {
++                    searchStr = searchStr.slice(8);
++                }
 +
 +                // Search by id
 +                if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) {
 +                    return true;
 +                }
 +
 +                // Search the description
 +                // The use of `for`-loops instead of `foreach` enables us to return early
 +                let terms = searchStr.split(" ");
 +                let docsLowerCase = lint.docs.toLowerCase();
 +                for (index = 0; index < terms.length; index++) {
 +                    // This is more likely and will therefor be checked first
 +                    if (docsLowerCase.indexOf(terms[index]) !== -1) {
 +                        continue;
 +                    }
 +
 +                    if (lint.id.indexOf(terms[index]) !== -1) {
 +                        continue;
 +                    }
 +
 +                    return false;
 +                }
 +
 +                return true;
 +            }
 +
 +            $scope.copyToClipboard = function (lint) {
 +                const clipboard = document.getElementById("clipboard-" + lint.id);
 +                if (clipboard) {
 +                    let resetClipboardTimeout = null;
 +                    let resetClipboardIcon = clipboard.innerHTML;
 +
 +                    function resetClipboard() {
 +                        resetClipboardTimeout = null;
 +                        clipboard.innerHTML = resetClipboardIcon;
 +                    }
 +
 +                    navigator.clipboard.writeText("clippy::" + lint.id);
 +
 +                    clipboard.innerHTML = "&#10003;";
 +                    if (resetClipboardTimeout !== null) {
 +                        clearTimeout(resetClipboardTimeout);
 +                    }
 +                    resetClipboardTimeout = setTimeout(resetClipboard, 1000);
 +                }
 +            }
 +
 +            // Get data
 +            $scope.open = {};
 +            $scope.loading = true;
 +            // This will be used to jump into the source code of the version that this documentation is for.
 +            $scope.docVersion = window.location.pathname.split('/')[2] || "master";
 +
 +            if (window.location.hash.length > 1) {
 +                $scope.search = window.location.hash.slice(1);
 +                $scope.open[window.location.hash.slice(1)] = true;
 +                scrollToLintByURL($scope);
 +            }
 +
 +            $http.get('./lints.json')
 +                .success(function (data) {
 +                    $scope.data = data;
 +                    $scope.loading = false;
 +
 +                    var selectedGroup = getQueryVariable("sel");
 +                    if (selectedGroup) {
 +                        selectGroup($scope, selectedGroup.toLowerCase());
 +                    }
 +
 +                    scrollToLintByURL($scope);
 +
 +                    setTimeout(function () {
 +                        var el = document.getElementById('filter-input');
 +                        if (el) { el.focus() }
 +                    }, 0);
 +                })
 +                .error(function (data) {
 +                    $scope.error = data;
 +                    $scope.loading = false;
 +                });
 +
 +            window.addEventListener('hashchange', function () {
 +                // trigger re-render
 +                $timeout(function () {
 +                    $scope.levels = LEVEL_FILTERS_DEFAULT;
 +                    $scope.search = window.location.hash.slice(1);
 +                    $scope.open[window.location.hash.slice(1)] = true;
 +
 +                    scrollToLintByURL($scope);
 +                });
 +                return true;
 +            }, false);
 +        });
 +})();
 +
 +function getQueryVariable(variable) {
 +    var query = window.location.search.substring(1);
 +    var vars = query.split('&');
 +    for (var i = 0; i < vars.length; i++) {
 +        var pair = vars[i].split('=');
 +        if (decodeURIComponent(pair[0]) == variable) {
 +            return decodeURIComponent(pair[1]);
 +        }
 +    }
 +}
 +
 +function setTheme(theme, store) {
 +    let enableHighlight = false;
 +    let enableNight = false;
 +    let enableAyu = false;
 +
- setTheme(localStorage.getItem('clippy-lint-list-theme'), false);
++    switch(theme) {
++        case "ayu":
++            enableAyu = true;
++            break;
++        case "coal":
++        case "navy":
++            enableNight = true;
++            break;
++        case "rust":
++            enableHighlight = true;
++            break;
++        default:
++            enableHighlight = true;
++            theme = "light";
++            break;
 +    }
++
 +    document.getElementsByTagName("body")[0].className = theme;
 +
 +    document.getElementById("styleHighlight").disabled = !enableHighlight;
 +    document.getElementById("styleNight").disabled = !enableNight;
 +    document.getElementById("styleAyu").disabled = !enableAyu;
 +
 +    if (store) {
 +        try {
 +            localStorage.setItem('clippy-lint-list-theme', theme);
 +        } catch (e) { }
 +    }
 +}
 +
 +// loading the theme after the initial load
++const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
++const theme = localStorage.getItem('clippy-lint-list-theme');
++if (prefersDark.matches && !theme) {
++    setTheme("coal", false);
++} else {
++    setTheme(theme, false);
++}