]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'e329249b6a3a98830d860c74c8234a8dd9407436' into clippyup
authorflip1995 <hello@philkrones.com>
Sat, 26 Feb 2022 13:26:21 +0000 (14:26 +0100)
committerflip1995 <hello@philkrones.com>
Sat, 26 Feb 2022 13:26:21 +0000 (14:26 +0100)
106 files changed:
1  2 
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
src/tools/clippy/clippy_lints/src/cargo/feature_name.rs
src/tools/clippy/clippy_lints/src/cargo/mod.rs
src/tools/clippy/clippy_lints/src/cargo/multiple_crate_versions.rs
src/tools/clippy/clippy_lints/src/cargo/wildcard_dependencies.rs
src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/casts/utils.rs
src/tools/clippy/clippy_lints/src/dbg_macro.rs
src/tools/clippy/clippy_lints/src/default.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/duration_subsec.rs
src/tools/clippy/clippy_lints/src/eq_op.rs
src/tools/clippy/clippy_lints/src/eta_reduction.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/format_impl.rs
src/tools/clippy/clippy_lints/src/infinite_iter.rs
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_cargo.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_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/manual_bits.rs
src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
src/tools/clippy/clippy_lints/src/mem_forget.rs
src/tools/clippy/clippy_lints/src/mem_replace.rs
src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
src/tools/clippy/clippy_lints/src/minmax.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/redundant_clone.rs
src/tools/clippy/clippy_lints/src/redundant_slicing.rs
src/tools/clippy/clippy_lints/src/size_of_in_element_count.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/transmute_undefined_repr.rs
src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/await_holding_lock.rs
src/tools/clippy/tests/ui/await_holding_lock.stderr
src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
src/tools/clippy/tests/ui/cast.rs
src/tools/clippy/tests/ui/cast.stderr
src/tools/clippy/tests/ui/dbg_macro.rs
src/tools/clippy/tests/ui/dbg_macro.stderr
src/tools/clippy/tests/ui/default_trait_access.fixed
src/tools/clippy/tests/ui/default_trait_access.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/eta.fixed
src/tools/clippy/tests/ui/eta.rs
src/tools/clippy/tests/ui/large_enum_variant.rs
src/tools/clippy/tests/ui/large_enum_variant.stderr
src/tools/clippy/tests/ui/match_as_ref.fixed
src/tools/clippy/tests/ui/match_as_ref.rs
src/tools/clippy/tests/ui/match_bool.rs
src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
src/tools/clippy/tests/ui/match_same_arms2.rs
src/tools/clippy/tests/ui/match_single_binding.fixed
src/tools/clippy/tests/ui/match_single_binding.rs
src/tools/clippy/tests/ui/match_single_binding.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/new_without_default.stderr
src/tools/clippy/tests/ui/print_in_format_impl.rs
src/tools/clippy/tests/ui/print_in_format_impl.stderr
src/tools/clippy/tests/ui/ptr_arg.rs
src/tools/clippy/tests/ui/ptr_as_ptr.fixed
src/tools/clippy/tests/ui/ptr_as_ptr.rs
src/tools/clippy/tests/ui/ptr_as_ptr.stderr
src/tools/clippy/tests/ui/recursive_format_impl.rs
src/tools/clippy/tests/ui/recursive_format_impl.stderr
src/tools/clippy/tests/ui/redundant_slicing.fixed
src/tools/clippy/tests/ui/redundant_slicing.rs
src/tools/clippy/tests/ui/redundant_slicing.stderr
src/tools/clippy/tests/ui/rename.fixed
src/tools/clippy/tests/ui/rename.rs
src/tools/clippy/tests/ui/rename.stderr
src/tools/clippy/tests/ui/single_match.rs
src/tools/clippy/tests/ui/transmute_undefined_repr.rs
src/tools/clippy/tests/ui/transmute_undefined_repr.stderr

index c9adf77c0d639a0560073ea4889d32b06bb60344,0000000000000000000000000000000000000000..35983b7fb506f7799cd2f6e6549061bf5b64e868
mode 100644,000000..100644
--- /dev/null
@@@ -1,3563 -1,0 +1,3566 @@@
- * [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +# 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
 +
 +[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master)
 +
 +## Rust 1.59 (beta)
 +
 +Current beta, release 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
 +
 +Current stable, 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`]: avoid linting when calling `to_string()` on anything that is not `self`
++* `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`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
++* `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
 +[`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
 +[`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_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
 +[`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_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
 +[`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_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_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
 +[`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
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`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
 +[`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
 +[`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_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_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_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`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_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
 +[`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
 +[`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_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_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
 +[`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_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_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_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_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`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
 +[`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
 +[`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_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_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
 +[`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
 +[`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
 +[`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_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 +[`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
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`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
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`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_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_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`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
 +[`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
 +[`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_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_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_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_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`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
 +[`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_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
 +[`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
 +[`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
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`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
 +[`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
 +[`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_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`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_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_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
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index e445889a58f77d7b4db6354b2ad7aa3c4a5a9e3f,0000000000000000000000000000000000000000..5cc5530f874dd63e275f54d8832bf33ffd003e44
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,65 @@@
- version = "0.1.60"
 +[package]
 +name = "clippy"
++version = "0.1.61"
 +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 = { version = "0.1", path = "clippy_lints" }
 +semver = "1.0"
 +rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
 +tempfile = { version = "3.2", optional = true }
 +
 +[dev-dependencies]
 +cargo_metadata = "0.14"
 +compiletest_rs = { version = "0.7.1", 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
 +clippy_utils = { path = "clippy_utils" }
 +derive-new = "0.5"
 +if_chain = "1.0"
 +itertools = "0.10"
 +quote = "1.0"
 +serde = { version = "1.0", features = ["derive"] }
 +syn = { version = "1.0", features = ["full"] }
 +futures = "0.3"
 +parking_lot = "0.11.2"
 +tokio = { version = "1", features = ["io-util"] }
++num_cpus = "1.13"
 +
 +[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 2053ca64ba23d19626dfdbaaebd826f70d82a4db,0000000000000000000000000000000000000000..40d7dd702628f77c47475290ad312c3e5b0160d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,37 -1,0 +1,37 @@@
- version = "0.1.60"
 +[package]
 +name = "clippy_lints"
++version = "0.1.61"
 +description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 +repository = "https://github.com/rust-lang/rust-clippy"
 +readme = "README.md"
 +license = "MIT OR Apache-2.0"
 +keywords = ["clippy", "lint", "plugin"]
 +edition = "2021"
 +
 +[dependencies]
 +cargo_metadata = "0.14"
 +clippy_utils = { path = "../clippy_utils" }
 +if_chain = "1.0"
 +itertools = "0.10"
 +pulldown-cmark = { version = "0.9", default-features = false }
 +quine-mc_cluskey = "0.2"
 +regex-syntax = "0.6"
 +serde = { version = "1.0", features = ["derive"] }
 +serde_json = { version = "1.0", optional = true }
 +toml = "0.5"
 +unicode-normalization = "0.1"
 +unicode-script = { version = "0.5", default-features = false }
 +semver = "1.0"
 +rustc-semver = "1.1"
 +# NOTE: cargo requires serde feat in its url dep
 +# see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 +url = { version = "2.2", features = ["serde"] }
 +
 +[features]
 +deny-warnings = ["clippy_utils/deny-warnings"]
 +# build clippy with internal lints enabled, off by default
 +internal = ["clippy_utils/internal", "serde_json"]
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 1cc3418d4748c98728116538fba64a80949abefc,0000000000000000000000000000000000000000..f0979840ff8d8ab9d55d0a1426b5905ee10d23ec
mode 100644,000000..100644
--- /dev/null
@@@ -1,155 -1,0 +1,200 @@@
- use clippy_utils::diagnostics::span_lint_and_note;
++use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::{match_def_path, paths};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::GeneratorInteriorTypeCause;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// Checks for calls to await while holding a
-     /// non-async-aware MutexGuard.
++    /// Checks for calls to await while holding a non-async-aware MutexGuard.
 +    ///
 +    /// ### Why is this bad?
 +    /// The Mutex types found in std::sync and parking_lot
 +    /// are not designed to operate in an async context across await points.
 +    ///
 +    /// There are two potential solutions. One is to use an async-aware Mutex
 +    /// type. Many asynchronous foundation crates provide such a Mutex type. The
 +    /// other solution is to ensure the mutex is unlocked before calling await,
 +    /// either by introducing a scope or an explicit call to Drop::drop.
 +    ///
 +    /// ### Known problems
-     /// Will report false positive for explicitly dropped guards ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)).
++    /// Will report false positive for explicitly dropped guards
++    /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
++    /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
 +    ///
 +    /// ### Example
-     /// ```rust,ignore
-     /// use std::sync::Mutex;
-     ///
++    /// ```rust
++    /// # use std::sync::Mutex;
++    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
-     ///   let guard = x.lock().unwrap();
++    ///   let mut guard = x.lock().unwrap();
 +    ///   *guard += 1;
-     ///   bar.await;
++    ///   baz().await;
++    /// }
++    ///
++    /// async fn bar(x: &Mutex<u32>) {
++    ///   let mut guard = x.lock().unwrap();
++    ///   *guard += 1;
++    ///   drop(guard); // explicit drop
++    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
-     /// ```rust,ignore
-     /// use std::sync::Mutex;
-     ///
++    /// ```rust
++    /// # use std::sync::Mutex;
++    /// # async fn baz() {}
 +    /// async fn foo(x: &Mutex<u32>) {
 +    ///   {
-     ///     let guard = x.lock().unwrap();
++    ///     let mut guard = x.lock().unwrap();
 +    ///     *guard += 1;
 +    ///   }
-     ///   bar.await;
++    ///   baz().await;
++    /// }
++    ///
++    /// async fn bar(x: &Mutex<u32>) {
++    ///   {
++    ///     let mut guard = x.lock().unwrap();
++    ///     *guard += 1;
++    ///   } // guard dropped here at end of scope
++    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub AWAIT_HOLDING_LOCK,
-     pedantic,
-     "Inside an async function, holding a MutexGuard while calling await"
++    suspicious,
++    "inside an async function, holding a `MutexGuard` while calling `await`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// Checks for calls to await while holding a
-     /// `RefCell` `Ref` or `RefMut`.
++    /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `RefCell` refs only check for exclusive mutable access
 +    /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
 +    /// risks panics from a mutable ref shared while other refs are outstanding.
 +    ///
 +    /// ### Known problems
-     /// Will report false positive for explicitly dropped refs ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)).
++    /// Will report false positive for explicitly dropped refs
++    /// ([#6353](https://github.com/rust-lang/rust-clippy/issues/6353)). A workaround for this is
++    /// to wrap the `.borrow[_mut]()` call in a block instead of explicitly dropping the ref.
 +    ///
 +    /// ### Example
-     /// ```rust,ignore
-     /// use std::cell::RefCell;
-     ///
++    /// ```rust
++    /// # use std::cell::RefCell;
++    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   let mut y = x.borrow_mut();
 +    ///   *y += 1;
-     ///   bar.await;
++    ///   baz().await;
++    /// }
++    ///
++    /// async fn bar(x: &RefCell<u32>) {
++    ///   let mut y = x.borrow_mut();
++    ///   *y += 1;
++    ///   drop(y); // explicit drop
++    ///   baz().await;
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
-     /// ```rust,ignore
-     /// use std::cell::RefCell;
-     ///
++    /// ```rust
++    /// # use std::cell::RefCell;
++    /// # async fn baz() {}
 +    /// async fn foo(x: &RefCell<u32>) {
 +    ///   {
 +    ///      let mut y = x.borrow_mut();
 +    ///      *y += 1;
 +    ///   }
-     ///   bar.await;
++    ///   baz().await;
++    /// }
++    ///
++    /// async fn bar(x: &RefCell<u32>) {
++    ///   {
++    ///     let mut y = x.borrow_mut();
++    ///     *y += 1;
++    ///   } // y dropped here at end of scope
++    ///   baz().await;
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub AWAIT_HOLDING_REFCELL_REF,
-     pedantic,
-     "Inside an async function, holding a RefCell ref while calling await"
++    suspicious,
++    "inside an async function, holding a `RefCell` ref while calling `await`"
 +}
 +
 +declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
 +
 +impl LateLintPass<'_> for AwaitHolding {
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
 +        use AsyncGeneratorKind::{Block, Closure, Fn};
 +        if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
 +            let body_id = BodyId {
 +                hir_id: body.value.hir_id,
 +            };
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            check_interior_types(
 +                cx,
 +                typeck_results.generator_interior_types.as_ref().skip_binder(),
 +                body.value.span,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_interior_types(cx: &LateContext<'_>, ty_causes: &[GeneratorInteriorTypeCause<'_>], span: Span) {
 +    for ty_cause in ty_causes {
 +        if let rustc_middle::ty::Adt(adt, _) = ty_cause.ty.kind() {
 +            if is_mutex_guard(cx, adt.did) {
-                 span_lint_and_note(
++                span_lint_and_then(
 +                    cx,
 +                    AWAIT_HOLDING_LOCK,
 +                    ty_cause.span,
-                     "this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await",
-                     ty_cause.scope_span.or(Some(span)),
-                     "these are all the await points this lock is held through",
++                    "this `MutexGuard` is held across an `await` point",
++                    |diag| {
++                        diag.help(
++                            "consider using an async-aware `Mutex` type or ensuring the \
++                                `MutexGuard` is dropped before calling await",
++                        );
++                        diag.span_note(
++                            ty_cause.scope_span.unwrap_or(span),
++                            "these are all the `await` points this lock is held through",
++                        );
++                    },
 +                );
 +            }
 +            if is_refcell_ref(cx, adt.did) {
-                 span_lint_and_note(
++                span_lint_and_then(
 +                    cx,
 +                    AWAIT_HOLDING_REFCELL_REF,
 +                    ty_cause.span,
-                     "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await",
-                     ty_cause.scope_span.or(Some(span)),
-                     "these are all the await points this ref is held through",
++                    "this `RefCell` reference is held across an `await` point",
++                    |diag| {
++                        diag.help("ensure the reference is dropped before calling `await`");
++                        diag.span_note(
++                            ty_cause.scope_span.unwrap_or(span),
++                            "these are all the `await` points this reference is held through",
++                        );
++                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::RWLOCK_WRITE_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
 +        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
 +}
 +
 +fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e0442dda479d75c89023dde50eb2ca669a955356
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,54 @@@
++//! lint on missing cargo common metadata
++
++use cargo_metadata::Metadata;
++use clippy_utils::diagnostics::span_lint;
++use rustc_lint::LateContext;
++use rustc_span::source_map::DUMMY_SP;
++
++use super::CARGO_COMMON_METADATA;
++
++pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata, ignore_publish: bool) {
++    for package in &metadata.packages {
++        // only run the lint if publish is `None` (`publish = true` or skipped entirely)
++        // or if the vector isn't empty (`publish = ["something"]`)
++        if package.publish.as_ref().filter(|publish| publish.is_empty()).is_none() || ignore_publish {
++            if is_empty_str(&package.description) {
++                missing_warning(cx, package, "package.description");
++            }
++
++            if is_empty_str(&package.license) && is_empty_str(&package.license_file) {
++                missing_warning(cx, package, "either package.license or package.license_file");
++            }
++
++            if is_empty_str(&package.repository) {
++                missing_warning(cx, package, "package.repository");
++            }
++
++            if is_empty_str(&package.readme) {
++                missing_warning(cx, package, "package.readme");
++            }
++
++            if is_empty_vec(&package.keywords) {
++                missing_warning(cx, package, "package.keywords");
++            }
++
++            if is_empty_vec(&package.categories) {
++                missing_warning(cx, package, "package.categories");
++            }
++        }
++    }
++}
++
++fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, field: &str) {
++    let message = format!("package `{}` is missing `{}` metadata", package.name, field);
++    span_lint(cx, CARGO_COMMON_METADATA, DUMMY_SP, &message);
++}
++
++fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: &Option<T>) -> bool {
++    value.as_ref().map_or(true, |s| s.as_ref().is_empty())
++}
++
++fn is_empty_vec(value: &[String]) -> bool {
++    // This works because empty iterators return true
++    value.iter().all(String::is_empty)
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..79a469a4258bbcc4643153e2d84eb64a814becfc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,92 @@@
++use cargo_metadata::Metadata;
++use clippy_utils::diagnostics::span_lint_and_help;
++use rustc_lint::LateContext;
++use rustc_span::source_map::DUMMY_SP;
++
++use super::{NEGATIVE_FEATURE_NAMES, REDUNDANT_FEATURE_NAMES};
++
++static PREFIXES: [&str; 8] = ["no-", "no_", "not-", "not_", "use-", "use_", "with-", "with_"];
++static SUFFIXES: [&str; 2] = ["-support", "_support"];
++
++pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
++    for package in &metadata.packages {
++        let mut features: Vec<&String> = package.features.keys().collect();
++        features.sort();
++        for feature in features {
++            let prefix_opt = {
++                let i = PREFIXES.partition_point(|prefix| prefix < &feature.as_str());
++                if i > 0 && feature.starts_with(PREFIXES[i - 1]) {
++                    Some(PREFIXES[i - 1])
++                } else {
++                    None
++                }
++            };
++            if let Some(prefix) = prefix_opt {
++                lint(cx, feature, prefix, true);
++            }
++
++            let suffix_opt: Option<&str> = {
++                let i = SUFFIXES.partition_point(|suffix| {
++                    suffix.bytes().rev().cmp(feature.bytes().rev()) == std::cmp::Ordering::Less
++                });
++                if i > 0 && feature.ends_with(SUFFIXES[i - 1]) {
++                    Some(SUFFIXES[i - 1])
++                } else {
++                    None
++                }
++            };
++            if let Some(suffix) = suffix_opt {
++                lint(cx, feature, suffix, false);
++            }
++        }
++    }
++}
++
++fn is_negative_prefix(s: &str) -> bool {
++    s.starts_with("no")
++}
++
++fn lint(cx: &LateContext<'_>, feature: &str, substring: &str, is_prefix: bool) {
++    let is_negative = is_prefix && is_negative_prefix(substring);
++    span_lint_and_help(
++        cx,
++        if is_negative {
++            NEGATIVE_FEATURE_NAMES
++        } else {
++            REDUNDANT_FEATURE_NAMES
++        },
++        DUMMY_SP,
++        &format!(
++            "the \"{}\" {} in the feature name \"{}\" is {}",
++            substring,
++            if is_prefix { "prefix" } else { "suffix" },
++            feature,
++            if is_negative { "negative" } else { "redundant" }
++        ),
++        None,
++        &format!(
++            "consider renaming the feature to \"{}\"{}",
++            if is_prefix {
++                feature.strip_prefix(substring)
++            } else {
++                feature.strip_suffix(substring)
++            }
++            .unwrap(),
++            if is_negative {
++                ", but make sure the feature adds functionality"
++            } else {
++                ""
++            }
++        ),
++    );
++}
++
++#[test]
++fn test_prefixes_sorted() {
++    let mut sorted_prefixes = PREFIXES;
++    sorted_prefixes.sort_unstable();
++    assert_eq!(PREFIXES, sorted_prefixes);
++    let mut sorted_suffixes = SUFFIXES;
++    sorted_suffixes.sort_by(|a, b| a.bytes().rev().cmp(b.bytes().rev()));
++    assert_eq!(SUFFIXES, sorted_suffixes);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..abe95c6663f70eb6a614c4335fc96dfbeec2f356
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,221 @@@
++use cargo_metadata::MetadataCommand;
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::is_lint_allowed;
++use rustc_hir::hir_id::CRATE_HIR_ID;
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::DUMMY_SP;
++
++mod common_metadata;
++mod feature_name;
++mod multiple_crate_versions;
++mod wildcard_dependencies;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks to see if all common metadata is defined in
++    /// `Cargo.toml`. See: https://rust-lang-nursery.github.io/api-guidelines/documentation.html#cargotoml-includes-all-common-metadata-c-metadata
++    ///
++    /// ### Why is this bad?
++    /// It will be more difficult for users to discover the
++    /// purpose of the crate, and key information related to it.
++    ///
++    /// ### Example
++    /// ```toml
++    /// # This `Cargo.toml` is missing a description field:
++    /// [package]
++    /// name = "clippy"
++    /// version = "0.0.212"
++    /// 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"]
++    /// ```
++    ///
++    /// Should include a description field like:
++    ///
++    /// ```toml
++    /// # This `Cargo.toml` includes all common metadata
++    /// [package]
++    /// name = "clippy"
++    /// version = "0.0.212"
++    /// 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"]
++    /// ```
++    #[clippy::version = "1.32.0"]
++    pub CARGO_COMMON_METADATA,
++    cargo,
++    "common metadata is defined in `Cargo.toml`"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
++    ///
++    /// ### Why is this bad?
++    /// These prefixes and suffixes have no significant meaning.
++    ///
++    /// ### Example
++    /// ```toml
++    /// # The `Cargo.toml` with feature name redundancy
++    /// [features]
++    /// default = ["use-abc", "with-def", "ghi-support"]
++    /// use-abc = []  // redundant
++    /// with-def = []   // redundant
++    /// ghi-support = []   // redundant
++    /// ```
++    ///
++    /// Use instead:
++    /// ```toml
++    /// [features]
++    /// default = ["abc", "def", "ghi"]
++    /// abc = []
++    /// def = []
++    /// ghi = []
++    /// ```
++    ///
++    #[clippy::version = "1.57.0"]
++    pub REDUNDANT_FEATURE_NAMES,
++    cargo,
++    "usage of a redundant feature name"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for negative feature names with prefix `no-` or `not-`
++    ///
++    /// ### Why is this bad?
++    /// Features are supposed to be additive, and negatively-named features violate it.
++    ///
++    /// ### Example
++    /// ```toml
++    /// # The `Cargo.toml` with negative feature names
++    /// [features]
++    /// default = []
++    /// no-abc = []
++    /// not-def = []
++    ///
++    /// ```
++    /// Use instead:
++    /// ```toml
++    /// [features]
++    /// default = ["abc", "def"]
++    /// abc = []
++    /// def = []
++    ///
++    /// ```
++    #[clippy::version = "1.57.0"]
++    pub NEGATIVE_FEATURE_NAMES,
++    cargo,
++    "usage of a negative feature name"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks to see if multiple versions of a crate are being
++    /// used.
++    ///
++    /// ### Why is this bad?
++    /// This bloats the size of targets, and can lead to
++    /// confusing error messages when structs or traits are used interchangeably
++    /// between different versions of a crate.
++    ///
++    /// ### Known problems
++    /// Because this can be caused purely by the dependencies
++    /// themselves, it's not always possible to fix this issue.
++    ///
++    /// ### Example
++    /// ```toml
++    /// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
++    /// [dependencies]
++    /// ctrlc = "=3.1.0"
++    /// ansi_term = "=0.11.0"
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub MULTIPLE_CRATE_VERSIONS,
++    cargo,
++    "multiple versions of the same crate being used"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for wildcard dependencies in the `Cargo.toml`.
++    ///
++    /// ### Why is this bad?
++    /// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
++    /// it is highly unlikely that you work with any possible version of your dependency,
++    /// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
++    ///
++    /// ### Example
++    /// ```toml
++    /// [dependencies]
++    /// regex = "*"
++    /// ```
++    #[clippy::version = "1.32.0"]
++    pub WILDCARD_DEPENDENCIES,
++    cargo,
++    "wildcard dependencies being used"
++}
++
++pub struct Cargo {
++    pub ignore_publish: bool,
++}
++
++impl_lint_pass!(Cargo => [
++    CARGO_COMMON_METADATA,
++    REDUNDANT_FEATURE_NAMES,
++    NEGATIVE_FEATURE_NAMES,
++    MULTIPLE_CRATE_VERSIONS,
++    WILDCARD_DEPENDENCIES
++]);
++
++impl LateLintPass<'_> for Cargo {
++    fn check_crate(&mut self, cx: &LateContext<'_>) {
++        static NO_DEPS_LINTS: &[&Lint] = &[
++            CARGO_COMMON_METADATA,
++            REDUNDANT_FEATURE_NAMES,
++            NEGATIVE_FEATURE_NAMES,
++            WILDCARD_DEPENDENCIES,
++        ];
++        static WITH_DEPS_LINTS: &[&Lint] = &[MULTIPLE_CRATE_VERSIONS];
++
++        if !NO_DEPS_LINTS
++            .iter()
++            .all(|&lint| is_lint_allowed(cx, lint, CRATE_HIR_ID))
++        {
++            match MetadataCommand::new().no_deps().exec() {
++                Ok(metadata) => {
++                    common_metadata::check(cx, &metadata, self.ignore_publish);
++                    feature_name::check(cx, &metadata);
++                    wildcard_dependencies::check(cx, &metadata);
++                },
++                Err(e) => {
++                    for lint in NO_DEPS_LINTS {
++                        span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e));
++                    }
++                },
++            }
++        }
++
++        if !WITH_DEPS_LINTS
++            .iter()
++            .all(|&lint| is_lint_allowed(cx, lint, CRATE_HIR_ID))
++        {
++            match MetadataCommand::new().exec() {
++                Ok(metadata) => {
++                    multiple_crate_versions::check(cx, &metadata);
++                },
++                Err(e) => {
++                    for lint in WITH_DEPS_LINTS {
++                        span_lint(cx, lint, DUMMY_SP, &format!("could not read cargo metadata: {}", e));
++                    }
++                },
++            }
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..76fd0819a39a5f5225e7b7b44feb0dfa1a1f203e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++//! lint on multiple versions of a crate being used
++
++use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
++use clippy_utils::diagnostics::span_lint;
++use if_chain::if_chain;
++use itertools::Itertools;
++use rustc_hir::def_id::LOCAL_CRATE;
++use rustc_lint::LateContext;
++use rustc_span::source_map::DUMMY_SP;
++
++use super::MULTIPLE_CRATE_VERSIONS;
++
++pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
++    let local_name = cx.tcx.crate_name(LOCAL_CRATE);
++    let mut packages = metadata.packages.clone();
++    packages.sort_by(|a, b| a.name.cmp(&b.name));
++
++    if_chain! {
++        if let Some(resolve) = &metadata.resolve;
++        if let Some(local_id) = packages
++            .iter()
++            .find_map(|p| if p.name == local_name.as_str() { Some(&p.id) } else { None });
++        then {
++            for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
++                let group: Vec<&Package> = group.collect();
++
++                if group.len() <= 1 {
++                    continue;
++                }
++
++                if group.iter().all(|p| is_normal_dep(&resolve.nodes, local_id, &p.id)) {
++                    let mut versions: Vec<_> = group.into_iter().map(|p| &p.version).collect();
++                    versions.sort();
++                    let versions = versions.iter().join(", ");
++
++                    span_lint(
++                        cx,
++                        MULTIPLE_CRATE_VERSIONS,
++                        DUMMY_SP,
++                        &format!("multiple versions for dependency `{}`: {}", name, versions),
++                    );
++                }
++            }
++        }
++    }
++}
++
++fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {
++    fn depends_on(node: &Node, dep_id: &PackageId) -> bool {
++        node.deps.iter().any(|dep| {
++            dep.pkg == *dep_id
++                && dep
++                    .dep_kinds
++                    .iter()
++                    .any(|info| matches!(info.kind, DependencyKind::Normal))
++        })
++    }
++
++    nodes
++        .iter()
++        .filter(|node| depends_on(node, dep_id))
++        .any(|node| node.id == *local_id || is_normal_dep(nodes, local_id, &node.id))
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7fa6acbf557b1efb7f293a9ee1947df122ee88bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++use cargo_metadata::Metadata;
++use clippy_utils::diagnostics::span_lint;
++use if_chain::if_chain;
++use rustc_lint::LateContext;
++use rustc_span::source_map::DUMMY_SP;
++
++use super::WILDCARD_DEPENDENCIES;
++
++pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
++    for dep in &metadata.packages[0].dependencies {
++        // VersionReq::any() does not work
++        if_chain! {
++            if let Ok(wildcard_ver) = semver::VersionReq::parse("*");
++            if let Some(ref source) = dep.source;
++            if !source.starts_with("git");
++            if dep.req == wildcard_ver;
++            then {
++                span_lint(
++                    cx,
++                    WILDCARD_DEPENDENCIES,
++                    DUMMY_SP,
++                    &format!("wildcard dependency for `{}`", dep.name),
++                );
++            }
++        }
++    }
++}
index ea74d5acbda0528ade62cc8b1d0aca92cf2fc7f4,0000000000000000000000000000000000000000..9b189ea1ef8fb102396b7cefa76829bcff426ee5
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,171 @@@
- use clippy_utils::ty::is_isize_or_usize;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::expr_or_init;
- use super::{utils, CAST_POSSIBLE_TRUNCATION};
++use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
++use rustc_ast::ast;
++use rustc_attr::IntType;
++use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{BinOpKind, Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, FloatTy, Ty};
 +
-     let msg = match (cast_from.is_integral(), cast_to.is_integral()) {
-         (true, true) => {
++use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
 +
 +fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
 +    if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
 +        Some(c)
 +    } else {
 +        None
 +    }
 +}
 +
 +fn get_constant_bits(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u64> {
 +    constant_int(cx, expr).map(|c| u64::from(128 - c.leading_zeros()))
 +}
 +
 +fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: bool) -> u64 {
 +    match expr_or_init(cx, expr).kind {
 +        ExprKind::Cast(inner, _) => apply_reductions(cx, nbits, inner, signed),
 +        ExprKind::Block(block, _) => block.expr.map_or(nbits, |e| apply_reductions(cx, nbits, e, signed)),
 +        ExprKind::Binary(op, left, right) => match op.node {
 +            BinOpKind::Div => {
 +                apply_reductions(cx, nbits, left, signed)
 +                    - (if signed {
 +                        0 // let's be conservative here
 +                    } else {
 +                        // by dividing by 1, we remove 0 bits, etc.
 +                        get_constant_bits(cx, right).map_or(0, |b| b.saturating_sub(1))
 +                    })
 +            },
 +            BinOpKind::Rem | BinOpKind::BitAnd => get_constant_bits(cx, right)
 +                .unwrap_or(u64::max_value())
 +                .min(apply_reductions(cx, nbits, left, signed)),
 +            BinOpKind::Shr => {
 +                apply_reductions(cx, nbits, left, signed)
 +                    - constant_int(cx, right).map_or(0, |s| u64::try_from(s).expect("shift too high"))
 +            },
 +            _ => nbits,
 +        },
 +        ExprKind::MethodCall(method, [left, right], _) => {
 +            if signed {
 +                return nbits;
 +            }
 +            let max_bits = if method.ident.as_str() == "min" {
 +                get_constant_bits(cx, right)
 +            } else {
 +                None
 +            };
 +            apply_reductions(cx, nbits, left, signed).min(max_bits.unwrap_or(u64::max_value()))
 +        },
 +        ExprKind::MethodCall(method, [_, lo, hi], _) => {
 +            if method.ident.as_str() == "clamp" {
 +                //FIXME: make this a diagnostic item
 +                if let (Some(lo_bits), Some(hi_bits)) = (get_constant_bits(cx, lo), get_constant_bits(cx, hi)) {
 +                    return lo_bits.max(hi_bits);
 +                }
 +            }
 +            nbits
 +        },
 +        ExprKind::MethodCall(method, [_value], _) => {
 +            if method.ident.name.as_str() == "signum" {
 +                0 // do not lint if cast comes from a `signum` function
 +            } else {
 +                nbits
 +            }
 +        },
 +        _ => nbits,
 +    }
 +}
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
-         (false, true) => {
-             format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to)
-         },
-         (_, _) => {
-             if matches!(cast_from.kind(), &ty::Float(FloatTy::F64))
-                 && matches!(cast_to.kind(), &ty::Float(FloatTy::F32))
++    let msg = match (cast_from.kind(), cast_to.is_integral()) {
++        (ty::Int(_) | ty::Uint(_), true) => {
 +            let from_nbits = apply_reductions(
 +                cx,
 +                utils::int_ty_to_nbits(cast_from, cx.tcx),
 +                cast_expr,
 +                cast_from.is_signed(),
 +            );
 +            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 +
 +            let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) {
 +                (true, true) | (false, false) => (to_nbits < from_nbits, ""),
 +                (true, false) => (
 +                    to_nbits <= 32,
 +                    if to_nbits == 32 {
 +                        " on targets with 64-bit wide pointers"
 +                    } else {
 +                        ""
 +                    },
 +                ),
 +                (false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"),
 +            };
 +
 +            if !should_lint {
 +                return;
 +            }
 +
 +            format!(
 +                "casting `{}` to `{}` may truncate the value{}",
 +                cast_from, cast_to, suffix,
 +            )
 +        },
 +
-                 "casting `f64` to `f32` may truncate the value".to_string()
++        (ty::Adt(def, _), true) if def.is_enum() => {
++            let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind
++                && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id)
 +            {
++                let i = def.variant_index_with_ctor_id(id);
++                let variant = &def.variants[i];
++                let nbits = utils::enum_value_nbits(get_discriminant_value(cx.tcx, def, i));
++                (nbits, Some(variant))
 +            } else {
++                (utils::enum_ty_to_nbits(def, cx.tcx), None)
++            };
++            let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
++
++            let cast_from_ptr_size = def.repr.int.map_or(true, |ty| {
++                matches!(
++                    ty,
++                    IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize)
++                )
++            });
++            let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
++                (false, false) if from_nbits > to_nbits => "",
++                (true, false) if from_nbits > to_nbits => "",
++                (false, true) if from_nbits > 64 => "",
++                (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers",
++                _ => return,
++            };
++
++            if let Some(variant) = variant {
++                span_lint(
++                    cx,
++                    CAST_ENUM_TRUNCATION,
++                    expr.span,
++                    &format!(
++                        "casting `{}::{}` to `{}` will truncate the value{}",
++                        cast_from, variant.name, cast_to, suffix,
++                    ),
++                );
 +                return;
 +            }
++            format!(
++                "casting `{}` to `{}` may truncate the value{}",
++                cast_from, cast_to, suffix,
++            )
 +        },
++
++        (ty::Float(_), true) => {
++            format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to)
++        },
++
++        (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => {
++            "casting `f64` to `f32` may truncate the value".to_string()
++        },
++
++        _ => return,
 +    };
 +
 +    span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
 +}
index 079b7ff0675b4b34ad1b8d96e7a5e7810a2bf811,0000000000000000000000000000000000000000..a4ef1344ab9511beb75790081393d1e456c98040
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,64 @@@
- use rustc_span::symbol::sym;
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::is_hir_ty_cfg_dependant;
++use clippy_utils::ty::is_c_void;
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind, GenericArg};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, Ty};
- /// Check if the given type is either `core::ffi::c_void` or
- /// one of the platform specific `libc::<platform>::c_void` of libc.
- fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
-     if let ty::Adt(adt, _) = ty.kind() {
-         let names = cx.get_def_path(adt.did);
-         if names.is_empty() {
-             return false;
-         }
-         if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) {
-             return true;
-         }
-     }
-     false
- }
 +
 +use super::CAST_PTR_ALIGNMENT;
 +
 +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
 +        if is_hir_ty_cfg_dependant(cx, cast_to) {
 +            return;
 +        }
 +        let (cast_from, cast_to) = (
 +            cx.typeck_results().expr_ty(cast_expr),
 +            cx.typeck_results().expr_ty(expr),
 +        );
 +        lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
 +    } else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind {
 +        if_chain! {
 +            if method_path.ident.name == sym!(cast);
 +            if let Some(generic_args) = method_path.args;
 +            if let [GenericArg::Type(cast_to)] = generic_args.args;
 +            // There probably is no obvious reason to do this, just to be consistent with `as` cases.
 +            if !is_hir_ty_cfg_dependant(cx, cast_to);
 +            then {
 +                let (cast_from, cast_to) =
 +                    (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
 +                lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
 +            }
 +        }
 +    }
 +}
 +
 +fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::RawPtr(from_ptr_ty) = &cast_from.kind();
 +        if let ty::RawPtr(to_ptr_ty) = &cast_to.kind();
 +        if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
 +        if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
 +        if from_layout.align.abi < to_layout.align.abi;
 +        // with c_void, we inherently need to trust the user
 +        if !is_c_void(cx, from_ptr_ty.ty);
 +        // when casting from a ZST, we don't know enough to properly lint
 +        if !from_layout.is_zst();
 +        then {
 +            span_lint(
 +                cx,
 +                CAST_PTR_ALIGNMENT,
 +                expr.span,
 +                &format!(
 +                    "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
 +                    cast_from,
 +                    cast_to,
 +                    from_layout.align.abi.bytes(),
 +                    to_layout.align.abi.bytes(),
 +                ),
 +            );
 +        }
 +    }
 +}
index aee1e50b94a2a549d199daa74d19d7dabbfa0a69,0000000000000000000000000000000000000000..f2077c569c04121a1232d7d3b483dc927f2069e2
mode 100644,000000..100644
--- /dev/null
@@@ -1,462 -1,0 +1,484 @@@
-                     cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +mod cast_lossless;
 +mod cast_possible_truncation;
 +mod cast_possible_wrap;
 +mod cast_precision_loss;
 +mod cast_ptr_alignment;
 +mod cast_ref_to_mut;
 +mod cast_sign_loss;
 +mod char_lit_as_u8;
 +mod fn_to_numeric_cast;
 +mod fn_to_numeric_cast_any;
 +mod fn_to_numeric_cast_with_truncation;
 +mod ptr_as_ptr;
 +mod unnecessary_cast;
 +mod utils;
 +
 +use clippy_utils::is_hir_ty_cfg_dependant;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from any numerical to a float type where
 +    /// the receiving type cannot store all values from the original type without
 +    /// rounding errors. This possible rounding is to be expected, so this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
 +    /// or any 64-bit integer to `f64`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad at all. But in some applications it can be
 +    /// helpful to know where precision loss can take place. This lint can help find
 +    /// those places in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = u64::MAX;
 +    /// x as f64;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PRECISION_LOSS,
 +    pedantic,
 +    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from a signed to an unsigned numerical
 +    /// type. In this case, negative values wrap around to large positive values,
 +    /// which can be quite surprising in practice. However, as the cast works as
 +    /// defined, this lint is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// Possibly surprising results. You can activate this lint
 +    /// as a one-time check to see where numerical wrapping can arise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let y: i8 = -1;
 +    /// y as u128; // will return 18446744073709551615
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_SIGN_LOSS,
 +    pedantic,
 +    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// truncate large values. This is expected behavior, so the cast is `Allow` by
 +    /// default.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some problem domains, it is good practice to avoid
 +    /// truncation. This lint can be activated to help assess where additional
 +    /// checks could be beneficial.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u8(x: u64) -> u8 {
 +    ///     x as u8
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_TRUNCATION,
 +    pedantic,
 +    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an unsigned type to a signed type of
 +    /// the same size. Performing such a cast is a 'no-op' for the compiler,
 +    /// i.e., nothing is changed at the bit level, and the binary representation of
 +    /// the value is reinterpreted. This can cause wrapping if the value is too big
 +    /// for the target signed type. However, the cast works as defined, so this lint
 +    /// is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// While such a cast is not bad in itself, the results can
 +    /// be surprising when this is not the intended behavior, as demonstrated by the
 +    /// example below.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// u32::MAX as i32; // will yield a value of `-1`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_WRAP,
 +    pedantic,
 +    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// be replaced by safe conversion functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust's `as` keyword will perform many kinds of
 +    /// conversions, including silently lossy conversions. Conversion functions such
 +    /// as `i32::from` will only perform lossless conversions. Using the conversion
 +    /// functions prevents conversions from turning into silent lossy conversions if
 +    /// the types of the input expressions ever change, and make it easier for
 +    /// people reading the code to know that the conversion is lossless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     x as u64
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `::from` would look like this:
 +    ///
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     u64::from(x)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_LOSSLESS,
 +    pedantic,
 +    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts to the same type, casts of int literals to integer types
 +    /// and casts of float literals to float types.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's just unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 2i32 as i32;
 +    /// let _ = 0.5 as f32;
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// let _ = 2_i32;
 +    /// let _ = 0.5_f32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_CAST,
 +    complexity,
 +    "cast to the same type, e.g., `x as i32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts, using `as` or `pointer::cast`,
 +    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing the resulting pointer may be undefined
 +    /// behavior.
 +    ///
 +    /// ### Known problems
 +    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
 +    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
 +    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (&1u8 as *const u8) as *const u16;
 +    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
 +    ///
 +    /// (&1u8 as *const u8).cast::<u16>();
 +    /// (&mut 1u8 as *mut u8).cast::<u16>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PTR_ALIGNMENT,
 +    pedantic,
 +    "cast from a pointer to a more-strictly-aligned pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of function pointers to something other than usize
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to anything other than usize/isize is not portable across
 +    /// architectures, because you end up losing bits if the target type is too small or end up with a
 +    /// bunch of extra bits that waste space and add more instructions to the final binary than
 +    /// strictly necessary for the problem
 +    ///
 +    /// Casting to isize also doesn't make sense since there are no signed addresses.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// fn fun() -> i32 { 1 }
 +    /// let a = fun as i64;
 +    ///
 +    /// // Good
 +    /// fn fun2() -> i32 { 1 }
 +    /// let a = fun2 as usize;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST,
 +    style,
 +    "casting a function pointer to a numeric type other than usize"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to a numeric type not wide enough to
 +    /// store address.
 +    ///
 +    /// ### Why is this bad?
 +    /// Such a cast discards some bits of the function's address. If this is intended, it would be more
 +    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
 +    /// a comment) to perform the truncation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as i32;
 +    ///
 +    /// // Better: Cast to usize first, then comment with the reason for the truncation
 +    /// fn fn2() -> i16 {
 +    ///     1
 +    /// };
 +    /// let fn_ptr = fn2 as usize;
 +    /// let fn_ptr_truncated = fn_ptr as i32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    style,
 +    "casting a function pointer to a numeric type not wide enough to store the address"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to any integer type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to an integer can have surprising results and can occur
 +    /// accidentally if parantheses are omitted from a function call. If you aren't doing anything
 +    /// low-level with function pointers then you can opt-out of casting functions to integers in
 +    /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
 +    /// pointer casts in your code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad: fn1 is cast as `usize`
 +    /// fn fn1() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as usize;
 +    ///
 +    /// // Good: maybe you intended to call the function?
 +    /// fn fn2() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn2() as usize;
 +    ///
 +    /// // Good: maybe you intended to cast it to a function type?
 +    /// fn fn3() -> u16 {
 +    ///     1
 +    /// }
 +    /// let _ = fn3 as fn() -> u16;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FN_TO_NUMERIC_CAST_ANY,
 +    restriction,
 +    "casting a function pointer to any integer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of `&T` to `&mut T` anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// It’s basically guaranteed to be undefined behaviour.
 +    /// `UnsafeCell` is the only way to obtain aliasable data that is considered
 +    /// mutable.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn x(r: &i32) {
 +    ///     unsafe {
 +    ///         *(r as *const _ as *mut _) += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Instead consider using interior mutability types.
 +    ///
 +    /// ```rust
 +    /// use std::cell::UnsafeCell;
 +    ///
 +    /// fn x(r: &UnsafeCell<i32>) {
 +    ///     unsafe {
 +    ///         *r.get() += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub CAST_REF_TO_MUT,
 +    correctness,
 +    "a cast of reference to a mutable pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where a character literal is cast
 +    /// to `u8` and suggests using a byte literal instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, casting values to smaller types is
 +    /// error-prone and should be avoided where possible. In the particular case of
 +    /// converting a character literal to u8, it is easy to avoid by just using a
 +    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
 +    /// than `'a' as u8`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 'x' as u8
 +    /// ```
 +    ///
 +    /// A better version, using the byte literal:
 +    ///
 +    /// ```rust,ignore
 +    /// b'x'
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHAR_LIT_AS_U8,
 +    complexity,
 +    "casting a character literal to `u8` truncates"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers without changing its mutability,
 +    /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
 +    /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr as *const i32;
 +    /// let _ = mut_ptr as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr.cast::<i32>();
 +    /// let _ = mut_ptr.cast::<i32>();
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub PTR_AS_PTR,
 +    pedantic,
 +    "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for casts from an enum type to an integral type which will definitely truncate the
++    /// value.
++    ///
++    /// ### Why is this bad?
++    /// The resulting integral value will not match the value of the variant it came from.
++    ///
++    /// ### Example
++    /// ```rust
++    /// enum E { X = 256 };
++    /// let _ = E::X as u8;
++    /// ```
++    #[clippy::version = "1.60.0"]
++    pub CAST_ENUM_TRUNCATION,
++    suspicious,
++    "casts from an enum type to an integral type which will truncate the value"
++}
++
 +pub struct Casts {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Casts {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Casts => [
 +    CAST_PRECISION_LOSS,
 +    CAST_SIGN_LOSS,
 +    CAST_POSSIBLE_TRUNCATION,
 +    CAST_POSSIBLE_WRAP,
 +    CAST_LOSSLESS,
 +    CAST_REF_TO_MUT,
 +    CAST_PTR_ALIGNMENT,
 +    UNNECESSARY_CAST,
 +    FN_TO_NUMERIC_CAST_ANY,
 +    FN_TO_NUMERIC_CAST,
 +    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    CHAR_LIT_AS_U8,
 +    PTR_AS_PTR,
++    CAST_ENUM_TRUNCATION,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Casts {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if !in_external_macro(cx.sess(), expr.span) {
++            ptr_as_ptr::check(cx, expr, &self.msrv);
++        }
++
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
 +            if is_hir_ty_cfg_dependant(cx, cast_to) {
 +                return;
 +            }
 +            let (cast_from, cast_to) = (
 +                cx.typeck_results().expr_ty(cast_expr),
 +                cx.typeck_results().expr_ty(expr),
 +            );
 +
 +            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
 +                return;
 +            }
 +
 +            fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +
 +            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
++                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +                if cast_from.is_numeric() {
 +                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
 +                    cast_precision_loss::check(cx, expr, cast_from, cast_to);
 +                    cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
 +                }
-         ptr_as_ptr::check(cx, expr, &self.msrv);
 +                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
 +            }
 +        }
 +
 +        cast_ref_to_mut::check(cx, expr);
 +        cast_ptr_alignment::check(cx, expr);
 +        char_lit_as_u8::check(cx, expr);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 00fd0b3473b4417949097b5ebb34ba8c10144e60,0000000000000000000000000000000000000000..bbed766c47a8526f45c9ec90fd34b3b6b57b18e9
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,75 @@@
- use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy};
++use clippy_utils::ty::{read_explicit_enum_value, EnumValue};
++use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
 +
 +/// Returns the size in bits of an integral type.
 +/// Will return 0 if the type is not an int or uint variant
 +pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
 +    match typ.kind() {
 +        ty::Int(i) => match i {
 +            IntTy::Isize => tcx.data_layout.pointer_size.bits(),
 +            IntTy::I8 => 8,
 +            IntTy::I16 => 16,
 +            IntTy::I32 => 32,
 +            IntTy::I64 => 64,
 +            IntTy::I128 => 128,
 +        },
 +        ty::Uint(i) => match i {
 +            UintTy::Usize => tcx.data_layout.pointer_size.bits(),
 +            UintTy::U8 => 8,
 +            UintTy::U16 => 16,
 +            UintTy::U32 => 32,
 +            UintTy::U64 => 64,
 +            UintTy::U128 => 128,
 +        },
 +        _ => 0,
 +    }
 +}
++
++pub(super) fn enum_value_nbits(value: EnumValue) -> u64 {
++    match value {
++        EnumValue::Unsigned(x) => 128 - x.leading_zeros(),
++        EnumValue::Signed(x) if x < 0 => 128 - (-(x + 1)).leading_zeros() + 1,
++        EnumValue::Signed(x) => 128 - x.leading_zeros(),
++    }
++    .into()
++}
++
++pub(super) fn enum_ty_to_nbits(adt: &AdtDef, tcx: TyCtxt<'_>) -> u64 {
++    let mut explicit = 0i128;
++    let (start, end) = adt
++        .variants
++        .iter()
++        .fold((0, i128::MIN), |(start, end), variant| match variant.discr {
++            VariantDiscr::Relative(x) => match explicit.checked_add(i128::from(x)) {
++                Some(x) => (start, end.max(x)),
++                None => (i128::MIN, end),
++            },
++            VariantDiscr::Explicit(id) => match read_explicit_enum_value(tcx, id) {
++                Some(EnumValue::Signed(x)) => {
++                    explicit = x;
++                    (start.min(x), end.max(x))
++                },
++                Some(EnumValue::Unsigned(x)) => match i128::try_from(x) {
++                    Ok(x) => {
++                        explicit = x;
++                        (start, end.max(x))
++                    },
++                    Err(_) => (i128::MIN, end),
++                },
++                None => (start, end),
++            },
++        });
++
++    if start > end {
++        // No variants.
++        0
++    } else {
++        let neg_bits = if start < 0 {
++            128 - (-(start + 1)).leading_zeros() + 1
++        } else {
++            0
++        };
++        let pos_bits = if end > 0 { 128 - end.leading_zeros() } else { 0 };
++        neg_bits.max(pos_bits).into()
++    }
++}
index 5a0b60fdfbc01924de5a09c3dc36d62a6b8592c1,0000000000000000000000000000000000000000..df1a4128af35957aa8cb5bf8fa02d37b905611cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,82 @@@
- use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
- use clippy_utils::source::snippet_opt;
- use rustc_ast::ast;
- use rustc_ast::tokenstream::TokenStream;
++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 rustc_errors::Applicability;
- use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_hir::{Expr, ExprKind};
++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 usage of dbg!() macro.
 +    ///
 +    /// ### Why is this bad?
 +    /// `dbg!` macro is intended as a debugging tool. It
 +    /// should not be in version control.
 +    ///
-     /// ### Known problems
-     /// * The lint level is unaffected by crate attributes. The level can still
-     ///   be set for functions, modules and other items. To change the level for
-     ///   the entire crate, please use command line flags. More information and a
-     ///   configuration example can be found in [clippy#6610].
-     ///
-     /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
-     ///
 +    /// ### 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"
 +}
 +
 +declare_lint_pass!(DbgMacro => [DBG_MACRO]);
 +
- impl EarlyLintPass for DbgMacro {
-     fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &ast::MacCall) {
-         if mac.path == sym!(dbg) {
-             if let Some(sugg) = tts_span(mac.args.inner_tokens()).and_then(|span| snippet_opt(cx, span)) {
-                 span_lint_and_sugg(
-                     cx,
-                     DBG_MACRO,
-                     mac.span(),
-                     "`dbg!` macro is intended as a debugging tool",
-                     "ensure to avoid having uses of it in version control",
-                     sugg,
-                     Applicability::MaybeIncorrect,
-                 );
-             } else {
-                 span_lint_and_help(
-                     cx,
-                     DBG_MACRO,
-                     mac.span(),
-                     "`dbg!` macro is intended as a debugging tool",
-                     None,
-                     "ensure to avoid having uses of it in version control",
-                 );
-             }
++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) {
++            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,
++            );
 +        }
 +    }
 +}
- // Get span enclosing entire the token stream.
- fn tts_span(tts: TokenStream) -> Option<Span> {
-     let mut cursor = tts.into_trees();
-     let first = cursor.next()?.span();
-     let span = cursor.last().map_or(first, |tree| first.to(tree.span()));
-     Some(span)
- }
index 3070588483c21b33d97fc3ab305cce090b096c0f,0000000000000000000000000000000000000000..06e6bf986c2a9458e6417ec0ec9fe2991bed7626
mode 100644,000000..100644
--- /dev/null
@@@ -1,292 -1,0 +1,306 @@@
- use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
 +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 +use clippy_utils::source::snippet_with_macro_callsite;
 +use clippy_utils::ty::{has_drop, is_copy};
++use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{Ident, Symbol};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for literal calls to `Default::default()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more clear to the reader to use the name of the type whose default is
 +    /// being gotten than the generic `Default`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let s: String = Default::default();
 +    ///
 +    /// // Good
 +    /// let s = String::default();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DEFAULT_TRAIT_ACCESS,
 +    pedantic,
 +    "checks for literal calls to `Default::default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for immediate reassignment of fields initialized
 +    /// with Default::default().
 +    ///
 +    /// ### Why is this bad?
 +    ///It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
 +    ///
 +    /// ### Known problems
 +    /// Assignments to patterns that are of tuple type are not linted.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```
 +    /// # #[derive(Default)]
 +    /// # struct A { i: i32 }
 +    /// let mut a: A = Default::default();
 +    /// a.i = 42;
 +    /// ```
 +    /// Use instead:
 +    /// ```
 +    /// # #[derive(Default)]
 +    /// # struct A { i: i32 }
 +    /// let a = A {
 +    ///     i: 42,
 +    ///     .. Default::default()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub FIELD_REASSIGN_WITH_DEFAULT,
 +    style,
 +    "binding initialized with Default should have its fields set in the initializer"
 +}
 +
 +#[derive(Default)]
 +pub struct Default {
 +    // Spans linted by `field_reassign_with_default`.
 +    reassigned_linted: FxHashSet<Span>,
 +}
 +
 +impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Default {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if !expr.span.from_expansion();
 +            // Avoid cases already linted by `field_reassign_with_default`
 +            if !self.reassigned_linted.contains(&expr.span);
 +            if let ExprKind::Call(path, ..) = expr.kind;
 +            if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
 +            if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
++            if !is_update_syntax_base(cx, expr);
 +            // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
 +            if let QPath::Resolved(None, _path) = qpath;
 +            let expr_ty = cx.typeck_results().expr_ty(expr);
 +            if let ty::Adt(def, ..) = expr_ty.kind();
 +            then {
 +                // TODO: Work out a way to put "whatever the imported way of referencing
 +                // this type in this file" rather than a fully-qualified type.
 +                let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
 +                span_lint_and_sugg(
 +                    cx,
 +                    DEFAULT_TRAIT_ACCESS,
 +                    expr.span,
 +                    &format!("calling `{}` is more clear than this expression", replacement),
 +                    "try",
 +                    replacement,
 +                    Applicability::Unspecified, // First resolve the TODO above
 +                );
 +            }
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +        // start from the `let mut _ = _::default();` and look at all the following
 +        // statements, see if they re-assign the fields of the binding
 +        let stmts_head = match block.stmts {
 +            // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
 +            [head @ .., _] if !head.is_empty() => head,
 +            _ => return,
 +        };
 +        for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
 +            // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
 +            // `default` method of the `Default` trait, and store statement index in current block being
 +            // checked and the name of the bound variable
 +            let (local, variant, binding_name, binding_type, span) = if_chain! {
 +                // only take `let ...` statements
 +                if let StmtKind::Local(local) = stmt.kind;
 +                if let Some(expr) = local.init;
 +                if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
 +                if !expr.span.from_expansion();
 +                // only take bindings to identifiers
 +                if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
 +                // only when assigning `... = Default::default()`
 +                if is_expr_default(expr, cx);
 +                let binding_type = cx.typeck_results().node_type(binding_id);
 +                if let Some(adt) = binding_type.ty_adt_def();
 +                if adt.is_struct();
 +                let variant = adt.non_enum_variant();
 +                if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
 +                let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
 +                if variant
 +                    .fields
 +                    .iter()
 +                    .all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
 +                let all_fields_are_copy = variant
 +                    .fields
 +                    .iter()
 +                    .all(|field| {
 +                        is_copy(cx, cx.tcx.type_of(field.did))
 +                    });
 +                if !has_drop(cx, binding_type) || all_fields_are_copy;
 +                then {
 +                    (local, variant, ident.name, binding_type, expr.span)
 +                } else {
 +                    continue;
 +                }
 +            };
 +
 +            // find all "later statement"'s where the fields of the binding set as
 +            // Default::default() get reassigned, unless the reassignment refers to the original binding
 +            let mut first_assign = None;
 +            let mut assigned_fields = Vec::new();
 +            let mut cancel_lint = false;
 +            for consecutive_statement in &block.stmts[stmt_idx + 1..] {
 +                // find out if and which field was set by this `consecutive_statement`
 +                if let Some((field_ident, assign_rhs)) = field_reassigned_by_stmt(consecutive_statement, binding_name) {
 +                    // interrupt and cancel lint if assign_rhs references the original binding
 +                    if contains_name(binding_name, assign_rhs) {
 +                        cancel_lint = true;
 +                        break;
 +                    }
 +
 +                    // if the field was previously assigned, replace the assignment, otherwise insert the assignment
 +                    if let Some(prev) = assigned_fields
 +                        .iter_mut()
 +                        .find(|(field_name, _)| field_name == &field_ident.name)
 +                    {
 +                        *prev = (field_ident.name, assign_rhs);
 +                    } else {
 +                        assigned_fields.push((field_ident.name, assign_rhs));
 +                    }
 +
 +                    // also set first instance of error for help message
 +                    if first_assign.is_none() {
 +                        first_assign = Some(consecutive_statement);
 +                    }
 +                }
 +                // interrupt if no field was assigned, since we only want to look at consecutive statements
 +                else {
 +                    break;
 +                }
 +            }
 +
 +            // if there are incorrectly assigned fields, do a span_lint_and_note to suggest
 +            // construction using `Ty { fields, ..Default::default() }`
 +            if !assigned_fields.is_empty() && !cancel_lint {
 +                // if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
 +                let ext_with_default = !variant
 +                    .fields
 +                    .iter()
 +                    .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
 +
 +                let field_list = assigned_fields
 +                    .into_iter()
 +                    .map(|(field, rhs)| {
 +                        // extract and store the assigned value for help message
 +                        let value_snippet = snippet_with_macro_callsite(cx, rhs.span, "..");
 +                        format!("{}: {}", field, value_snippet)
 +                    })
 +                    .collect::<Vec<String>>()
 +                    .join(", ");
 +
 +                // give correct suggestion if generics are involved (see #6944)
 +                let binding_type = if_chain! {
 +                    if let ty::Adt(adt_def, substs) = binding_type.kind();
 +                    if !substs.is_empty();
 +                    then {
 +                        let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
 +                        let generic_args = substs.iter().collect::<Vec<_>>();
 +                        let tys_str = generic_args
 +                            .iter()
 +                            .map(ToString::to_string)
 +                            .collect::<Vec<_>>()
 +                            .join(", ");
 +                        format!("{}::<{}>", adt_def_ty_name, &tys_str)
 +                    } else {
 +                        binding_type.to_string()
 +                    }
 +                };
 +
 +                let sugg = if ext_with_default {
 +                    if field_list.is_empty() {
 +                        format!("{}::default()", binding_type)
 +                    } else {
 +                        format!("{} {{ {}, ..Default::default() }}", binding_type, field_list)
 +                    }
 +                } else {
 +                    format!("{} {{ {} }}", binding_type, field_list)
 +                };
 +
 +                // span lint once per statement that binds default
 +                span_lint_and_note(
 +                    cx,
 +                    FIELD_REASSIGN_WITH_DEFAULT,
 +                    first_assign.unwrap().span,
 +                    "field assignment outside of initializer for an instance created with Default::default()",
 +                    Some(local.span),
 +                    &format!(
 +                        "consider initializing the variable with `{}` and removing relevant reassignments",
 +                        sugg
 +                    ),
 +                );
 +                self.reassigned_linted.insert(span);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if the given expression is the `default` method belonging to the `Default` trait.
 +fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
 +    if_chain! {
 +        if let ExprKind::Call(fn_expr, _) = &expr.kind;
 +        if let ExprKind::Path(qpath) = &fn_expr.kind;
 +        if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id);
 +        then {
 +            // right hand side of assignment is `Default::default`
 +            match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD)
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +/// Returns the reassigned field and the assigning expression (right-hand side of assign).
 +fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
 +    if_chain! {
 +        // only take assignments
 +        if let StmtKind::Semi(later_expr) = this.kind;
 +        if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind;
 +        // only take assignments to fields where the left-hand side field is a field of
 +        // the same binding as the previous statement
 +        if let ExprKind::Field(binding, field_ident) = assign_lhs.kind;
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind;
 +        if let Some(second_binding_name) = path.segments.last();
 +        if second_binding_name.ident.name == binding_name;
 +        then {
 +            Some((field_ident, assign_rhs))
 +        } else {
 +            None
 +        }
 +    }
 +}
++
++/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
++fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
++    if_chain! {
++        if let Some(parent) = get_parent_expr(cx, expr);
++        if let ExprKind::Struct(_, _, Some(base)) = parent.kind;
++        then {
++            base.hir_id == expr.hir_id
++        } else {
++            false
++        }
++    }
++}
index 6d3df260ca25592a59a43e5d75751200c20f7591,0000000000000000000000000000000000000000..7277e4080c5c0763ffa132a28a48dc4fd394486d
mode 100644,000000..100644
--- /dev/null
@@@ -1,420 -1,0 +1,421 @@@
- use clippy_utils::{get_trait_def_id, is_automatically_derived, is_lint_allowed, match_def_path};
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, is_copy};
-         if match_def_path(cx, def_id, &paths::HASH);
++use clippy_utils::{is_automatically_derived, is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 +use rustc_hir::{
 +    BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, TraitRef, UnsafeSource, Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
++use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for 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 behaviour, these traits should
 +    /// agree and the behaviour 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_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE
 +]);
 +
 +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 attrs = cx.tcx.hir().attrs(item.hir_id());
 +            let is_automatically_derived = is_automatically_derived(attrs);
 +
 +            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);
 +            } 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<'_>,
 +    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 let Some(ord_trait_def_id) = get_trait_def_id(cx, &paths::ORD);
++        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 = is_automatically_derived(cx.tcx.get_attrs(impl_id));
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &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 = is_automatically_derived(cx.tcx.get_attrs(impl_id));
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &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<'_>,
 +    trait_ref: &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()
 +    }
 +}
index a8f8e3d8a42c0bba557b0dc04fdbb9b2f9730a6b,0000000000000000000000000000000000000000..5c4b35fd4b9d2e572b155fb29e85b3cf7019ecf3
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,170 @@@
- use clippy_utils::{match_def_path, paths};
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::ty::is_copy;
-                     if match_def_path(cx, def_id, &paths::DROP) {
-                         lint = DROP_REF;
-                         msg = DROP_REF_SUMMARY.to_string();
-                     } else if match_def_path(cx, def_id, &paths::MEM_FORGET) {
-                         lint = FORGET_REF;
-                         msg = FORGET_REF_SUMMARY.to_string();
-                     } else {
-                         return;
 +use if_chain::if_chain;
 +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::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a reference
 +    /// instead of an owned value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `drop` on a reference will only drop the
 +    /// reference itself, which is a no-op. It will not call the `drop` method (from
 +    /// the `Drop` trait implementation) on the underlying referenced value, which
 +    /// is likely what was intended.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let mut lock_guard = mutex.lock();
 +    /// std::mem::drop(&lock_guard) // Should have been drop(lock_guard), mutex
 +    /// // still locked
 +    /// operation_that_requires_mutex_to_be_unlocked();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DROP_REF,
 +    correctness,
 +    "calls to `std::mem::drop` with a reference instead of an owned value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a reference
 +    /// instead of an owned value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `forget` on a reference will only forget the
 +    /// reference itself, which is a no-op. It will not forget the underlying
 +    /// referenced
 +    /// value, which is likely what was intended.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Box::new(1);
 +    /// std::mem::forget(&x) // Should have been forget(x), x will still be dropped
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FORGET_REF,
 +    correctness,
 +    "calls to `std::mem::forget` with a reference instead of an owned value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::drop` with a value
 +    /// that derives the Copy trait
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::drop` [does nothing for types that
 +    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
 +    /// value will be copied and moved into the function on invocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 42; // i32 implements Copy
 +    /// std::mem::drop(x) // A copy of x is passed to the function, leaving the
 +    ///                   // original unaffected
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DROP_COPY,
 +    correctness,
 +    "calls to `std::mem::drop` with a value that implements Copy"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `std::mem::forget` with a value that
 +    /// derives the Copy trait
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `std::mem::forget` [does nothing for types that
 +    /// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
 +    /// value will be copied and moved into the function on invocation.
 +    ///
 +    /// An alternative, but also valid, explanation is that Copy types do not
 +    /// implement
 +    /// the Drop trait, which means they have no destructors. Without a destructor,
 +    /// there
 +    /// is nothing for `std::mem::forget` to ignore.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = 42; // i32 implements Copy
 +    /// std::mem::forget(x) // A copy of x is passed to the function, leaving the
 +    ///                     // original unaffected
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FORGET_COPY,
 +    correctness,
 +    "calls to `std::mem::forget` with a value that implements Copy"
 +}
 +
 +const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
 +                                Dropping a reference does nothing";
 +const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
 +                                  Forgetting a reference does nothing";
 +const DROP_COPY_SUMMARY: &str = "calls to `std::mem::drop` with a value that implements `Copy`. \
 +                                 Dropping a copy leaves the original intact";
 +const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
 +                                   Forgetting a copy leaves the original intact";
 +
 +declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(path, args) = expr.kind;
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if args.len() == 1;
 +            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
 +            then {
 +                let lint;
 +                let msg;
 +                let arg = &args[0];
 +                let arg_ty = cx.typeck_results().expr_ty(arg);
 +
 +                if let ty::Ref(..) = arg_ty.kind() {
-                     if match_def_path(cx, def_id, &paths::DROP) {
-                         lint = DROP_COPY;
-                         msg = DROP_COPY_SUMMARY.to_string();
-                     } else if match_def_path(cx, def_id, &paths::MEM_FORGET) {
-                         lint = FORGET_COPY;
-                         msg = FORGET_COPY_SUMMARY.to_string();
-                     } else {
-                         return;
++                    match cx.tcx.get_diagnostic_name(def_id) {
++                        Some(sym::mem_drop) => {
++                            lint = DROP_REF;
++                            msg = DROP_REF_SUMMARY.to_string();
++                        },
++                        Some(sym::mem_forget) => {
++                            lint = FORGET_REF;
++                            msg = FORGET_REF_SUMMARY.to_string();
++                        },
++                        _ => return,
 +                    }
 +                    span_lint_and_note(cx,
 +                                       lint,
 +                                       expr.span,
 +                                       &msg,
 +                                       Some(arg.span),
 +                                       &format!("argument has type `{}`", arg_ty));
 +                } else if is_copy(cx, arg_ty) {
++                    match cx.tcx.get_diagnostic_name(def_id) {
++                        Some(sym::mem_drop) => {
++                            lint = DROP_COPY;
++                            msg = DROP_COPY_SUMMARY.to_string();
++                        },
++                        Some(sym::mem_forget) => {
++                            lint = FORGET_COPY;
++                            msg = FORGET_COPY_SUMMARY.to_string();
++                        },
++                        _ => return,
 +                    }
 +                    span_lint_and_note(cx,
 +                                       lint,
 +                                       expr.span,
 +                                       &msg,
 +                                       Some(arg.span),
 +                                       &format!("argument has type {}", arg_ty));
 +                }
 +            }
 +        }
 +    }
 +}
index 24e32c09f44b3252d158653ef366eb8bb66a8e28,0000000000000000000000000000000000000000..09318f74527c2bf16485add7c10f1cf70981f30e
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,73 @@@
- use clippy_utils::ty::match_type;
++use clippy_utils::consts::{constant, Constant};
++use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
- use clippy_utils::consts::{constant, Constant};
- use clippy_utils::diagnostics::span_lint_and_sugg;
- use clippy_utils::paths;
++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;
-             if match_type(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), &paths::DURATION);
++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;
 +    /// let dur = Duration::new(5, 0);
 +    ///
 +    /// // Bad
 +    /// let _micros = dur.subsec_nanos() / 1_000;
 +    /// let _millis = dur.subsec_nanos() / 1_000_000;
 +    ///
 +    /// // Good
 +    /// let _micros = dur.subsec_micros();
 +    /// let _millis = dur.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 ea547793b1ea2528a0a6662e972b68041d9c4174,0000000000000000000000000000000000000000..6490231fed8a7195ee6c1b9cd62ed1554d43cac0
mode 100644,000000..100644
--- /dev/null
@@@ -1,320 -1,0 +1,322 @@@
- use rustc_hir::{
-     def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind,
- };
 +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;
- fn in_impl<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, bin_op: DefId) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
++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
 +    /// ```rust
 +    /// # 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 {
 +    #[allow(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 {
 +                #[allow(clippy::match_same_arms)]
 +                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 263bff4873caf4cb45af2f3efdd8bb95dd31bbf1,0000000000000000000000000000000000000000..d23c0c225e192e84fc4ad27b0e6a5d3ac613713d
mode 100644,000000..100644
--- /dev/null
@@@ -1,225 -1,0 +1,232 @@@
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::higher::VecArgs;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::usage::local_used_after_expr;
 +use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, ExprKind, Param, PatKind, Unsafety};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
++use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::subst::Subst;
 +use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which just call another function where
 +    /// the function can be called directly. `unsafe` functions or calls where types
 +    /// get adjusted are ignored.
 +    ///
 +    /// ### Why is this bad?
 +    /// Needlessly creating a closure adds code for no benefit
 +    /// and gives the optimizer more work.
 +    ///
 +    /// ### Known problems
 +    /// If creating the closure inside the closure has a side-
 +    /// effect then moving the closure creation out will change when that side-
 +    /// effect runs.
 +    /// See [#1439](https://github.com/rust-lang/rust-clippy/issues/1439) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// xs.map(|x| foo(x))
 +    ///
 +    /// // Good
 +    /// xs.map(foo)
 +    /// ```
 +    /// where `foo(_)` is a plain function that takes the exact argument type of
 +    /// `x`.
 +    #[clippy::version = "pre 1.29.0"]
 +    pub REDUNDANT_CLOSURE,
 +    style,
 +    "redundant closures, i.e., `|a| foo(a)` (which can be written as just `foo`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for closures which only invoke a method on the closure
 +    /// argument and can be replaced by referencing the method directly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's unnecessary to create the closure.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// Some('a').map(|s| s.to_uppercase());
 +    /// ```
 +    /// may be rewritten as
 +    /// ```rust,ignore
 +    /// Some('a').map(char::to_uppercase);
 +    /// ```
 +    #[clippy::version = "1.35.0"]
 +    pub REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    pedantic,
 +    "redundant closures for method calls"
 +}
 +
 +declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for EtaReduction {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +        let body = match expr.kind {
 +            ExprKind::Closure(_, _, id, _, _) => cx.tcx.hir().body(id),
 +            _ => return,
 +        };
 +        if body.value.span.from_expansion() {
 +            if body.params.is_empty() {
 +                if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
 +                    // replace `|| vec![]` with `Vec::new`
 +                    span_lint_and_sugg(
 +                        cx,
 +                        REDUNDANT_CLOSURE,
 +                        expr.span,
 +                        "redundant closure",
 +                        "replace the closure with `Vec::new`",
 +                        "std::vec::Vec::new".into(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +            // skip `foo(|| macro!())`
 +            return;
 +        }
 +
 +        let closure_ty = cx.typeck_results().expr_ty(expr);
 +
 +        if_chain!(
 +            if let ExprKind::Call(callee, args) = body.value.kind;
 +            if let ExprKind::Path(_) = callee.kind;
 +            if check_inputs(cx, body.params, args);
 +            let callee_ty = cx.typeck_results().expr_ty_adjusted(callee);
 +            let call_ty = cx.typeck_results().type_dependent_def_id(body.value.hir_id)
 +                .map_or(callee_ty, |id| cx.tcx.type_of(id));
 +            if check_sig(cx, closure_ty, call_ty);
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            // This fixes some false positives that I don't entirely understand
 +            if substs.is_empty() || !cx.typeck_results().expr_ty(expr).has_late_bound_regions();
 +            // A type param function ref like `T::f` is not 'static, however
 +            // it is if cast like `T::f as fn()`. This seems like a rustc bug.
 +            if !substs.types().any(|t| matches!(t.kind(), ty::Param(_)));
 +            let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs();
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc);
 +            if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
 +                    if let Some(mut snippet) = snippet_opt(cx, callee.span) {
 +                        if_chain! {
 +                            if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
 +                            if substs.as_closure().kind() == ClosureKind::FnMut;
 +                            if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
 +                                || path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
 +
 +                            then {
 +                                // Mutable closure is used after current expr; we cannot consume it.
 +                                snippet = format!("&mut {}", snippet);
 +                            }
 +                        }
 +                        diag.span_suggestion(
 +                            expr.span,
 +                            "replace the closure with the function itself",
 +                            snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                });
 +            }
 +        );
 +
 +        if_chain!(
 +            if let ExprKind::MethodCall(path, args, _) = body.value.kind;
 +            if check_inputs(cx, body.params, args);
 +            let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
 +            let substs = cx.typeck_results().node_substs(body.value.hir_id);
 +            let call_ty = cx.tcx.type_of(method_def_id).subst(cx.tcx, substs);
 +            if check_sig(cx, closure_ty, call_ty);
 +            then {
 +                span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| {
 +                    let name = get_ufcs_type_name(cx, method_def_id);
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "replace the closure with the method itself",
 +                        format!("{}::{}", name, path.ident.name),
 +                        Applicability::MachineApplicable,
 +                    );
 +                })
 +            }
 +        );
 +    }
 +}
 +
 +fn check_inputs(cx: &LateContext<'_>, params: &[Param<'_>], call_args: &[Expr<'_>]) -> bool {
 +    if params.len() != call_args.len() {
 +        return false;
 +    }
++    let binding_modes = cx.typeck_results().pat_binding_modes();
 +    std::iter::zip(params, call_args).all(|(param, arg)| {
 +        match param.pat.kind {
 +            PatKind::Binding(_, id, ..) if path_to_local_id(arg, id) => {},
 +            _ => return false,
 +        }
++        // checks that parameters are not bound as `ref` or `ref mut`
++        if let Some(BindingMode::BindByReference(_)) = binding_modes.get(param.pat.hir_id) {
++            return false;
++        }
++
 +        match *cx.typeck_results().expr_adjustments(arg) {
 +            [] => true,
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(AutoBorrow::Ref(_, mu2)),
 +                    ..
 +                },
 +            ] => {
 +                // re-borrow with the same mutability is allowed
 +                let ty = cx.typeck_results().expr_ty(arg);
 +                matches!(*ty.kind(), ty::Ref(.., mu1) if mu1 == mu2.into())
 +            },
 +            _ => false,
 +        }
 +    })
 +}
 +
 +fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {
 +    let call_sig = call_ty.fn_sig(cx.tcx);
 +    if call_sig.unsafety() == Unsafety::Unsafe {
 +        return false;
 +    }
 +    if !closure_ty.has_late_bound_regions() {
 +        return true;
 +    }
 +    let substs = match closure_ty.kind() {
 +        ty::Closure(_, substs) => substs,
 +        _ => return false,
 +    };
 +    let closure_sig = cx.tcx.signature_unclosure(substs.as_closure().sig(), Unsafety::Normal);
 +    cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig)
 +}
 +
 +fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String {
 +    match cx.tcx.associated_item(method_def_id).container {
 +        ty::TraitContainer(def_id) => cx.tcx.def_path_str(def_id),
 +        ty::ImplContainer(def_id) => {
 +            let ty = cx.tcx.type_of(def_id);
 +            match ty.kind() {
 +                ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did),
 +                _ => ty.to_string(),
 +            }
 +        },
 +    }
 +}
index 503aac8ccd02628a8702a8db7958e3b151896c0e,0000000000000000000000000000000000000000..1e6feaac26c3ab77e604572bd461efcc27803ca3
mode 100644,000000..100644
--- /dev/null
@@@ -1,219 -1,0 +1,199 @@@
- use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
- use clippy_utils::{is_diag_trait_item, match_def_path, paths};
++use clippy_utils::is_diag_trait_item;
++use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::implements_trait;
- const FORMAT_MACRO_PATHS: &[&[&str]] = &[
-     &paths::FORMAT_ARGS_MACRO,
-     &paths::ASSERT_EQ_MACRO,
-     &paths::ASSERT_MACRO,
-     &paths::ASSERT_NE_MACRO,
-     &paths::EPRINT_MACRO,
-     &paths::EPRINTLN_MACRO,
-     &paths::PRINT_MACRO,
-     &paths::PRINTLN_MACRO,
-     &paths::WRITE_MACRO,
-     &paths::WRITELN_MACRO,
- ];
- const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_macro];
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `format!` within the arguments of another macro that does
 +    /// formatting such as `format!` itself, `write!` or `println!`. Suggests
 +    /// inlining the `format!` call.
 +    ///
 +    /// ### Why is this bad?
 +    /// The recommended code is both shorter and avoids a temporary allocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: {}", format!("something failed at {}", Location::caller()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FORMAT_IN_FORMAT_ARGS,
 +    perf,
 +    "`format!` used in a macro that does formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
 +    /// applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)
 +    /// in a macro that does formatting.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since the type implements `Display`, the use of `to_string` is
 +    /// unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller().to_string());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TO_STRING_IN_FORMAT_ARGS,
 +    perf,
 +    "`to_string` applied to a type that implements `Display` in format args"
 +}
 +
 +declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]);
 +
-             if FORMAT_MACRO_PATHS
-                 .iter()
-                 .any(|path| match_def_path(cx, macro_def_id, path))
-                 || FORMAT_MACRO_DIAG_ITEMS
-                     .iter()
-                     .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, macro_def_id));
 +impl<'tcx> LateLintPass<'tcx> for FormatArgs {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if_chain! {
 +            if let Some(format_args) = FormatArgsExpn::parse(cx, expr);
 +            let expr_expn_data = expr.span.ctxt().outer_expn_data();
 +            let outermost_expn_data = outermost_expn_data(expr_expn_data);
 +            if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
++            if is_format_macro(cx, macro_def_id);
 +            if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
 +            if let Some(args) = format_args.args();
 +            then {
 +                for (i, arg) in args.iter().enumerate() {
 +                    if arg.format_trait != sym::Display {
 +                        continue;
 +                    }
 +                    if arg.has_string_formatting() {
 +                        continue;
 +                    }
 +                    if is_aliased(&args, i) {
 +                        continue;
 +                    }
 +                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
 +                    check_to_string_in_format_args(cx, name, arg.value);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
 +    if expn_data.call_site.from_expansion() {
 +        outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
 +    } else {
 +        expn_data
 +    }
 +}
 +
 +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
 +    let expn_data = arg.span.ctxt().outer_expn_data();
 +    if expn_data.call_site.from_expansion() {
 +        return;
 +    }
 +    let Some(mac_id) = expn_data.macro_def_id else { return };
 +    if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
 +        return;
 +    }
 +    span_lint_and_then(
 +        cx,
 +        FORMAT_IN_FORMAT_ARGS,
 +        call_site,
 +        &format!("`format!` in `{}!` args", name),
 +        |diag| {
 +            diag.help(&format!(
 +                "combine the `format!(..)` arguments with the outer `{}!(..)` call",
 +                name
 +            ));
 +            diag.help("or consider changing `format!` to `format_args!`");
 +        },
 +    );
 +}
 +
 +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
 +    if_chain! {
 +        if !value.span.from_expansion();
 +        if let ExprKind::MethodCall(_, [receiver], _) = value.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id);
 +        if is_diag_trait_item(cx, method_def_id, sym::ToString);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            let (n_needed_derefs, target) = count_needed_derefs(
 +                receiver_ty,
 +                cx.typeck_results().expr_adjustments(receiver).iter(),
 +            );
 +            if implements_trait(cx, target, display_trait_id, &[]) {
 +                if n_needed_derefs == 0 {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        TO_STRING_IN_FORMAT_ARGS,
 +                        value.span.with_lo(receiver.span.hi()),
 +                        &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
 +                        "remove this",
 +                        String::new(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                } else {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        TO_STRING_IN_FORMAT_ARGS,
 +                        value.span,
 +                        &format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
 +                        "use this",
 +                        format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
 +fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
 +    let value = args[i].value;
 +    args.iter()
 +        .enumerate()
 +        .any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
 +}
 +
 +fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
 +where
 +    I: Iterator<Item = &'tcx Adjustment<'tcx>>,
 +{
 +    let mut n_total = 0;
 +    let mut n_needed = 0;
 +    loop {
 +        if let Some(Adjustment {
 +            kind: Adjust::Deref(overloaded_deref),
 +            target,
 +        }) = iter.next()
 +        {
 +            n_total += 1;
 +            if overloaded_deref.is_some() {
 +                n_needed = n_total;
 +            }
 +            ty = *target;
 +        } else {
 +            return (n_needed, ty);
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ef8be9e878f6cb6f60a9342455e6d00b904987ad
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,253 @@@
++use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
++use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
++use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{sym, symbol::kw, Symbol};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
++    /// which uses `self` as a parameter.
++    /// This is typically done indirectly with the `write!` macro or with `to_string()`.
++    ///
++    /// ### Why is this bad?
++    /// This will lead to infinite recursion and a stack overflow.
++    ///
++    /// ### Example
++    ///
++    /// ```rust
++    /// use std::fmt;
++    ///
++    /// struct Structure(i32);
++    /// impl fmt::Display for Structure {
++    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++    ///         write!(f, "{}", self.to_string())
++    ///     }
++    /// }
++    ///
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::fmt;
++    ///
++    /// struct Structure(i32);
++    /// impl fmt::Display for Structure {
++    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
++    ///         write!(f, "{}", self.0)
++    ///     }
++    /// }
++    /// ```
++    #[clippy::version = "1.48.0"]
++    pub RECURSIVE_FORMAT_IMPL,
++    correctness,
++    "Format trait method called while implementing the same Format trait"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for use of `println`, `print`, `eprintln` or `eprint` in an
++    /// implementation of a formatting trait.
++    ///
++    /// ### Why is this bad?
++    /// Using a print macro is likely unintentional since formatting traits
++    /// should write to the `Formatter`, not stdout/stderr.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::fmt::{Display, Error, Formatter};
++    ///
++    /// struct S;
++    /// impl Display for S {
++    ///     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
++    ///         println!("S");
++    ///
++    ///         Ok(())
++    ///     }
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::fmt::{Display, Error, Formatter};
++    ///
++    /// struct S;
++    /// impl Display for S {
++    ///     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
++    ///         writeln!(f, "S");
++    ///
++    ///         Ok(())
++    ///     }
++    /// }
++    /// ```
++    #[clippy::version = "1.61.0"]
++    pub PRINT_IN_FORMAT_IMPL,
++    suspicious,
++    "use of a print macro in a formatting trait impl"
++}
++
++#[derive(Clone, Copy)]
++struct FormatTrait {
++    /// e.g. `sym::Display`
++    name: Symbol,
++    /// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
++    formatter_name: Option<Symbol>,
++}
++
++#[derive(Default)]
++pub struct FormatImpl {
++    // Whether we are inside Display or Debug trait impl - None for neither
++    format_trait_impl: Option<FormatTrait>,
++}
++
++impl FormatImpl {
++    pub fn new() -> Self {
++        Self {
++            format_trait_impl: None,
++        }
++    }
++}
++
++impl_lint_pass!(FormatImpl => [RECURSIVE_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL]);
++
++impl<'tcx> LateLintPass<'tcx> for FormatImpl {
++    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
++        self.format_trait_impl = is_format_trait_impl(cx, impl_item);
++    }
++
++    fn check_impl_item_post(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
++        // Assume no nested Impl of Debug and Display within eachother
++        if is_format_trait_impl(cx, impl_item).is_some() {
++            self.format_trait_impl = None;
++        }
++    }
++
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        let Some(format_trait_impl) = self.format_trait_impl else { return };
++
++        if format_trait_impl.name == sym::Display {
++            check_to_string_in_display(cx, expr);
++        }
++
++        check_self_in_format_args(cx, expr, format_trait_impl);
++        check_print_in_format_impl(cx, expr, format_trait_impl);
++    }
++}
++
++fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
++    if_chain! {
++        // Get the hir_id of the object we are calling the method on
++        if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
++        // Is the method to_string() ?
++        if path.ident.name == sym!(to_string);
++        // Is the method a part of the ToString trait? (i.e. not to_string() implemented
++        // separately)
++        if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if is_diag_trait_item(cx, expr_def_id, sym::ToString);
++        // Is the method is called on self
++        if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind;
++        if let [segment] = path.segments;
++        if segment.ident.name == kw::SelfLower;
++        then {
++            span_lint(
++                cx,
++                RECURSIVE_FORMAT_IMPL,
++                expr.span,
++                "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
++            );
++        }
++    }
++}
++
++fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait) {
++    // Check each arg in format calls - do we ever use Display on self (directly or via deref)?
++    if_chain! {
++        if let Some(outer_macro) = root_macro_call_first_node(cx, expr);
++        if let macro_def_id = outer_macro.def_id;
++        if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
++        if is_format_macro(cx, macro_def_id);
++        if let Some(args) = format_args.args();
++        then {
++            for arg in args {
++                if arg.format_trait != impl_trait.name {
++                    continue;
++                }
++                check_format_arg_self(cx, expr, &arg, impl_trait);
++            }
++        }
++    }
++}
++
++fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
++    // Handle multiple dereferencing of references e.g. &&self
++    // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
++    // Since the argument to fmt is itself a reference: &self
++    let reference = peel_ref_operators(cx, arg.value);
++    let map = cx.tcx.hir();
++    // Is the reference self?
++    if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
++        let FormatTrait { name, .. } = impl_trait;
++        span_lint(
++            cx,
++            RECURSIVE_FORMAT_IMPL,
++            expr.span,
++            &format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
++        );
++    }
++}
++
++fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTrait) {
++    if_chain! {
++        if let Some(macro_call) = root_macro_call_first_node(cx, expr);
++        if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
++        then {
++            let replacement = match name {
++                sym::print_macro | sym::eprint_macro => "write",
++                sym::println_macro | sym::eprintln_macro => "writeln",
++                _ => return,
++            };
++
++            let name = name.as_str().strip_suffix("_macro").unwrap();
++
++            span_lint_and_sugg(
++                cx,
++                PRINT_IN_FORMAT_IMPL,
++                macro_call.span,
++                &format!("use of `{}!` in `{}` impl", name, impl_trait.name),
++                "replace with",
++                if let Some(formatter_name) = impl_trait.formatter_name {
++                    format!("{}!({}, ..)", replacement, formatter_name)
++                } else {
++                    format!("{}!(..)", replacement)
++                },
++                Applicability::HasPlaceholders,
++            );
++        }
++    }
++}
++
++fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTrait> {
++    if_chain! {
++        if impl_item.ident.name == sym::fmt;
++        if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
++        if let Some(Impl { of_trait: Some(trait_ref),..}) = get_parent_as_impl(cx.tcx, impl_item.hir_id());
++        if let Some(did) = trait_ref.trait_def_id();
++        if let Some(name) = cx.tcx.get_diagnostic_name(did);
++        if matches!(name, sym::Debug | sym::Display);
++        then {
++            let body = cx.tcx.hir().body(body_id);
++            let formatter_name = body.params.get(1)
++                .and_then(|param| param.pat.simple_ident())
++                .map(|ident| ident.name);
++
++            Some(FormatTrait {
++                name,
++                formatter_name,
++            })
++        } else {
++            None
++        }
++    }
++}
index b6badef02f58a989e4fa23332fded3c3d3fa7a99,0000000000000000000000000000000000000000..b2b9889f5dc74c21431c6bac8c98d842849044b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,256 -1,0 +1,259 @@@
- use clippy_utils::{get_trait_def_id, higher, match_def_path, path_def_id, paths};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-                 let not_double_ended = get_trait_def_id(cx, &paths::DOUBLE_ENDED_ITERATOR).map_or(false, |id| {
-                     !implements_trait(cx, cx.typeck_results().expr_ty(&args[0]), id, &[])
-                 });
++use clippy_utils::{higher, match_def_path, path_def_id, paths};
 +use rustc_hir::{BorrowKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::{sym, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for iteration that is guaranteed to be infinite.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there may be places where this is acceptable
 +    /// (e.g., in event streams), in most cases this is simply an error.
 +    ///
 +    /// ### Example
 +    /// ```no_run
 +    /// use std::iter;
 +    ///
 +    /// iter::repeat(1_u8).collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub INFINITE_ITER,
 +    correctness,
 +    "infinite iteration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for iteration that may be infinite.
 +    ///
 +    /// ### Why is this bad?
 +    /// While there may be places where this is acceptable
 +    /// (e.g., in event streams), in most cases this is simply an error.
 +    ///
 +    /// ### Known problems
 +    /// The code may have a condition to stop iteration, but
 +    /// this lint is not clever enough to analyze it.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let infinite_iter = 0..;
 +    /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MAYBE_INFINITE_ITER,
 +    pedantic,
 +    "possible infinite iteration"
 +}
 +
 +declare_lint_pass!(InfiniteIter => [INFINITE_ITER, MAYBE_INFINITE_ITER]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InfiniteIter {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (lint, msg) = match complete_infinite_iter(cx, expr) {
 +            Infinite => (INFINITE_ITER, "infinite iteration detected"),
 +            MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
 +            Finite => {
 +                return;
 +            },
 +        };
 +        span_lint(cx, lint, expr.span, msg);
 +    }
 +}
 +
 +#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 +enum Finiteness {
 +    Infinite,
 +    MaybeInfinite,
 +    Finite,
 +}
 +
 +use self::Finiteness::{Finite, Infinite, MaybeInfinite};
 +
 +impl Finiteness {
 +    #[must_use]
 +    fn and(self, b: Self) -> Self {
 +        match (self, b) {
 +            (Finite, _) | (_, Finite) => Finite,
 +            (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
 +            _ => Infinite,
 +        }
 +    }
 +
 +    #[must_use]
 +    fn or(self, b: Self) -> Self {
 +        match (self, b) {
 +            (Infinite, _) | (_, Infinite) => Infinite,
 +            (MaybeInfinite, _) | (_, MaybeInfinite) => MaybeInfinite,
 +            _ => Finite,
 +        }
 +    }
 +}
 +
 +impl From<bool> for Finiteness {
 +    #[must_use]
 +    fn from(b: bool) -> Self {
 +        if b { Infinite } else { Finite }
 +    }
 +}
 +
 +/// This tells us what to look for to know if the iterator returned by
 +/// this method is infinite
 +#[derive(Copy, Clone)]
 +enum Heuristic {
 +    /// infinite no matter what
 +    Always,
 +    /// infinite if the first argument is
 +    First,
 +    /// infinite if any of the supplied arguments is
 +    Any,
 +    /// infinite if all of the supplied arguments are
 +    All,
 +}
 +
 +use self::Heuristic::{All, Always, Any, First};
 +
 +/// a slice of (method name, number of args, heuristic, bounds) tuples
 +/// that will be used to determine whether the method in question
 +/// returns an infinite or possibly infinite iterator. The finiteness
 +/// is an upper bound, e.g., some methods can return a possibly
 +/// infinite iterator at worst, e.g., `take_while`.
 +const HEURISTICS: [(&str, usize, Heuristic, Finiteness); 19] = [
 +    ("zip", 2, All, Infinite),
 +    ("chain", 2, Any, Infinite),
 +    ("cycle", 1, Always, Infinite),
 +    ("map", 2, First, Infinite),
 +    ("by_ref", 1, First, Infinite),
 +    ("cloned", 1, First, Infinite),
 +    ("rev", 1, First, Infinite),
 +    ("inspect", 1, First, Infinite),
 +    ("enumerate", 1, First, Infinite),
 +    ("peekable", 2, First, Infinite),
 +    ("fuse", 1, First, Infinite),
 +    ("skip", 2, First, Infinite),
 +    ("skip_while", 1, First, Infinite),
 +    ("filter", 2, First, Infinite),
 +    ("filter_map", 2, First, Infinite),
 +    ("flat_map", 2, First, Infinite),
 +    ("unzip", 1, First, Infinite),
 +    ("take_while", 2, First, MaybeInfinite),
 +    ("scan", 3, First, MaybeInfinite),
 +];
 +
 +fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
 +    match expr.kind {
 +        ExprKind::MethodCall(method, args, _) => {
 +            for &(name, len, heuristic, cap) in &HEURISTICS {
 +                if method.ident.name.as_str() == name && args.len() == len {
 +                    return (match heuristic {
 +                        Always => Infinite,
 +                        First => is_infinite(cx, &args[0]),
 +                        Any => is_infinite(cx, &args[0]).or(is_infinite(cx, &args[1])),
 +                        All => is_infinite(cx, &args[0]).and(is_infinite(cx, &args[1])),
 +                    })
 +                    .and(cap);
 +                }
 +            }
 +            if method.ident.name == sym!(flat_map) && args.len() == 2 {
 +                if let ExprKind::Closure(_, _, body_id, _, _) = args[1].kind {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    return is_infinite(cx, &body.value);
 +                }
 +            }
 +            Finite
 +        },
 +        ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
 +        ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
 +        ExprKind::Call(path, _) => path_def_id(cx, path)
 +            .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
 +            .into(),
 +        ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
 +        _ => Finite,
 +    }
 +}
 +
 +/// the names and argument lengths of methods that *may* exhaust their
 +/// iterators
 +const POSSIBLY_COMPLETING_METHODS: [(&str, usize); 6] = [
 +    ("find", 2),
 +    ("rfind", 2),
 +    ("position", 2),
 +    ("rposition", 2),
 +    ("any", 2),
 +    ("all", 2),
 +];
 +
 +/// the names and argument lengths of methods that *always* exhaust
 +/// their iterators
 +const COMPLETING_METHODS: [(&str, usize); 12] = [
 +    ("count", 1),
 +    ("fold", 3),
 +    ("for_each", 2),
 +    ("partition", 2),
 +    ("max", 1),
 +    ("max_by", 2),
 +    ("max_by_key", 2),
 +    ("min", 1),
 +    ("min_by", 2),
 +    ("min_by_key", 2),
 +    ("sum", 1),
 +    ("product", 1),
 +];
 +
 +/// the paths of types that are known to be infinitely allocating
 +const INFINITE_COLLECTORS: &[Symbol] = &[
 +    sym::BinaryHeap,
 +    sym::BTreeMap,
 +    sym::BTreeSet,
 +    sym::HashMap,
 +    sym::HashSet,
 +    sym::LinkedList,
 +    sym::Vec,
 +    sym::VecDeque,
 +];
 +
 +fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
 +    match expr.kind {
 +        ExprKind::MethodCall(method, args, _) => {
 +            for &(name, len) in &COMPLETING_METHODS {
 +                if method.ident.name.as_str() == name && args.len() == len {
 +                    return is_infinite(cx, &args[0]);
 +                }
 +            }
 +            for &(name, len) in &POSSIBLY_COMPLETING_METHODS {
 +                if method.ident.name.as_str() == name && args.len() == len {
 +                    return MaybeInfinite.and(is_infinite(cx, &args[0]));
 +                }
 +            }
 +            if method.ident.name == sym!(last) && args.len() == 1 {
++                let not_double_ended = cx
++                    .tcx
++                    .get_diagnostic_item(sym::DoubleEndedIterator)
++                    .map_or(false, |id| {
++                        !implements_trait(cx, cx.typeck_results().expr_ty(&args[0]), id, &[])
++                    });
 +                if not_double_ended {
 +                    return is_infinite(cx, &args[0]);
 +                }
 +            } else if method.ident.name == sym!(collect) {
 +                let ty = cx.typeck_results().expr_ty(expr);
 +                if INFINITE_COLLECTORS
 +                    .iter()
 +                    .any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
 +                {
 +                    return is_infinite(cx, &args[0]);
 +                }
 +            }
 +        },
 +        ExprKind::Binary(op, l, r) => {
 +            if op.node.is_comparison() {
 +                return is_infinite(cx, l).and(is_infinite(cx, r)).and(MaybeInfinite);
 +            }
 +        }, // TODO: ExprKind::Loop + Match
 +        _ => (),
 +    }
 +    Finite
 +}
index 0191713f60d3f599f773303b61dfa3331c1170ae,0000000000000000000000000000000000000000..d1dc6b775c567ed467214db9d66683f9d506a7b9
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,172 @@@
-             let mut variants_size: Vec<VariantInfo> = adt
-                 .variants
-                 .iter()
-                 .enumerate()
-                 .map(|(i, variant)| {
-                     let mut fields_size = Vec::new();
-                     let size: u64 = variant
-                         .fields
-                         .iter()
-                         .enumerate()
-                         .filter_map(|(i, f)| {
-                             let ty = cx.tcx.type_of(f.did);
-                             // don't count generics by filtering out everything
-                             // that does not have a layout
-                             cx.layout_of(ty).ok().map(|l| {
-                                 let size = l.size.bytes();
-                                 fields_size.push(FieldInfo { ind: i, size });
-                                 size
-                             })
-                         })
-                         .sum();
-                     VariantInfo {
-                         ind: i,
-                         size,
-                         fields_size,
 +//! lint when there is a large size difference between variants on an enum
 +
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_with_applicability;
 +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_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.
 +    ///
 +    /// ### 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 {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
 +        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;
 +            }
-                 })
-                 .collect();
++            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;
 +                        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);
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
index 4721b7f2b472b3ffaab9da2a2e21b8f486cf8457,0000000000000000000000000000000000000000..f6d467941e3ef80dd629caba969e28cfdf208b16
mode 100644,000000..100644
--- /dev/null
@@@ -1,316 -1,0 +1,321 @@@
-     LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
 +// 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(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_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(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    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(collapsible_match::COLLAPSIBLE_MATCH),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    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(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_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    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(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +    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(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(get_last_with_len::GET_LAST_WITH_LEN),
 +    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::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_map::MANUAL_MAP),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +    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(match_str_case_mismatch::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    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::REDUNDANT_PATTERN_MATCHING),
 +    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::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::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    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_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    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::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_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(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_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
 +    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(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(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    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(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::TRANSMUTE_UNDEFINED_REPR),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(try_err::TRY_ERR),
 +    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(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
 +    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_ARG),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    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 1809f2cc7d46278cac14ff0488d59e415ef4521c,0000000000000000000000000000000000000000..c890523fe5aebc1808dba4de08d1d4956f2fca28
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,11 @@@
-     LintId::of(cargo_common_metadata::CARGO_COMMON_METADATA),
-     LintId::of(feature_name::NEGATIVE_FEATURE_NAMES),
-     LintId::of(feature_name::REDUNDANT_FEATURE_NAMES),
-     LintId::of(multiple_crate_versions::MULTIPLE_CRATE_VERSIONS),
-     LintId::of(wildcard_dependencies::WILDCARD_DEPENDENCIES),
 +// 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::cargo", Some("clippy_cargo"), vec![
++    LintId::of(cargo::CARGO_COMMON_METADATA),
++    LintId::of(cargo::MULTIPLE_CRATE_VERSIONS),
++    LintId::of(cargo::NEGATIVE_FEATURE_NAMES),
++    LintId::of(cargo::REDUNDANT_FEATURE_NAMES),
++    LintId::of(cargo::WILDCARD_DEPENDENCIES),
 +])
index 4217fd3a3ea72c04af802f688f39d34f1f501c7a,0000000000000000000000000000000000000000..35b1e644a8a71d63bd9afa2375a0d089cec14bb0
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,76 @@@
-     LintId::of(to_string_in_display::TO_STRING_IN_DISPLAY),
 +// 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(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(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(match_str_case_mismatch::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::TRANSMUTE_UNDEFINED_REPR),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
 +    LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
 +    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 a80320a578f0e7ba609c90fad800c08ab7c8b456,0000000000000000000000000000000000000000..686482671b482ae698579d25f42f7edb9893731a
mode 100644,000000..100644
--- /dev/null
@@@ -1,537 -1,0 +1,540 @@@
-     cargo_common_metadata::CARGO_COMMON_METADATA,
 +// 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::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,
 +    approx_const::APPROX_CONSTANT,
 +    arithmetic::FLOAT_ARITHMETIC,
 +    arithmetic::INTEGER_ARITHMETIC,
 +    as_conversions::AS_CONVERSIONS,
 +    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::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_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,
 +    bytecount::NAIVE_BYTECOUNT,
-     feature_name::NEGATIVE_FEATURE_NAMES,
-     feature_name::REDUNDANT_FEATURE_NAMES,
++    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_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::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,
 +    collapsible_match::COLLAPSIBLE_MATCH,
 +    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,
 +    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::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,
 +    double_comparison::DOUBLE_COMPARISONS,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_REF,
 +    drop_forget_ref::FORGET_COPY,
 +    drop_forget_ref::FORGET_REF,
 +    duration_subsec::DURATION_SUBSEC,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +    empty_enum::EMPTY_ENUM,
 +    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,
 +    eval_order_dependence::DIVERGING_SUB_EXPRESSION,
 +    eval_order_dependence::EVAL_ORDER_DEPENDENCE,
 +    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,
-     multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
 +    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,
 +    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,
 +    get_last_with_len::GET_LAST_WITH_LEN,
 +    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_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::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_map::MANUAL_MAP,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
 +    manual_ok_or::MANUAL_OK_OR,
 +    manual_strip::MANUAL_STRIP,
 +    manual_unwrap_or::MANUAL_UNWRAP_OR,
 +    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_on_vec_items::MATCH_ON_VEC_ITEMS,
 +    match_result_ok::MATCH_RESULT_OK,
 +    match_str_case_mismatch::MATCH_STR_CASE_MISMATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
 +    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::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_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
 +    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::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_SPLITN,
 +    methods::NEW_RET_NO_SELF,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    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_FOLD,
 +    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,
 +    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,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
 +    modulo_arithmetic::MODULO_ARITHMETIC,
-     to_string_in_display::TO_STRING_IN_DISPLAY,
 +    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_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
 +    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,
 +    octal_escapes::OCTAL_ESCAPES,
 +    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,
 +    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,
 +    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,
 +    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,
 +    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,
 +    tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +    temporary_assignment::TEMPORARY_ASSIGNMENT,
 +    to_digit_is_some::TO_DIGIT_IS_SOME,
-     wildcard_dependencies::WILDCARD_DEPENDENCIES,
 +    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,
 +    try_err::TRY_ERR,
 +    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,
 +    undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
 +    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_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_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 8d4dde42bbecad322b0d3ba28da5e24a8ac8d7f2,0000000000000000000000000000000000000000..a7353790100267681b11ae92b234125b48571dfe
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,31 @@@
-     LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
 +// 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(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(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(strings::STRING_LIT_AS_BYTES),
 +    LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
 +    LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(use_self::USE_SELF),
 +])
index 1292675f4a96cde9b178869674b143c6e5038907,0000000000000000000000000000000000000000..00d305131810df7418ae29eb519c660cbd954746
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,99 @@@
-     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
-     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +// 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(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(match_on_vec_items::MATCH_ON_VEC_ITEMS),
 +    LintId::of(matches::MATCH_BOOL),
 +    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(misc::FLOAT_CMP),
 +    LintId::of(misc::USED_UNDERSCORE_BINDING),
 +    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(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(unit_types::LET_UNIT_VALUE),
 +    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 5a89fdb05a9904ab1442fd73bada9b12c7be9a37,0000000000000000000000000000000000000000..f89f35b885c15a377c772e103254c0b0884aeebf
mode 100644,000000..100644
--- /dev/null
@@@ -1,73 -1,0 +1,74 @@@
 +// 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(arithmetic::FLOAT_ARITHMETIC),
 +    LintId::of(arithmetic::INTEGER_ARITHMETIC),
 +    LintId::of(as_conversions::AS_CONVERSIONS),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    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(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(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::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(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +    LintId::of(modulo_arithmetic::MODULO_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(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 10f8ae4b7f7fca8583cde6a976955579a3efc4e1,0000000000000000000000000000000000000000..465baa8258174e59f14d91b941dbe4bae33d3936
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,25 @@@
 +// 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(assign_ops::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
++    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
++    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
++    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
 +    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(methods::SUSPICIOUS_MAP),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +])
index 85256ff0e995fa247c5ab0622371fce176a366c2,0000000000000000000000000000000000000000..230e2b2ccdfb597e8fe293ce666587f746ad22fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,960 -1,0 +1,961 @@@
- mod cargo_common_metadata;
 +// error-pattern:cargo-clippy
 +
 +#![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(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_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;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod absurd_extreme_comparisons;
 +mod approx_const;
 +mod arithmetic;
 +mod as_conversions;
 +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 bytecount;
- mod feature_name;
++mod cargo;
 +mod case_sensitive_file_extension_comparisons;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod collapsible_match;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +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 double_comparison;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duration_subsec;
 +mod else_if_without_else;
 +mod empty_enum;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod eq_op;
 +mod equatable_if_let;
 +mod erasing_op;
 +mod escape;
 +mod eta_reduction;
 +mod eval_order_dependence;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
- mod multiple_crate_versions;
 +mod float_equality_without_abs;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
++mod format_impl;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
 +mod get_last_with_len;
 +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_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_map;
 +mod manual_non_exhaustive;
 +mod manual_ok_or;
 +mod manual_strip;
 +mod manual_unwrap_or;
 +mod map_clone;
 +mod map_err_ignore;
 +mod map_unit_fn;
 +mod match_on_vec_items;
 +mod match_result_ok;
 +mod match_str_case_mismatch;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod module_style;
 +mod modulo_arithmetic;
- mod to_string_in_display;
 +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_option_as_deref;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod non_send_fields_in_send_ty;
 +mod nonstandard_macro_braces;
 +mod octal_escapes;
 +mod 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 question_mark;
 +mod ranges;
 +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 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 tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
- mod wildcard_dependencies;
 +mod trailing_empty_array;
 +mod trait_bounds;
 +mod transmute;
 +mod transmuting_null;
 +mod try_err;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod undropped_manually_drops;
 +mod unicode;
 +mod uninit_vec;
 +mod unit_hash;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +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_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;
-     store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
 +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::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 }));
-     store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
 +}
 +
 +#[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(),
 +            error
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[allow(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::inspector::DeepCodeInspector));
 +        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::author::Author));
 +    store.register_late_pass(|| Box::new(await_holding_invalid::AwaitHolding));
 +    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_option_as_deref::OptionNeedlessDeref));
 +    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(collapsible_match::CollapsibleMatch));
 +    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;
 +    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)));
 +    store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustive::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(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(get_last_with_len::GetLastWithLen));
 +    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(arithmetic::Arithmetic::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(eval_order_dependence::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(try_err::TryErr));
 +    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));
-     let cargo_ignore_publish = conf.cargo_ignore_publish;
-     store.register_late_pass(move || Box::new(cargo_common_metadata::CargoCommonMetadata::new(cargo_ignore_publish)));
-     store.register_late_pass(|| Box::new(multiple_crate_versions::MultipleCrateVersions));
-     store.register_late_pass(|| Box::new(wildcard_dependencies::WildcardDependencies));
++    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));
-     store.register_late_pass(move || Box::new(feature_name::FeatureName));
 +    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(match_on_vec_items::MatchOnVecItems));
 +    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_unwrap_or::ManualUnwrapOr));
 +    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(undropped_manually_drops::UndroppedManuallyDrops));
 +    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(manual_map::ManualMap));
 +    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::default()));
 +    store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
 +    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_late_pass(|| Box::new(dbg_macro::DbgMacro));
++    let cargo_ignore_publish = conf.cargo_ignore_publish;
++    store.register_late_pass(move || {
++        Box::new(cargo::Cargo {
++            ignore_publish: cargo_ignore_publish,
++        })
++    });
 +    // 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) {
 +    // NOTE: when renaming a lint, add a corresponding test to tests/ui/rename.rs
 +    ls.register_renamed("clippy::stutter", "clippy::module_name_repetitions");
 +    ls.register_renamed("clippy::new_without_default_derive", "clippy::new_without_default");
 +    ls.register_renamed("clippy::cyclomatic_complexity", "clippy::cognitive_complexity");
 +    ls.register_renamed("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes");
 +    ls.register_renamed("clippy::option_and_then_some", "clippy::bind_instead_of_map");
 +    ls.register_renamed("clippy::box_vec", "clippy::box_collection");
 +    ls.register_renamed("clippy::block_in_if_condition_expr", "clippy::blocks_in_if_conditions");
 +    ls.register_renamed("clippy::block_in_if_condition_stmt", "clippy::blocks_in_if_conditions");
 +    ls.register_renamed("clippy::option_map_unwrap_or", "clippy::map_unwrap_or");
 +    ls.register_renamed("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or");
 +    ls.register_renamed("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or");
 +    ls.register_renamed("clippy::option_unwrap_used", "clippy::unwrap_used");
 +    ls.register_renamed("clippy::result_unwrap_used", "clippy::unwrap_used");
 +    ls.register_renamed("clippy::option_expect_used", "clippy::expect_used");
 +    ls.register_renamed("clippy::result_expect_used", "clippy::expect_used");
 +    ls.register_renamed("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles");
 +    ls.register_renamed("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles");
 +    ls.register_renamed("clippy::identity_conversion", "clippy::useless_conversion");
 +    ls.register_renamed("clippy::zero_width_space", "clippy::invisible_characters");
 +    ls.register_renamed("clippy::single_char_push_str", "clippy::single_char_add_str");
 +    ls.register_renamed("clippy::if_let_some_result", "clippy::match_result_ok");
 +    ls.register_renamed("clippy::disallowed_type", "clippy::disallowed_types");
 +    ls.register_renamed("clippy::disallowed_method", "clippy::disallowed_methods");
 +    ls.register_renamed("clippy::ref_in_deref", "clippy::needless_borrow");
++    ls.register_renamed("clippy::to_string_in_display", "clippy::recursive_format_impl");
 +
 +    // uplifted lints
 +    ls.register_renamed("clippy::invalid_ref", "invalid_value");
 +    ls.register_renamed("clippy::into_iter_on_array", "array_into_iter");
 +    ls.register_renamed("clippy::unused_label", "unused_labels");
 +    ls.register_renamed("clippy::drop_bounds", "drop_bounds");
 +    ls.register_renamed("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr");
 +    ls.register_renamed("clippy::panic_params", "non_fmt_panics");
 +    ls.register_renamed("clippy::unknown_clippy_lints", "unknown_lints");
 +    ls.register_renamed("clippy::invalid_atomic_ordering", "invalid_atomic_ordering");
 +    ls.register_renamed("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums");
 +}
 +
 +// 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 809aa168a7a0ee2f8152dc3f0b7884a9f20dba5e,0000000000000000000000000000000000000000..dcf44303cf449f2a0b83322414643fdd7243e6db
mode 100644,000000..100644
--- /dev/null
@@@ -1,109 -1,0 +1,110 @@@
- use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
-         if match_def_path(cx, def_id, &paths::MEM_SIZE_OF);
++use clippy_utils::{meets_msrv, msrvs};
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of `std::mem::size_of::<T>() * 8` when
 +    /// `T::BITS` is available.
 +    ///
 +    /// ### Why is this bad?
 +    /// Can be written as the shorter `T::BITS`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// std::mem::size_of::<usize>() * 8;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// usize::BITS;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub MANUAL_BITS,
 +    style,
 +    "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`"
 +}
 +
 +#[derive(Clone)]
 +pub struct ManualBits {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl ManualBits {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualBits {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind;
 +            if let BinOpKind::Mul = &bin_op.node;
 +            if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr);
 +            if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_));
 +            if let ExprKind::Lit(lit) = &other_expr.kind;
 +            if let LitKind::Int(8, _) = lit.node;
 +
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MANUAL_BITS,
 +                    expr.span,
 +                    "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits",
 +                    "consider using",
 +                    format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn get_one_size_of_ty<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr1: &'tcx Expr<'_>,
 +    expr2: &'tcx Expr<'_>,
 +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> {
 +    match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) {
 +        (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)),
 +        (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)),
 +        _ => None,
 +    }
 +}
 +
 +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
 +    if_chain! {
 +        if let ExprKind::Call(count_func, _func_args) = expr.kind;
 +        if let ExprKind::Path(ref count_func_qpath) = count_func.kind;
 +
 +        if let QPath::Resolved(_, count_func_path) = count_func_qpath;
 +        if let Some(segment_zero) = count_func_path.segments.get(0);
 +        if let Some(args) = segment_zero.args;
 +        if let Some(GenericArg::Type(real_ty)) = args.args.get(0);
 +
 +        if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id();
++        if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id);
 +        then {
 +            cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty))
 +        } else {
 +            None
 +        }
 +    }
 +}
index d605b6d73c09d8b9684e4d2b83a76a91a19b82f2,0000000000000000000000000000000000000000..2e1f7646eb400b3924ff813738c90091205e139f
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,169 @@@
- use rustc_hir::{BorrowKind, Expr, ExprKind, Guard, MatchSource, Pat};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{higher, is_wild};
 +use rustc_ast::{Attribute, LitKind};
 +use rustc_errors::Applicability;
- pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
++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!`
-         return find_matches_sugg(
++pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    if let Some(higher::IfLet {
 +        let_pat,
 +        let_expr,
 +        if_then,
 +        if_else: Some(if_else),
 +    }) = higher::IfLet::hir(cx, expr)
 +    {
-     if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
-         return find_matches_sugg(
-             cx,
-             scrut,
-             arms.iter().map(|arm| {
-                 (
-                     cx.tcx.hir().attrs(arm.hir_id),
-                     Some(arm.pat),
-                     arm.body,
-                     arm.guard.as_ref(),
-                 )
-             }),
-             expr,
-             false,
-         );
-     }
-     false
++        find_matches_sugg(
 +            cx,
 +            let_expr,
 +            IntoIterator::into_iter([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, 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 271a386859555040a8c43990020700447e7b9c9e,0000000000000000000000000000000000000000..d11dda57e6fd94c2676a4ef1a0fc2d95bc4d8feb
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,109 @@@
- use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdSet, MatchSource, Pat, PatKind};
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{path_to_local, search_same, SpanlessEq, SpanlessHash};
- pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
-     if let ExprKind::Match(_, arms, MatchSource::Normal) = expr.kind {
-         let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
-             let mut h = SpanlessHash::new(cx);
-             h.hash_expr(arm.body);
-             h.finish()
-         };
++use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdSet, Pat, PatKind};
 +use rustc_lint::LateContext;
 +use std::collections::hash_map::Entry;
 +
 +use super::MATCH_SAME_ARMS;
 +
-         let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
-             let min_index = usize::min(lindex, rindex);
-             let max_index = usize::max(lindex, rindex);
++pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
++    let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
++        let mut h = SpanlessHash::new(cx);
++        h.hash_expr(arm.body);
++        h.finish()
++    };
 +
-             let mut local_map: HirIdMap<HirId> = HirIdMap::default();
-             let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
-                 if_chain! {
-                     if let Some(a_id) = path_to_local(a);
-                     if let Some(b_id) = path_to_local(b);
-                     let entry = match local_map.entry(a_id) {
-                         Entry::Vacant(entry) => entry,
-                         // check if using the same bindings as before
-                         Entry::Occupied(entry) => return *entry.get() == b_id,
-                     };
-                     // the names technically don't have to match; this makes the lint more conservative
-                     if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
-                     if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
-                     if pat_contains_local(lhs.pat, a_id);
-                     if pat_contains_local(rhs.pat, b_id);
-                     then {
-                         entry.insert(b_id);
-                         true
-                     } else {
-                         false
-                     }
++    let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
++        let min_index = usize::min(lindex, rindex);
++        let max_index = usize::max(lindex, rindex);
 +
-             };
-             // Arms with a guard are ignored, those can’t always be merged together
-             // This is also the case for arms in-between each there is an arm with a guard
-             (min_index..=max_index).all(|index| arms[index].guard.is_none())
-                 && SpanlessEq::new(cx)
-                     .expr_fallback(eq_fallback)
-                     .eq_expr(lhs.body, rhs.body)
-                 // these checks could be removed to allow unused bindings
-                 && bindings_eq(lhs.pat, local_map.keys().copied().collect())
-                 && bindings_eq(rhs.pat, local_map.values().copied().collect())
++        let mut local_map: HirIdMap<HirId> = HirIdMap::default();
++        let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
++            if_chain! {
++                if let Some(a_id) = path_to_local(a);
++                if let Some(b_id) = path_to_local(b);
++                let entry = match local_map.entry(a_id) {
++                    Entry::Vacant(entry) => entry,
++                    // check if using the same bindings as before
++                    Entry::Occupied(entry) => return *entry.get() == b_id,
++                };
++                // the names technically don't have to match; this makes the lint more conservative
++                if cx.tcx.hir().name(a_id) == cx.tcx.hir().name(b_id);
++                if cx.typeck_results().expr_ty(a) == cx.typeck_results().expr_ty(b);
++                if pat_contains_local(lhs.pat, a_id);
++                if pat_contains_local(rhs.pat, b_id);
++                then {
++                    entry.insert(b_id);
++                    true
++                } else {
++                    false
 +                }
-         let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
-         for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
-             span_lint_and_then(
-                 cx,
-                 MATCH_SAME_ARMS,
-                 j.body.span,
-                 "this `match` has identical arm bodies",
-                 |diag| {
-                     diag.span_note(i.body.span, "same as this");
++            }
 +        };
++        // Arms with a guard are ignored, those can’t always be merged together
++        // This is also the case for arms in-between each there is an arm with a guard
++        (min_index..=max_index).all(|index| arms[index].guard.is_none())
++            && SpanlessEq::new(cx)
++                .expr_fallback(eq_fallback)
++                .eq_expr(lhs.body, rhs.body)
++            // these checks could be removed to allow unused bindings
++            && bindings_eq(lhs.pat, local_map.keys().copied().collect())
++            && bindings_eq(rhs.pat, local_map.values().copied().collect())
++    };
 +
-                     // Note: this does not use `span_suggestion` on purpose:
-                     // there is no clean way
-                     // to remove the other arm. Building a span and suggest to replace it to ""
-                     // makes an even more confusing error message. Also in order not to make up a
-                     // span for the whole pattern, the suggestion is only shown when there is only
-                     // one pattern. The user should know about `|` if they are already using it…
++    let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
++    for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
++        span_lint_and_then(
++            cx,
++            MATCH_SAME_ARMS,
++            j.body.span,
++            "this `match` has identical arm bodies",
++            |diag| {
++                diag.span_note(i.body.span, "same as this");
 +
-                     let lhs = snippet(cx, i.pat.span, "<pat1>");
-                     let rhs = snippet(cx, j.pat.span, "<pat2>");
++                // Note: this does not use `span_suggestion` on purpose:
++                // there is no clean way
++                // to remove the other arm. Building a span and suggest to replace it to ""
++                // makes an even more confusing error message. Also in order not to make up a
++                // span for the whole pattern, the suggestion is only shown when there is only
++                // one pattern. The user should know about `|` if they are already using it…
 +
-                     if let PatKind::Wild = j.pat.kind {
-                         // if the last arm is _, then i could be integrated into _
-                         // note that i.pat cannot be _, because that would mean that we're
-                         // hiding all the subsequent arms, and rust won't compile
-                         diag.span_note(
-                             i.body.span,
-                             &format!(
-                                 "`{}` has the same arm body as the `_` wildcard, consider removing it",
-                                 lhs
-                             ),
-                         );
-                     } else {
-                         diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
-                             .help("...or consider changing the match arm bodies");
-                     }
-                 },
-             );
-         }
++                let lhs = snippet(cx, i.pat.span, "<pat1>");
++                let rhs = snippet(cx, j.pat.span, "<pat2>");
 +
++                if let PatKind::Wild = j.pat.kind {
++                    // if the last arm is _, then i could be integrated into _
++                    // note that i.pat cannot be _, because that would mean that we're
++                    // hiding all the subsequent arms, and rust won't compile
++                    diag.span_note(
++                        i.body.span,
++                        &format!(
++                            "`{}` has the same arm body as the `_` wildcard, consider removing it",
++                            lhs
++                        ),
++                    );
++                } else {
++                    diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs,))
++                        .help("...or consider changing the match arm bodies");
++                }
++            },
++        );
 +    }
 +}
 +
 +fn pat_contains_local(pat: &Pat<'_>, id: HirId) -> bool {
 +    let mut result = false;
 +    pat.walk_short(|p| {
 +        result |= matches!(p.kind, PatKind::Binding(_, binding_id, ..) if binding_id == id);
 +        !result
 +    });
 +    result
 +}
 +
 +/// Returns true if all the bindings in the `Pat` are in `ids` and vice versa
 +fn bindings_eq(pat: &Pat<'_>, mut ids: HirIdSet) -> bool {
 +    let mut result = true;
 +    pat.each_binding_or_first(&mut |_, id, _, _| result &= ids.remove(&id));
 +    result && ids.is_empty()
 +}
index 8ae19e03f1a6a5afbfb1016ebd066c4da9d76d0c,0000000000000000000000000000000000000000..39fe54648fbc754c37f5fba926d8ba9d45a54f45
mode 100644,000000..100644
--- /dev/null
@@@ -1,166 -1,0 +1,149 @@@
- use clippy_utils::source::{indent_of, snippet_block, snippet_opt, snippet_with_applicability};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-     // HACK:
-     // This is a hack to deal with arms that are excluded by macros like `#[cfg]`. It is only used here
-     // to prevent false positives as there is currently no better way to detect if code was excluded by
-     // a macro. See PR #6435
-     if_chain! {
-         if let Some(match_snippet) = snippet_opt(cx, expr.span);
-         if let Some(arm_snippet) = snippet_opt(cx, arms[0].span);
-         if let Some(ex_snippet) = snippet_opt(cx, ex.span);
-         let rest_snippet = match_snippet.replace(&arm_snippet, "").replace(&ex_snippet, "");
-         if rest_snippet.contains("=>");
-         then {
-             // The code it self contains another thick arrow "=>"
-             // -> Either another arm or a comment
-             return;
-         }
-     }
++use clippy_utils::source::{indent_of, snippet_block, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, is_refutable, peel_blocks};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, Expr, ExprKind, Local, Node, PatKind};
 +use rustc_lint::LateContext;
 +
 +use super::MATCH_SINGLE_BINDING;
 +
 +#[allow(clippy::too_many_lines)]
 +pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if expr.span.from_expansion() || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
 +        return;
 +    }
 +
 +    let matched_vars = ex.span;
 +    let bind_names = arms[0].pat.span;
 +    let match_body = peel_blocks(arms[0].body);
 +    let mut snippet_body = if match_body.span.from_expansion() {
 +        Sugg::hir_with_macro_callsite(cx, match_body, "..").to_string()
 +    } else {
 +        snippet_block(cx, match_body.span, "..", Some(expr.span)).to_string()
 +    };
 +
 +    // Do we need to add ';' to suggestion ?
 +    match match_body.kind {
 +        ExprKind::Block(block, _) => {
 +            // macro + expr_ty(body) == ()
 +            if block.span.from_expansion() && cx.typeck_results().expr_ty(match_body).is_unit() {
 +                snippet_body.push(';');
 +            }
 +        },
 +        _ => {
 +            // expr_ty(body) == ()
 +            if cx.typeck_results().expr_ty(match_body).is_unit() {
 +                snippet_body.push(';');
 +            }
 +        },
 +    }
 +
 +    let mut applicability = Applicability::MaybeIncorrect;
 +    match arms[0].pat.kind {
 +        PatKind::Binding(..) | PatKind::Tuple(_, _) | PatKind::Struct(..) => {
 +            // If this match is in a local (`let`) stmt
 +            let (target_span, sugg) = if let Some(parent_let_node) = opt_parent_let(cx, ex) {
 +                (
 +                    parent_let_node.span,
 +                    format!(
 +                        "let {} = {};\n{}let {} = {};",
 +                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
 +                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
 +                        " ".repeat(indent_of(cx, expr.span).unwrap_or(0)),
 +                        snippet_with_applicability(cx, parent_let_node.pat.span, "..", &mut applicability),
 +                        snippet_body
 +                    ),
 +                )
 +            } else {
 +                // If we are in closure, we need curly braces around suggestion
 +                let mut indent = " ".repeat(indent_of(cx, ex.span).unwrap_or(0));
 +                let (mut cbrace_start, mut cbrace_end) = ("".to_string(), "".to_string());
 +                if let Some(parent_expr) = get_parent_expr(cx, expr) {
 +                    if let ExprKind::Closure(..) = parent_expr.kind {
 +                        cbrace_end = format!("\n{}}}", indent);
 +                        // Fix body indent due to the closure
 +                        indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
 +                        cbrace_start = format!("{{\n{}", indent);
 +                    }
 +                }
 +                // If the parent is already an arm, and the body is another match statement,
 +                // we need curly braces around suggestion
 +                let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
 +                if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
 +                    if let ExprKind::Match(..) = arm.body.kind {
 +                        cbrace_end = format!("\n{}}}", indent);
 +                        // Fix body indent due to the match
 +                        indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
 +                        cbrace_start = format!("{{\n{}", indent);
 +                    }
 +                }
 +                (
 +                    expr.span,
 +                    format!(
 +                        "{}let {} = {};\n{}{}{}",
 +                        cbrace_start,
 +                        snippet_with_applicability(cx, bind_names, "..", &mut applicability),
 +                        snippet_with_applicability(cx, matched_vars, "..", &mut applicability),
 +                        indent,
 +                        snippet_body,
 +                        cbrace_end
 +                    ),
 +                )
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_SINGLE_BINDING,
 +                target_span,
 +                "this match could be written as a `let` statement",
 +                "consider using `let` statement",
 +                sugg,
 +                applicability,
 +            );
 +        },
 +        PatKind::Wild => {
 +            if ex.can_have_side_effects() {
 +                let indent = " ".repeat(indent_of(cx, expr.span).unwrap_or(0));
 +                let sugg = format!(
 +                    "{};\n{}{}",
 +                    snippet_with_applicability(cx, ex.span, "..", &mut applicability),
 +                    indent,
 +                    snippet_body
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its scrutinee and body",
 +                    "consider using the scrutinee and body instead",
 +                    sugg,
 +                    applicability,
 +                );
 +            } else {
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_SINGLE_BINDING,
 +                    expr.span,
 +                    "this match could be replaced by its body itself",
 +                    "consider using the match body instead",
 +                    snippet_body,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        },
 +        _ => (),
 +    }
 +}
 +
 +/// Returns true if the `ex` match expression is in a local (`let`) statement
 +fn opt_parent_let<'a>(cx: &LateContext<'a>, ex: &Expr<'a>) -> Option<&'a Local<'a>> {
 +    let map = &cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Expr(parent_arm_expr)) = map.find(map.get_parent_node(ex.hir_id));
 +        if let Some(Node::Local(parent_let_expr)) = map.find(map.get_parent_node(parent_arm_expr.hir_id));
 +        then {
 +            return Some(parent_let_expr);
 +        }
 +    }
 +    None
 +}
index b5ee4561f06eceeb346ea5f648adf7357ddb9ea6,0000000000000000000000000000000000000000..92179eb6f0e60e5f7c8a90bdd1e6daa7b54d742c
mode 100644,000000..100644
--- /dev/null
@@@ -1,646 -1,0 +1,746 @@@
- use rustc_hir::{Expr, ExprKind, Local, MatchSource, Pat};
++use clippy_utils::source::{snippet_opt, walk_span_to_context};
 +use clippy_utils::{meets_msrv, msrvs};
-         redundant_pattern_match::check(cx, expr);
++use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
++use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{Span, SpanData, SyntaxContext};
 +
 +mod infalliable_detructuring_match;
 +mod match_as_ref;
 +mod match_bool;
 +mod match_like_matches;
 +mod match_ref_pats;
 +mod match_same_arms;
 +mod match_single_binding;
 +mod match_wild_enum;
 +mod match_wild_err_arm;
 +mod overlapping_arms;
 +mod redundant_pattern_match;
 +mod rest_pat_in_fully_bound_struct;
 +mod single_match;
 +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"
 +}
 +
 +#[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,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Matches {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
-         if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
-             if !match_like_matches::check(cx, expr) {
-                 match_same_arms::check(cx, expr);
++        if let ExprKind::Match(ex, arms, source) = expr.kind {
++            if !contains_cfg_arm(cx, expr, ex, arms) {
++                if source == MatchSource::Normal {
++                    if !(meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO)
++                        && match_like_matches::check_match(cx, expr, ex, arms))
++                    {
++                        match_same_arms::check(cx, arms);
++                    }
 +
-         } else {
-             match_same_arms::check(cx, expr);
-         }
++                    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);
++
++                    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);
 +            }
-         if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
-             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);
++            // These don't depend on a relationship between multiple arms
 +            match_wild_err_arm::check(cx, ex, arms);
-             if self.infallible_destructuring_match_linted {
-                 self.infallible_destructuring_match_linted = false;
-             } else {
-                 match_single_binding::check(cx, ex, arms, expr);
 +            wild_in_or_pats::check(cx, arms);
-         }
-         if let ExprKind::Match(ex, arms, _) = expr.kind {
-             match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
++        } else {
++            if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
++                match_like_matches::check(cx, expr);
 +            }
++            redundant_pattern_match::check(cx, expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 +        self.infallible_destructuring_match_linted |= infalliable_detructuring_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 677b8cdf2ba0c80bc64dc4618a074cd6f349ec5b,0000000000000000000000000000000000000000..777ec9b75bc24705494bf68c10fe2be018f83401
mode 100644,000000..100644
--- /dev/null
@@@ -1,436 -1,0 +1,432 @@@
-     Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
 +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::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
 +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_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, PollPending};
 +use rustc_hir::{
 +    intravisit::{walk_expr, Visitor},
- pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++    Arm, Block, Expr, ExprKind, LangItem, Node, Pat, PatKind, QPath, UnOp,
 +};
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
 +use rustc_span::sym;
 +
-     }
-     if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
-         find_sugg_for_match(cx, expr, op, arms);
-     }
-     if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    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());
-             &ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, ty, seen),
++    } else 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);
 +    }
 +}
 +
 +/// 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.
 +fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 +}
 +
 +fn type_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.needs_drop(cx.tcx, cx.param_env) {
 +        false
 +    } 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| type_needs_ordered_drop_inner(cx, ty, seen)),
- fn find_sugg_for_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op: &Expr<'_>, arms: &[Arm<'_>]) {
++            ty::Array(ty, _) => type_needs_ordered_drop_inner(cx, *ty, seen),
 +            ty::Adt(adt, subs) => adt
 +                .all_fields()
 +                .map(|f| f.ty(cx.tcx, subs))
 +                .any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
 +            _ => true,
 +        }
 +    }
 +    // Check for std types which implement drop, but only for memory allocation.
 +    else if is_type_diagnostic_item(cx, ty, sym::Vec)
 +        || is_type_lang_item(cx, ty, LangItem::OwnedBox)
 +        || is_type_diagnostic_item(cx, ty, sym::Rc)
 +        || is_type_diagnostic_item(cx, ty, sym::Arc)
 +        || is_type_diagnostic_item(cx, ty, sym::cstring_type)
 +        || is_type_diagnostic_item(cx, ty, sym::BTreeMap)
 +        || is_type_diagnostic_item(cx, ty, sym::LinkedList)
 +        || 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| type_needs_ordered_drop_inner(cx, ty, seen))
 +        } else {
 +            true
 +        }
 +    } else {
 +        true
 +    }
 +}
 +
 +// 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 type_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 type_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 && type_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().and_then(|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 = type_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
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[allow(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 5ffcfd4d2641709c5fa7fd0f8f2fd824e2c5cd24,0000000000000000000000000000000000000000..d6c235b5a693a96975c006c3f5ee123f17a82fa5
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,46 @@@
- use clippy_utils::{match_def_path, paths};
 +use clippy_utils::diagnostics::span_lint;
-                     if match_def_path(cx, def_id, &paths::MEM_FORGET) {
 +use rustc_hir::{Expr, ExprKind};
 +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 `std::mem::forget(t)` where `t` is
 +    /// `Drop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `std::mem::forget(t)` prevents `t` from running its
 +    /// destructor, possibly causing leaks.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::mem;
 +    /// # use std::rc::Rc;
 +    /// mem::forget(Rc::new(55))
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MEM_FORGET,
 +    restriction,
 +    "`mem::forget` usage on `Drop` types, likely to cause memory leaks"
 +}
 +
 +declare_lint_pass!(MemForget => [MEM_FORGET]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MemForget {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if let ExprKind::Call(path_expr, [ref first_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::mem_forget, def_id) {
 +                        let forgot_ty = cx.typeck_results().expr_ty(first_arg);
 +
 +                        if forgot_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
 +                            span_lint(cx, MEM_FORGET, e.span, "usage of `mem::forget` on `Drop` type");
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index a184806d021bcfcf86617c9fff35eaba84cff16a,0000000000000000000000000000000000000000..054937e3e36b9970bb8b9a2f0da0c6a1a61c4ff6
mode 100644,000000..100644
--- /dev/null
@@@ -1,264 -1,0 +1,264 @@@
- use clippy_utils::{is_default_equivalent, is_lang_ctor, match_def_path, meets_msrv, msrvs, paths};
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::ty::is_non_aggregate_primitive_type;
-             if match_def_path(cx, def_id, &paths::MEM_REPLACE);
++use clippy_utils::{is_default_equivalent, is_lang_ctor, meets_msrv, msrvs};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::OptionNone;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `mem::replace()` on an `Option` with
 +    /// `None`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` already has the method `take()` for
 +    /// taking its current value (Some(..) or None) and replacing it with
 +    /// `None`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::mem;
 +    ///
 +    /// let mut an_option = Some(0);
 +    /// let replaced = mem::replace(&mut an_option, None);
 +    /// ```
 +    /// Is better expressed with:
 +    /// ```rust
 +    /// let mut an_option = Some(0);
 +    /// let taken = an_option.take();
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MEM_REPLACE_OPTION_WITH_NONE,
 +    style,
 +    "replacing an `Option` with `None` instead of `take()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `mem::replace(&mut _, mem::uninitialized())`
 +    /// and `mem::replace(&mut _, mem::zeroed())`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This will lead to undefined behavior even if the
 +    /// value is overwritten later, because the uninitialized value may be
 +    /// observed in the case of a panic.
 +    ///
 +    /// ### Example
 +    /// ```
 +    /// use std::mem;
 +    ///# fn may_panic(v: Vec<i32>) -> Vec<i32> { v }
 +    ///
 +    /// #[allow(deprecated, invalid_value)]
 +    /// fn myfunc (v: &mut Vec<i32>) {
 +    ///     let taken_v = unsafe { mem::replace(v, mem::uninitialized()) };
 +    ///     let new_v = may_panic(taken_v); // undefined behavior on panic
 +    ///     mem::forget(mem::replace(v, new_v));
 +    /// }
 +    /// ```
 +    ///
 +    /// The [take_mut](https://docs.rs/take_mut) crate offers a sound solution,
 +    /// at the cost of either lazily creating a replacement value or aborting
 +    /// on panic, to ensure that the uninitialized value cannot be observed.
 +    #[clippy::version = "1.39.0"]
 +    pub MEM_REPLACE_WITH_UNINIT,
 +    correctness,
 +    "`mem::replace(&mut _, mem::uninitialized())` or `mem::replace(&mut _, mem::zeroed())`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `std::mem::replace` on a value of type
 +    /// `T` with `T::default()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `std::mem` module already has the method `take` to
 +    /// take the current value and replace it with the default value of that type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut text = String::from("foo");
 +    /// let replaced = std::mem::replace(&mut text, String::default());
 +    /// ```
 +    /// Is better expressed with:
 +    /// ```rust
 +    /// let mut text = String::from("foo");
 +    /// let taken = std::mem::take(&mut text);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub MEM_REPLACE_WITH_DEFAULT,
 +    style,
 +    "replacing a value of type `T` with `T::default()` instead of using `std::mem::take`"
 +}
 +
 +impl_lint_pass!(MemReplace =>
 +    [MEM_REPLACE_OPTION_WITH_NONE, MEM_REPLACE_WITH_UNINIT, MEM_REPLACE_WITH_DEFAULT]);
 +
 +fn check_replace_option_with_none(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    if let ExprKind::Path(ref replacement_qpath) = src.kind {
 +        // Check that second argument is `Option::None`
 +        if is_lang_ctor(cx, replacement_qpath, OptionNone) {
 +            // Since this is a late pass (already type-checked),
 +            // and we already know that the second argument is an
 +            // `Option`, we do not need to check the first
 +            // argument's type. All that's left is to get
 +            // replacee's path.
 +            let replaced_path = match dest.kind {
 +                ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, replaced) => {
 +                    if let ExprKind::Path(QPath::Resolved(None, replaced_path)) = replaced.kind {
 +                        replaced_path
 +                    } else {
 +                        return;
 +                    }
 +                },
 +                ExprKind::Path(QPath::Resolved(None, replaced_path)) => replaced_path,
 +                _ => return,
 +            };
 +
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MEM_REPLACE_OPTION_WITH_NONE,
 +                expr_span,
 +                "replacing an `Option` with `None`",
 +                "consider `Option::take()` instead",
 +                format!(
 +                    "{}.take()",
 +                    snippet_with_applicability(cx, replaced_path.span, "", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    if_chain! {
 +        // check if replacement is mem::MaybeUninit::uninit().assume_init()
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(src.hir_id);
 +        if cx.tcx.is_diagnostic_item(sym::assume_init, method_def_id);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MEM_REPLACE_WITH_UNINIT,
 +                expr_span,
 +                "replacing with `mem::MaybeUninit::uninit().assume_init()`",
 +                "consider using",
 +                format!(
 +                    "std::ptr::read({})",
 +                    snippet_with_applicability(cx, dest.span, "", &mut applicability)
 +                ),
 +                applicability,
 +            );
 +            return;
 +        }
 +    }
 +
 +    if_chain! {
 +        if let ExprKind::Call(repl_func, repl_args) = src.kind;
 +        if repl_args.is_empty();
 +        if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        then {
 +            if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
 +                let mut applicability = Applicability::MachineApplicable;
 +                span_lint_and_sugg(
 +                    cx,
 +                    MEM_REPLACE_WITH_UNINIT,
 +                    expr_span,
 +                    "replacing with `mem::uninitialized()`",
 +                    "consider using",
 +                    format!(
 +                        "std::ptr::read({})",
 +                        snippet_with_applicability(cx, dest.span, "", &mut applicability)
 +                    ),
 +                    applicability,
 +                );
 +            } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id) &&
 +                    !cx.typeck_results().expr_ty(src).is_primitive() {
 +                span_lint_and_help(
 +                    cx,
 +                    MEM_REPLACE_WITH_UNINIT,
 +                    expr_span,
 +                    "replacing with `mem::zeroed()`",
 +                    None,
 +                    "consider using a default value or the `take_mut` crate instead",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
 +    // disable lint for primitives
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(src);
 +    if is_non_aggregate_primitive_type(expr_type) {
 +        return;
 +    }
 +    // disable lint for Option since it is covered in another lint
 +    if let ExprKind::Path(q) = &src.kind {
 +        if is_lang_ctor(cx, q, OptionNone) {
 +            return;
 +        }
 +    }
 +    if is_default_equivalent(cx, src) && !in_external_macro(cx.tcx.sess, expr_span) {
 +        span_lint_and_then(
 +            cx,
 +            MEM_REPLACE_WITH_DEFAULT,
 +            expr_span,
 +            "replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`",
 +            |diag| {
 +                if !expr_span.from_expansion() {
 +                    let suggestion = format!("std::mem::take({})", snippet(cx, dest.span, ""));
 +
 +                    diag.span_suggestion(
 +                        expr_span,
 +                        "consider using",
 +                        suggestion,
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            },
 +        );
 +    }
 +}
 +
 +pub struct MemReplace {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl MemReplace {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for MemReplace {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            // Check that `expr` is a call to `mem::replace()`
 +            if let ExprKind::Call(func, func_args) = expr.kind;
 +            if let ExprKind::Path(ref func_qpath) = func.kind;
 +            if let Some(def_id) = cx.qpath_res(func_qpath, func.hir_id).opt_def_id();
++            if cx.tcx.is_diagnostic_item(sym::mem_replace, def_id);
 +            if let [dest, src] = func_args;
 +            then {
 +                check_replace_option_with_none(cx, src, dest, expr.span);
 +                check_replace_with_uninit(cx, src, dest, expr.span);
 +                if meets_msrv(self.msrv.as_ref(), &msrvs::MEM_TAKE) {
 +                    check_replace_with_default(cx, src, dest, expr.span);
 +                }
 +            }
 +        }
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
index ce89189bce9779e12305ca5c269ef9c4e359e30d,0000000000000000000000000000000000000000..77d21f1d3730c66afeb7ac64f42ea735dd1e611e
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,26 @@@
- use clippy_utils::{is_expr_path_def_path, paths, ty::is_uninit_value_valid_for_ty};
 +use clippy_utils::diagnostics::span_lint;
-         if is_expr_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT);
++use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
++use rustc_span::sym;
 +
 +use super::UNINIT_ASSUMED_INIT;
 +
 +/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
 +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
 +    if_chain! {
 +        if let hir::ExprKind::Call(callee, args) = recv.kind;
 +        if args.is_empty();
++        if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
 +        if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
 +        then {
 +            span_lint(
 +                cx,
 +                UNINIT_ASSUMED_INIT,
 +                expr.span,
 +                "this call for this type may be undefined behavior"
 +            );
 +        }
 +    }
 +}
index cf9770f5c1fd320af91b3d297424fd33bb20388b,0000000000000000000000000000000000000000..65d1f440b76391031109a7dbb7a1049cb9d869b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,125 -1,0 +1,122 @@@
- use clippy_utils::{match_def_path, match_trait_method, paths};
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::span_lint;
-                     .and_then(|def_id| {
-                         if match_def_path(cx, def_id, &paths::CMP_MIN) {
-                             fetch_const(cx, args, MinMax::Min)
-                         } else if match_def_path(cx, def_id, &paths::CMP_MAX) {
-                             fetch_const(cx, args, MinMax::Max)
-                         } else {
-                             None
-                         }
++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
 +    /// ```ignore
 +    /// min(0, max(100, x))
 +    /// ```
 +    /// or
 +    /// ```ignore
 +    /// 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 4cb79648ae36a054e13ce8aeda7f558fccb34d7a,0000000000000000000000000000000000000000..6c68c1bc48df88777a836650052e09f551eb0c00
mode 100644,000000..100644
--- /dev/null
@@@ -1,172 -1,0 +1,176 @@@
-     /// Checks for types with a `fn new() -> Self` method and no
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::return_ty;
 +use clippy_utils::source::snippet;
 +use clippy_utils::sugg::DiagnosticExt;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::HirIdSet;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// struct Foo(Bar);
++    /// Checks for public types with a `pub fn new() -> Self` method and no
 +    /// implementation of
 +    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html).
 +    ///
 +    /// ### Why is this bad?
 +    /// The user might expect to be able to use
 +    /// [`Default`](https://doc.rust-lang.org/std/default/trait.Default.html) as the
 +    /// type can be constructed without arguments.
 +    ///
 +    /// ### Example
 +    /// ```ignore
-     ///     fn new() -> Self {
++    /// pub struct Foo(Bar);
 +    ///
 +    /// impl Foo {
-     /// struct Foo(Bar);
++    ///     pub fn new() -> Self {
 +    ///         Foo(Bar::new())
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// To fix the lint, add a `Default` implementation that delegates to `new`:
 +    ///
 +    /// ```ignore
-     "`fn new() -> Self` method without `Default` implementation"
++    /// pub struct Foo(Bar);
 +    ///
 +    /// impl Default for Foo {
 +    ///     fn default() -> Self {
 +    ///         Foo::new()
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_WITHOUT_DEFAULT,
 +    style,
++    "`pub fn new() -> Self` method without `Default` implementation"
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct NewWithoutDefault {
 +    impling_types: Option<HirIdSet>,
 +}
 +
 +impl_lint_pass!(NewWithoutDefault => [NEW_WITHOUT_DEFAULT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl(hir::Impl {
 +            of_trait: None,
 +            ref generics,
 +            self_ty: impl_self_ty,
 +            items,
 +            ..
 +        }) = item.kind
 +        {
 +            for assoc_item in items {
 +                if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
 +                    let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
 +                    if in_external_macro(cx.sess(), impl_item.span) {
 +                        return;
 +                    }
 +                    if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
 +                        let name = impl_item.ident.name;
 +                        let id = impl_item.hir_id();
 +                        if sig.header.constness == hir::Constness::Const {
 +                            // can't be implemented by default
 +                            return;
 +                        }
 +                        if sig.header.unsafety == hir::Unsafety::Unsafe {
 +                            // can't be implemented for unsafe new
 +                            return;
 +                        }
++                        if clippy_utils::is_doc_hidden(cx.tcx.hir().attrs(id)) {
++                            // shouldn't be implemented when it is hidden in docs
++                            return;
++                        }
 +                        if impl_item
 +                            .generics
 +                            .params
 +                            .iter()
 +                            .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
 +                        {
 +                            // when the result of `new()` depends on a type parameter we should not require
 +                            // an
 +                            // impl of `Default`
 +                            return;
 +                        }
 +                        if_chain! {
 +                            if sig.decl.inputs.is_empty();
 +                            if name == sym::new;
 +                            if cx.access_levels.is_reachable(impl_item.def_id);
 +                            let self_def_id = cx.tcx.hir().get_parent_item(id);
 +                            let self_ty = cx.tcx.type_of(self_def_id);
 +                            if self_ty == return_ty(cx, id);
 +                            if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
 +                            then {
 +                                if self.impling_types.is_none() {
 +                                    let mut impls = HirIdSet::default();
 +                                    cx.tcx.for_each_impl(default_trait_id, |d| {
 +                                        if let Some(ty_def) = cx.tcx.type_of(d).ty_adt_def() {
 +                                            if let Some(local_def_id) = ty_def.did.as_local() {
 +                                                impls.insert(cx.tcx.hir().local_def_id_to_hir_id(local_def_id));
 +                                            }
 +                                        }
 +                                    });
 +                                    self.impling_types = Some(impls);
 +                                }
 +
 +                                // Check if a Default implementation exists for the Self type, regardless of
 +                                // generics
 +                                if_chain! {
 +                                    if let Some(ref impling_types) = self.impling_types;
 +                                    if let Some(self_def) = cx.tcx.type_of(self_def_id).ty_adt_def();
 +                                    if let Some(self_local_did) = self_def.did.as_local();
 +                                    let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
 +                                    if impling_types.contains(&self_id);
 +                                    then {
 +                                        return;
 +                                    }
 +                                }
 +
 +                                let generics_sugg = snippet(cx, generics.span, "");
 +                                let self_ty_fmt = self_ty.to_string();
 +                                let self_type_snip = snippet(cx, impl_self_ty.span, &self_ty_fmt);
 +                                span_lint_hir_and_then(
 +                                    cx,
 +                                    NEW_WITHOUT_DEFAULT,
 +                                    id,
 +                                    impl_item.span,
 +                                    &format!(
 +                                        "you should consider adding a `Default` implementation for `{}`",
 +                                        self_type_snip
 +                                    ),
 +                                    |diag| {
 +                                        diag.suggest_prepend_item(
 +                                            cx,
 +                                            item.span,
 +                                            "try adding this",
 +                                            &create_new_without_default_suggest_msg(&self_type_snip, &generics_sugg),
 +                                            Applicability::MaybeIncorrect,
 +                                        );
 +                                    },
 +                                );
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn create_new_without_default_suggest_msg(self_type_snip: &str, generics_sugg: &str) -> String {
 +    #[rustfmt::skip]
 +    format!(
 +"impl{} Default for {} {{
 +    fn default() -> Self {{
 +        Self::new()
 +    }}
 +}}", generics_sugg, self_type_snip)
 +}
index e0ce1b7db003afb96091e912a701d1e0ab2f7a84,0000000000000000000000000000000000000000..2c328195f24e1af1a6cc792f960d568e09b3b837
mode 100644,000000..100644
--- /dev/null
@@@ -1,680 -1,0 +1,680 @@@
-                 if result.skip {
 +//! Checks for usage of  `&Vec[_]` and `&String`.
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::expr_sig;
 +use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::HirIdMap;
 +use rustc_hir::intravisit::{walk_expr, Visitor};
 +use rustc_hir::{
 +    self as hir, AnonConst, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArg,
 +    ImplItemKind, ItemKind, Lifetime, LifetimeName, Mutability, Node, Param, ParamName, PatKind, QPath, TraitFn,
 +    TraitItem, TraitItemKind, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::{self, AssocItems, AssocKind, Ty};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, MultiSpan};
 +use std::fmt;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for function arguments of type `&String`, `&Vec`,
 +    /// `&PathBuf`, and `Cow<_>`. It will also suggest you replace `.clone()` calls
 +    /// with the appropriate `.to_owned()`/`to_string()` calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Requiring the argument to be of the specific size
 +    /// makes the function less useful for no benefit; slices in the form of `&[T]`
 +    /// or `&str` usually suffice and can be obtained from other types, too.
 +    ///
 +    /// ### Known problems
 +    /// There may be `fn(&Vec)`-typed references pointing to your function.
 +    /// If you have them, you will get a compiler error after applying this lint's
 +    /// suggestions. You then have the choice to undo your changes or change the
 +    /// type of the reference.
 +    ///
 +    /// Note that if the function is part of your public interface, there may be
 +    /// other crates referencing it, of which you may not be aware. Carefully
 +    /// deprecate the function before applying the lint suggestions in this case.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad
 +    /// fn foo(&Vec<u32>) { .. }
 +    ///
 +    /// // Good
 +    /// fn foo(&[u32]) { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PTR_ARG,
 +    style,
 +    "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for equality comparisons with `ptr::null`
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easier and more readable to use the inherent
 +    /// `.is_null()`
 +    /// method instead
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad
 +    /// if x == ptr::null {
 +    ///     ..
 +    /// }
 +    ///
 +    /// // Good
 +    /// if x.is_null() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CMP_NULL,
 +    style,
 +    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for functions that take immutable
 +    /// references and return mutable ones.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is trivially unsound, as one can create two
 +    /// mutable references from the same (immutable!) source.
 +    /// This [error](https://github.com/rust-lang/rust/issues/39465)
 +    /// actually lead to an interim Rust release 1.15.1.
 +    ///
 +    /// ### Known problems
 +    /// To be on the conservative side, if there's at least one
 +    /// mutable reference with the output lifetime, this lint will not trigger.
 +    /// In practice, this case is unlikely anyway.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn foo(&Foo) -> &mut Bar { .. }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub MUT_FROM_REF,
 +    correctness,
 +    "fns that create mutable refs from immutable ref args"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for invalid usages of `ptr::null`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This causes undefined behavior.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// // Bad. Undefined behavior
 +    /// unsafe { std::slice::from_raw_parts(ptr::null(), 0); }
 +    /// ```
 +    ///
 +    /// ```ignore
 +    /// // Good
 +    /// unsafe { std::slice::from_raw_parts(NonNull::dangling().as_ptr(), 0); }
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub INVALID_NULL_PTR_USAGE,
 +    correctness,
 +    "invalid usage of a null pointer, suggesting `NonNull::dangling()` instead"
 +}
 +
 +declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF, INVALID_NULL_PTR_USAGE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ptr {
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(sig, trait_method) = &item.kind {
 +            if matches!(trait_method, TraitFn::Provided(_)) {
 +                // Handled by check body.
 +                return;
 +            }
 +
 +            check_mut_from_ref(cx, sig.decl);
 +            for arg in check_fn_args(
 +                cx,
 +                cx.tcx.fn_sig(item.def_id).skip_binder().inputs(),
 +                sig.decl.inputs,
 +                &[],
 +            )
 +            .filter(|arg| arg.mutability() == Mutability::Not)
 +            {
 +                span_lint_and_sugg(
 +                    cx,
 +                    PTR_ARG,
 +                    arg.span,
 +                    &arg.build_msg(),
 +                    "change this to",
 +                    format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
 +                    Applicability::Unspecified,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        let mut parents = hir.parent_iter(body.value.hir_id);
 +        let (item_id, decl, is_trait_item) = match parents.next() {
 +            Some((_, Node::Item(i))) => {
 +                if let ItemKind::Fn(sig, ..) = &i.kind {
 +                    (i.def_id, sig.decl, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::ImplItem(i))) => {
 +                if !matches!(parents.next(),
 +                    Some((_, Node::Item(i))) if matches!(&i.kind, ItemKind::Impl(i) if i.of_trait.is_none())
 +                ) {
 +                    return;
 +                }
 +                if let ImplItemKind::Fn(sig, _) = &i.kind {
 +                    (i.def_id, sig.decl, false)
 +                } else {
 +                    return;
 +                }
 +            },
 +            Some((_, Node::TraitItem(i))) => {
 +                if let TraitItemKind::Fn(sig, _) = &i.kind {
 +                    (i.def_id, sig.decl, true)
 +                } else {
 +                    return;
 +                }
 +            },
 +            _ => return,
 +        };
 +
 +        check_mut_from_ref(cx, decl);
 +        let sig = cx.tcx.fn_sig(item_id).skip_binder();
 +        let lint_args: Vec<_> = check_fn_args(cx, sig.inputs(), decl.inputs, body.params)
 +            .filter(|arg| !is_trait_item || arg.mutability() == Mutability::Not)
 +            .collect();
 +        let results = check_ptr_arg_usage(cx, body, &lint_args);
 +
 +        for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
 +            span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
 +                diag.multipart_suggestion(
 +                    "change this to",
 +                    iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
 +                        .chain(result.replacements.iter().map(|r| {
 +                            (
 +                                r.expr_span,
 +                                format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
 +                            )
 +                        }))
 +                        .collect(),
 +                    Applicability::Unspecified,
 +                );
 +            });
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let ExprKind::Binary(ref op, l, r) = expr.kind {
 +            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(cx, l) || is_null_path(cx, r)) {
 +                span_lint(
 +                    cx,
 +                    CMP_NULL,
 +                    expr.span,
 +                    "comparing with null is better expressed by the `.is_null()` method",
 +                );
 +            }
 +        } else {
 +            check_invalid_ptr_usage(cx, expr);
 +        }
 +    }
 +}
 +
 +fn check_invalid_ptr_usage<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +    // (fn_path, arg_indices) - `arg_indices` are the `arg` positions where null would cause U.B.
 +    const INVALID_NULL_PTR_USAGE_TABLE: [(&[&str], &[usize]); 16] = [
 +        (&paths::SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_COPY, &[0, 1]),
 +        (&paths::PTR_COPY_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_READ, &[0]),
 +        (&paths::PTR_READ_UNALIGNED, &[0]),
 +        (&paths::PTR_READ_VOLATILE, &[0]),
 +        (&paths::PTR_REPLACE, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS, &[0]),
 +        (&paths::PTR_SLICE_FROM_RAW_PARTS_MUT, &[0]),
 +        (&paths::PTR_SWAP, &[0, 1]),
 +        (&paths::PTR_SWAP_NONOVERLAPPING, &[0, 1]),
 +        (&paths::PTR_WRITE, &[0]),
 +        (&paths::PTR_WRITE_UNALIGNED, &[0]),
 +        (&paths::PTR_WRITE_VOLATILE, &[0]),
 +        (&paths::PTR_WRITE_BYTES, &[0]),
 +    ];
 +
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        let fun_def_path = cx.get_def_path(fun_def_id).into_iter().map(Symbol::to_ident_string).collect::<Vec<_>>();
 +        if let Some(&(_, arg_indices)) = INVALID_NULL_PTR_USAGE_TABLE
 +            .iter()
 +            .find(|&&(fn_path, _)| fn_path == fun_def_path);
 +        then {
 +            for &arg_idx in arg_indices {
 +                if let Some(arg) = args.get(arg_idx).filter(|arg| is_null_path(cx, arg)) {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        INVALID_NULL_PTR_USAGE,
 +                        arg.span,
 +                        "pointer must be non-null",
 +                        "change this to",
 +                        "core::ptr::NonNull::dangling().as_ptr()".to_string(),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +struct PtrArgResult {
 +    skip: bool,
 +    replacements: Vec<PtrArgReplacement>,
 +}
 +
 +struct PtrArgReplacement {
 +    expr_span: Span,
 +    self_span: Span,
 +    replacement: &'static str,
 +}
 +
 +struct PtrArg<'tcx> {
 +    idx: usize,
 +    span: Span,
 +    ty_did: DefId,
 +    ty_name: Symbol,
 +    method_renames: &'static [(&'static str, &'static str)],
 +    ref_prefix: RefPrefix,
 +    deref_ty: DerefTy<'tcx>,
 +    deref_assoc_items: Option<(DefId, &'tcx AssocItems<'tcx>)>,
 +}
 +impl PtrArg<'_> {
 +    fn build_msg(&self) -> String {
 +        format!(
 +            "writing `&{}{}` instead of `&{}{}` involves a new object where a slice will do",
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.ty_name,
 +            self.ref_prefix.mutability.prefix_str(),
 +            self.deref_ty.argless_str(),
 +        )
 +    }
 +
 +    fn mutability(&self) -> Mutability {
 +        self.ref_prefix.mutability
 +    }
 +}
 +
 +struct RefPrefix {
 +    lt: LifetimeName,
 +    mutability: Mutability,
 +}
 +impl fmt::Display for RefPrefix {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use fmt::Write;
 +        f.write_char('&')?;
 +        match self.lt {
 +            LifetimeName::Param(ParamName::Plain(name)) => {
 +                name.fmt(f)?;
 +                f.write_char(' ')?;
 +            },
 +            LifetimeName::Underscore => f.write_str("'_ ")?,
 +            LifetimeName::Static => f.write_str("'static ")?,
 +            _ => (),
 +        }
 +        f.write_str(self.mutability.prefix_str())
 +    }
 +}
 +
 +struct DerefTyDisplay<'a, 'tcx>(&'a LateContext<'tcx>, &'a DerefTy<'tcx>);
 +impl fmt::Display for DerefTyDisplay<'_, '_> {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        use std::fmt::Write;
 +        match self.1 {
 +            DerefTy::Str => f.write_str("str"),
 +            DerefTy::Path => f.write_str("Path"),
 +            DerefTy::Slice(hir_ty, ty) => {
 +                f.write_char('[')?;
 +                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
 +                    Some(s) => f.write_str(&s)?,
 +                    None => ty.fmt(f)?,
 +                }
 +                f.write_char(']')
 +            },
 +        }
 +    }
 +}
 +
 +enum DerefTy<'tcx> {
 +    Str,
 +    Path,
 +    Slice(Option<Span>, Ty<'tcx>),
 +}
 +impl<'tcx> DerefTy<'tcx> {
 +    fn argless_str(&self) -> &'static str {
 +        match *self {
 +            Self::Str => "str",
 +            Self::Path => "Path",
 +            Self::Slice(..) => "[_]",
 +        }
 +    }
 +
 +    fn display<'a>(&'a self, cx: &'a LateContext<'tcx>) -> DerefTyDisplay<'a, 'tcx> {
 +        DerefTyDisplay(cx, self)
 +    }
 +}
 +
 +fn check_fn_args<'cx, 'tcx: 'cx>(
 +    cx: &'cx LateContext<'tcx>,
 +    tys: &'tcx [Ty<'_>],
 +    hir_tys: &'tcx [hir::Ty<'_>],
 +    params: &'tcx [Param<'_>],
 +) -> impl Iterator<Item = PtrArg<'tcx>> + 'cx {
 +    tys.iter()
 +        .zip(hir_tys.iter())
 +        .enumerate()
 +        .filter_map(|(i, (ty, hir_ty))| {
 +            if_chain! {
 +                if let ty::Ref(_, ty, mutability) = *ty.kind();
 +                if let ty::Adt(adt, substs) = *ty.kind();
 +
 +                if let TyKind::Rptr(lt, ref ty) = hir_ty.kind;
 +                if let TyKind::Path(QPath::Resolved(None, path)) = ty.ty.kind;
 +
 +                // Check that the name as typed matches the actual name of the type.
 +                // e.g. `fn foo(_: &Foo)` shouldn't trigger the lint when `Foo` is an alias for `Vec`
 +                if let [.., name] = path.segments;
 +                if cx.tcx.item_name(adt.did) == name.ident.name;
 +
 +                if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
 +                if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
 +
 +                then {
 +                    let (method_renames, deref_ty, deref_impl_id) = match cx.tcx.get_diagnostic_name(adt.did) {
 +                        Some(sym::Vec) => (
 +                            [("clone", ".to_owned()")].as_slice(),
 +                            DerefTy::Slice(
 +                                name.args
 +                                    .and_then(|args| args.args.first())
 +                                    .and_then(|arg| if let GenericArg::Type(ty) = arg {
 +                                        Some(ty.span)
 +                                    } else {
 +                                        None
 +                                    }),
 +                                substs.type_at(0),
 +                            ),
 +                            cx.tcx.lang_items().slice_impl()
 +                        ),
 +                        Some(sym::String) => (
 +                            [("clone", ".to_owned()"), ("as_str", "")].as_slice(),
 +                            DerefTy::Str,
 +                            cx.tcx.lang_items().str_impl()
 +                        ),
 +                        Some(sym::PathBuf) => (
 +                            [("clone", ".to_path_buf()"), ("as_path", "")].as_slice(),
 +                            DerefTy::Path,
 +                            None,
 +                        ),
 +                        Some(sym::Cow) => {
 +                            let ty_name = name.args
 +                                .and_then(|args| {
 +                                    args.args.iter().find_map(|a| match a {
 +                                        GenericArg::Type(x) => Some(x),
 +                                        _ => None,
 +                                    })
 +                                })
 +                                .and_then(|arg| snippet_opt(cx, arg.span))
 +                                .unwrap_or_else(|| substs.type_at(1).to_string());
 +                            span_lint_and_sugg(
 +                                cx,
 +                                PTR_ARG,
 +                                hir_ty.span,
 +                                "using a reference to `Cow` is not recommended",
 +                                "change this to",
 +                                format!("&{}{}", mutability.prefix_str(), ty_name),
 +                                Applicability::Unspecified,
 +                            );
 +                            return None;
 +                        },
 +                        _ => return None,
 +                    };
 +                    return Some(PtrArg {
 +                        idx: i,
 +                        span: hir_ty.span,
 +                        ty_did: adt.did,
 +                        ty_name: name.ident.name,
 +                        method_renames,
 +                        ref_prefix: RefPrefix {
 +                            lt: lt.name,
 +                            mutability,
 +                        },
 +                        deref_ty,
 +                        deref_assoc_items: deref_impl_id.map(|id| (id, cx.tcx.associated_items(id))),
 +                    });
 +                }
 +            }
 +            None
 +        })
 +}
 +
 +fn check_mut_from_ref(cx: &LateContext<'_>, decl: &FnDecl<'_>) {
 +    if let FnRetTy::Return(ty) = decl.output {
 +        if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
 +            let mut immutables = vec![];
 +            for (_, mutbl, argspan) in decl
 +                .inputs
 +                .iter()
 +                .filter_map(get_rptr_lm)
 +                .filter(|&(lt, _, _)| lt.name == out.name)
 +            {
 +                if mutbl == Mutability::Mut {
 +                    return;
 +                }
 +                immutables.push(argspan);
 +            }
 +            if immutables.is_empty() {
 +                return;
 +            }
 +            span_lint_and_then(
 +                cx,
 +                MUT_FROM_REF,
 +                ty.span,
 +                "mutable borrow from immutable input(s)",
 +                |diag| {
 +                    let ms = MultiSpan::from_spans(immutables);
 +                    diag.span_note(ms, "immutable borrow here");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: &[PtrArg<'tcx>]) -> Vec<PtrArgResult> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        /// Map from a local id to which argument it came from (index into `Self::args` and
 +        /// `Self::results`)
 +        bindings: HirIdMap<usize>,
 +        /// The arguments being checked.
 +        args: &'cx [PtrArg<'tcx>],
 +        /// The results for each argument (len should match args.len)
 +        results: Vec<PtrArgResult>,
 +        /// The number of arguments which can't be linted. Used to return early.
 +        skip_count: usize,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        type NestedFilter = nested_filter::OnlyBodies;
 +        fn nested_visit_map(&mut self) -> Self::Map {
 +            self.cx.tcx.hir()
 +        }
 +
 +        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if self.skip_count == self.args.len() {
 +                return;
 +            }
 +
 +            // Check if this is local we care about
 +            let args_idx = match path_to_local(e).and_then(|id| self.bindings.get(&id)) {
 +                Some(&i) => i,
 +                None => return walk_expr(self, e),
 +            };
 +            let args = &self.args[args_idx];
 +            let result = &mut self.results[args_idx];
 +
 +            // Helper function to handle early returns.
 +            let mut set_skip_flag = || {
++                if !result.skip {
 +                    self.skip_count += 1;
 +                }
 +                result.skip = true;
 +            };
 +
 +            match get_expr_use_or_unification_node(self.cx.tcx, e) {
 +                Some((Node::Stmt(_), _)) => (),
 +                Some((Node::Local(l), _)) => {
 +                    // Only trace simple bindings. e.g `let x = y;`
 +                    if let PatKind::Binding(BindingAnnotation::Unannotated, id, _, None) = l.pat.kind {
 +                        self.bindings.insert(id, args_idx);
 +                    } else {
 +                        set_skip_flag();
 +                    }
 +                },
 +                Some((Node::Expr(e), child_id)) => match e.kind {
 +                    ExprKind::Call(f, expr_args) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if expr_sig(self.cx, f)
 +                            .map(|sig| sig.input(i).skip_binder().peel_refs())
 +                            .map_or(true, |ty| match *ty.kind() {
 +                                ty::Param(_) => true,
 +                                ty::Adt(def, _) => def.did == args.ty_did,
 +                                _ => false,
 +                            })
 +                        {
 +                            // Passed to a function taking the non-dereferenced type.
 +                            set_skip_flag();
 +                        }
 +                    },
 +                    ExprKind::MethodCall(name, expr_args @ [self_arg, ..], _) => {
 +                        let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
 +                        if i == 0 {
 +                            // Check if the method can be renamed.
 +                            let name = name.ident.as_str();
 +                            if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) {
 +                                result.replacements.push(PtrArgReplacement {
 +                                    expr_span: e.span,
 +                                    self_span: self_arg.span,
 +                                    replacement,
 +                                });
 +                                return;
 +                            }
 +                        }
 +
 +                        let id = if let Some(x) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) {
 +                            x
 +                        } else {
 +                            set_skip_flag();
 +                            return;
 +                        };
 +
 +                        match *self.cx.tcx.fn_sig(id).skip_binder().inputs()[i].peel_refs().kind() {
 +                            ty::Param(_) => {
 +                                set_skip_flag();
 +                            },
 +                            // If the types match check for methods which exist on both types. e.g. `Vec::len` and
 +                            // `slice::len`
 +                            ty::Adt(def, _)
 +                                if def.did == args.ty_did
 +                                    && (i != 0
 +                                        || self.cx.tcx.trait_of_item(id).is_some()
 +                                        || !args.deref_assoc_items.map_or(false, |(id, items)| {
 +                                            items
 +                                                .find_by_name_and_kind(self.cx.tcx, name.ident, AssocKind::Fn, id)
 +                                                .is_some()
 +                                        })) =>
 +                            {
 +                                set_skip_flag();
 +                            },
 +                            _ => (),
 +                        }
 +                    },
 +                    // Indexing is fine for currently supported types.
 +                    ExprKind::Index(e, _) if e.hir_id == child_id => (),
 +                    _ => set_skip_flag(),
 +                },
 +                _ => set_skip_flag(),
 +            }
 +        }
 +    }
 +
 +    let mut skip_count = 0;
 +    let mut results = args.iter().map(|_| PtrArgResult::default()).collect::<Vec<_>>();
 +    let mut v = V {
 +        cx,
 +        bindings: args
 +            .iter()
 +            .enumerate()
 +            .filter_map(|(i, arg)| {
 +                let param = &body.params[arg.idx];
 +                match param.pat.kind {
 +                    PatKind::Binding(BindingAnnotation::Unannotated, id, _, None)
 +                        if !is_lint_allowed(cx, PTR_ARG, param.hir_id) =>
 +                    {
 +                        Some((id, i))
 +                    },
 +                    _ => {
 +                        skip_count += 1;
 +                        results[i].skip = true;
 +                        None
 +                    },
 +                }
 +            })
 +            .collect(),
 +        args,
 +        results,
 +        skip_count,
 +    };
 +    v.visit_expr(&body.value);
 +    v.results
 +}
 +
 +fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
 +    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
 +        Some((lt, m.mutbl, ty.span))
 +    } else {
 +        None
 +    }
 +}
 +
 +fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(pathexp, []) = expr.kind {
 +        path_def_id(cx, pathexp).map_or(false, |id| {
 +            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
 +        })
 +    } else {
 +        false
 +    }
 +}
index 3e0e32857f1dad1e0c02b49426f0e485e397982b,0000000000000000000000000000000000000000..b3988973256c4c16cad3c8306c6e43cf4916b062
mode 100644,000000..100644
--- /dev/null
@@@ -1,768 -1,0 +1,768 @@@
-                 if match_def_path(cx, def.did, &paths::MEM_MANUALLY_DROP) {
 +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, transitive_relation::TransitiveRelation};
 +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::convert::TryFrom;
 +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 {
 +    #[allow(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];
 +        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_ref().map(|(dest, _)| dest)?.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<mir::Local>,
 +    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 borrowers = self.possible_borrower.reachable_from(&row);
 +            if !borrowers.is_empty() {
 +                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
 +                for &c in borrowers {
 +                    if c != mir::Local::from_usize(0) {
 +                        bs.insert(c);
 +                    }
 +                }
 +
 +                if !bs.is_empty() {
 +                    map.insert(row, bs);
 +                }
 +            }
 +        }
 +
 +        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: Some((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 exampel, `_1 = &mut _2` generate _1: {_2,...}
 +/// Known Problems: not sure all borrowed are tracked
 +struct PossibleOriginVisitor<'a, 'tcx> {
 +    possible_origin: TransitiveRelation<mir::Local>,
 +    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 borrowers = self.possible_origin.reachable_from(&row);
 +            if !borrowers.is_empty() {
 +                let mut bs = HybridBitSet::new_empty(self.body.local_decls.len());
 +                for &c in borrowers {
 +                    if c != mir::Local::from_usize(0) {
 +                        bs.insert(c);
 +                    }
 +                }
 +
 +                if !bs.is_empty() {
 +                    map.insert(row, bs);
 +                }
 +            }
 +        }
 +        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)
 +    }
 +}
index cd3aee5565538aa07cda95414baca765cb26f1b8,0000000000000000000000000000000000000000..25a9072ef6e0cf0a2ccd2eb395ec90d49c6cf3c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,169 @@@
- use clippy_utils::ty::is_type_lang_item;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::get_parent_expr;
 +use clippy_utils::source::snippet_with_context;
- use rustc_lint::{LateContext, LateLintPass};
++use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs};
 +use if_chain::if_chain;
++use rustc_ast::util::parser::PREC_PREFIX;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
- declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]);
++use rustc_lint::{LateContext, LateLintPass, Lint};
++use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
++use rustc_middle::ty::subst::GenericArg;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for redundant slicing expressions which use the full range, and
 +    /// do not change the type.
 +    ///
 +    /// ### Why is this bad?
 +    /// It unnecessarily adds complexity to the expression.
 +    ///
 +    /// ### Known problems
 +    /// If the type being sliced has an implementation of `Index<RangeFull>`
 +    /// that actually changes anything then it can't be removed. However, this would be surprising
 +    /// to people reading the code and should have a note with it.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn get_slice(x: &[u32]) -> &[u32] {
 +    ///     &x[..]
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// fn get_slice(x: &[u32]) -> &[u32] {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub REDUNDANT_SLICING,
 +    complexity,
 +    "redundant slicing of the whole range of a type"
 +}
 +
-             if cx.typeck_results().expr_ty(expr) == cx.typeck_results().expr_ty(indexed);
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for slicing expressions which are equivalent to dereferencing the
++    /// value.
++    ///
++    /// ### Why is this bad?
++    /// Some people may prefer to dereference rather than slice.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let vec = vec![1, 2, 3];
++    /// let slice = &vec[..];
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let vec = vec![1, 2, 3];
++    /// let slice = &*vec;
++    /// ```
++    #[clippy::version = "1.60.0"]
++    pub DEREF_BY_SLICING,
++    restriction,
++    "slicing instead of dereferencing"
++}
++
++declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]);
++
++static REDUNDANT_SLICING_LINT: (&Lint, &str) = (REDUNDANT_SLICING, "redundant slicing of the whole range");
++static DEREF_BY_SLICING_LINT: (&Lint, &str) = (DEREF_BY_SLICING, "slicing when dereferencing would work");
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        let ctxt = expr.span.ctxt();
 +        if_chain! {
 +            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
 +            if addressee.span.ctxt() == ctxt;
 +            if let ExprKind::Index(indexed, range) = addressee.kind;
 +            if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
-                 let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
 +            then {
++                let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr));
++                let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed));
++                let parent_expr = get_parent_expr(cx, expr);
++                let needs_parens_for_prefix = parent_expr.map_or(false, |parent| {
++                    parent.precedence().order() > PREC_PREFIX
++                });
 +                let mut app = Applicability::MachineApplicable;
-                 let (reborrow_str, help_str) = if mutability == Mutability::Mut {
-                     // The slice was used to reborrow the mutable reference.
-                     ("&mut *", "reborrow the original value instead")
-                 } else if matches!(
-                     get_parent_expr(cx, expr),
-                     Some(Expr {
-                         kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
-                         ..
-                     })
-                 ) {
-                     // The slice was used to make a temporary reference.
-                     ("&*", "reborrow the original value instead")
 +
-                     ("", "use the original value instead")
++                let ((lint, msg), help, sugg) = if expr_ty == indexed_ty {
++                    if expr_ref_count > indexed_ref_count {
++                        // Indexing takes self by reference and can't return a reference to that
++                        // reference as it's a local variable. The only way this could happen is if
++                        // `self` contains a reference to the `Self` type. If this occurs then the
++                        // lint no longer applies as it's essentially a field access, which is not
++                        // redundant.
++                        return;
++                    }
++                    let deref_count = indexed_ref_count - expr_ref_count;
++
++                    let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut {
++                        // The slice was used to reborrow the mutable reference.
++                        (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead")
++                    } else if matches!(
++                        parent_expr,
++                        Some(Expr {
++                            kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
++                            ..
++                        })
++                    ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
++                        matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })))
++                    }) {
++                        // The slice was used to make a temporary reference.
++                        (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead")
++                    } else if deref_count != 0 {
++                        (DEREF_BY_SLICING_LINT, "", "dereference the original value instead")
++                    } else {
++                        (REDUNDANT_SLICING_LINT, "", "use the original value instead")
++                    };
++
++                    let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
++                    let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix {
++                        format!("({}{}{})", reborrow_str, "*".repeat(deref_count), snip)
++                    } else {
++                        format!("{}{}{}", reborrow_str, "*".repeat(deref_count), snip)
++                    };
++
++                    (lint, help_str, sugg)
++                } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
++                    if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
++                        cx.param_env,
++                        cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
++                    ) {
++                        if deref_ty == expr_ty {
++                            let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
++                            let sugg = if needs_parens_for_prefix {
++                                format!("(&{}{}*{})", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip)
++                            } else {
++                                format!("&{}{}*{}", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip)
++                            };
++                            (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg)
++                        } else {
++                            return;
++                        }
++                    } else {
++                        return;
++                    }
 +                } else {
-                     REDUNDANT_SLICING,
++                    return;
 +                };
 +
 +                span_lint_and_sugg(
 +                    cx,
-                     "redundant slicing of the whole range",
-                     help_str,
-                     format!("{}{}", reborrow_str, snip),
++                    lint,
 +                    expr.span,
++                    msg,
++                    help,
++                    sugg,
 +                    app,
 +                );
 +            }
 +        }
 +    }
 +}
index f3515ea3c2dde19addc39a4519dbbcc8990b5833,0000000000000000000000000000000000000000..3d7dc49b406a6cec8f294dbfb9ef99332279c5ad
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,154 @@@
-                 if match_def_path(cx, def_id, &paths::MEM_SIZE_OF)
-                     || match_def_path(cx, def_id, &paths::MEM_SIZE_OF_VAL);
 +//! 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 bca95b7f25638449b03f0c13d68c1d2a41d250f6,0000000000000000000000000000000000000000..be9d538c36267cf8fd3243dc9283b5724f18f64c
mode 100644,000000..100644
--- /dev/null
@@@ -1,267 -1,0 +1,268 @@@
-                 if let Some(PathSegment { res: Some(Res::SelfTy{ trait_: Some(def_id), alias_to: _ }), .. }) = segments.first();
 +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, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind,
 +    WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +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 specifing them only once.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    ///
 +    /// ```rust
 +    /// fn func<T: Clone + Default>(arg: T) {}
 +    /// ```
 +    /// or
 +    ///
 +    /// ```rust
 +    /// 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 Generics { where_clause, .. } = &item.generics;
 +        let mut self_bounds_map = FxHashMap::default();
 +
 +        for predicate in where_clause.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +                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.where_clause.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref p) = bound;
 +                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;
 +                            hint_string.push_str(&format!(
 +                                " {} +",
 +                                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;
 +                            hint_string.push_str(&format!(
 +                                " {} +",
 +                                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.where_clause.predicates.is_empty() {
 +        return;
 +    }
 +
 +    let mut map = FxHashMap::default();
 +    for param in gen.params {
 +        if let ParamName::Plain(ref ident) = param.name {
 +            let res = param
 +                .bounds
 +                .iter()
 +                .filter_map(get_trait_info_from_bound)
 +                .collect::<Vec<_>>();
 +            map.insert(*ident, res);
 +        }
 +    }
 +
 +    for predicate in gen.where_clause.predicates {
 +        if_chain! {
 +            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +            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();
 +            if let Some(trait_resolutions_direct) = map.get(&segment.ident);
 +            then {
 +                for (res_where, _,  _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
 +                    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",
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +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 5e94ab6d04820afe307d36b4915ec11617e2aff6,0000000000000000000000000000000000000000..22a8c53a5852e805476cfc2603b446fabb5e496c
mode 100644,000000..100644
--- /dev/null
@@@ -1,442 -1,0 +1,443 @@@
-     /// Checks for transmutes either to or from a type which does not have a defined representation.
 +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 behaviour.
 +    ///
 +    /// ### 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 behaviour 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,
 +    nursery,
 +    "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
-     nursery,
++    /// 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.
 +    ///
 +    /// ### 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,
++    correctness,
 +    "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 from/to bits and dereferencing raw pointers in const contexts.
 +                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
 +                // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
 +                let const_context = in_constant(cx, e.hir_id);
 +
 +                let from_ty = cx.typeck_results().expr_ty(arg);
 +                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)
 +                    | 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 a57c819cb22567f78a2f3c5fed977b3026ee65b1,0000000000000000000000000000000000000000..05eadab3e6ccdcc500f881b62668bbc1a53a2a08
mode 100644,000000..100644
--- /dev/null
@@@ -1,292 -1,0 +1,334 @@@
-             ReducedTys::FromFatPtr { unsized_ty, .. } => {
-                 span_lint_and_then(
-                     cx,
-                     TRANSMUTE_UNDEFINED_REPR,
-                     e.span,
-                     &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                     |diag| {
-                         if from_ty_orig.peel_refs() != unsized_ty {
-                             diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                         }
-                     },
-                 );
-                 return true;
 +use super::TRANSMUTE_UNDEFINED_REPR;
 +use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::ty::is_c_void;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::subst::Subst;
 +use rustc_middle::ty::{self, Ty, TypeAndMut};
 +use rustc_span::Span;
 +
 +#[allow(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty_orig: Ty<'tcx>,
 +    to_ty_orig: Ty<'tcx>,
 +) -> bool {
 +    let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
 +    let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
 +
 +    while from_ty != to_ty {
 +        match reduce_refs(cx, e.span, from_ty, to_ty) {
-             ReducedTys::ToFatPtr { unsized_ty, .. } => {
-                 span_lint_and_then(
-                     cx,
-                     TRANSMUTE_UNDEFINED_REPR,
-                     e.span,
-                     &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                     |diag| {
-                         if to_ty_orig.peel_refs() != unsized_ty {
-                             diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                         }
-                     },
-                 );
-                 return true;
++            ReducedTys::FromFatPtr {
++                unsized_ty,
++                to_ty: to_sub_ty,
++            } => match reduce_ty(cx, to_sub_ty) {
++                ReducedTy::IntArray | ReducedTy::TypeErasure => break,
++                ReducedTy::Ref(to_sub_ty) => {
++                    from_ty = unsized_ty;
++                    to_ty = to_sub_ty;
++                    continue;
++                },
++                _ => {
++                    span_lint_and_then(
++                        cx,
++                        TRANSMUTE_UNDEFINED_REPR,
++                        e.span,
++                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
++                        |diag| {
++                            if from_ty_orig.peel_refs() != unsized_ty {
++                                diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
++                            }
++                        },
++                    );
++                    return true;
++                },
 +            },
-                 (ReducedTy::IntArray, _) | (_, ReducedTy::IntArray) => return false,
++            ReducedTys::ToFatPtr {
++                unsized_ty,
++                from_ty: from_sub_ty,
++            } => match reduce_ty(cx, from_sub_ty) {
++                ReducedTy::IntArray | ReducedTy::TypeErasure => break,
++                ReducedTy::Ref(from_sub_ty) => {
++                    from_ty = from_sub_ty;
++                    to_ty = unsized_ty;
++                    continue;
++                },
++                _ => {
++                    span_lint_and_then(
++                        cx,
++                        TRANSMUTE_UNDEFINED_REPR,
++                        e.span,
++                        &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
++                        |diag| {
++                            if to_ty_orig.peel_refs() != unsized_ty {
++                                diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
++                            }
++                        },
++                    );
++                    return true;
++                },
 +            },
 +            ReducedTys::ToPtr {
 +                from_ty: from_sub_ty,
 +                to_ty: to_sub_ty,
 +            } => match reduce_ty(cx, from_sub_ty) {
 +                ReducedTy::UnorderedFields(from_ty) => {
 +                    span_lint_and_then(
 +                        cx,
 +                        TRANSMUTE_UNDEFINED_REPR,
 +                        e.span,
 +                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
 +                        |diag| {
 +                            if from_ty_orig.peel_refs() != from_ty {
 +                                diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
 +                            }
 +                        },
 +                    );
 +                    return true;
 +                },
 +                ReducedTy::Ref(from_sub_ty) => {
 +                    from_ty = from_sub_ty;
 +                    to_ty = to_sub_ty;
 +                    continue;
 +                },
 +                _ => break,
 +            },
 +            ReducedTys::FromPtr {
 +                from_ty: from_sub_ty,
 +                to_ty: to_sub_ty,
 +            } => match reduce_ty(cx, to_sub_ty) {
 +                ReducedTy::UnorderedFields(to_ty) => {
 +                    span_lint_and_then(
 +                        cx,
 +                        TRANSMUTE_UNDEFINED_REPR,
 +                        e.span,
 +                        &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
 +                        |diag| {
 +                            if to_ty_orig.peel_refs() != to_ty {
 +                                diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
 +                            }
 +                        },
 +                    );
 +                    return true;
 +                },
 +                ReducedTy::Ref(to_sub_ty) => {
 +                    from_ty = from_sub_ty;
 +                    to_ty = to_sub_ty;
 +                    continue;
 +                },
 +                _ => break,
 +            },
 +            ReducedTys::Other {
 +                from_ty: from_sub_ty,
 +                to_ty: to_sub_ty,
 +            } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
-     FromFatPtr { unsized_ty: Ty<'tcx> },
-     ToFatPtr { unsized_ty: Ty<'tcx> },
++                (ReducedTy::IntArray | ReducedTy::TypeErasure, _)
++                | (_, ReducedTy::IntArray | ReducedTy::TypeErasure) => return false,
 +                (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
 +                    span_lint_and_then(
 +                        cx,
 +                        TRANSMUTE_UNDEFINED_REPR,
 +                        e.span,
 +                        &format!(
 +                            "transmute from `{}` to `{}`, both of which have an undefined layout",
 +                            from_ty_orig, to_ty_orig
 +                        ),
 +                        |diag| {
 +                            if_chain! {
 +                                if let (Some(from_def), Some(to_def)) = (from_ty.ty_adt_def(), to_ty.ty_adt_def());
 +                                if from_def == to_def;
 +                                then {
 +                                    diag.note(&format!(
 +                                        "two instances of the same generic type (`{}`) may have different layouts",
 +                                        cx.tcx.item_name(from_def.did)
 +                                    ));
 +                                } else {
 +                                    if from_ty_orig.peel_refs() != from_ty {
 +                                        diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
 +                                    }
 +                                    if to_ty_orig.peel_refs() != to_ty {
 +                                        diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
 +                                    }
 +                                }
 +                            }
 +                        },
 +                    );
 +                    return true;
 +                },
 +                (
 +                    ReducedTy::UnorderedFields(from_ty),
 +                    ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
 +                ) => {
 +                    span_lint_and_then(
 +                        cx,
 +                        TRANSMUTE_UNDEFINED_REPR,
 +                        e.span,
 +                        &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
 +                        |diag| {
 +                            if from_ty_orig.peel_refs() != from_ty {
 +                                diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
 +                            }
 +                        },
 +                    );
 +                    return true;
 +                },
 +                (
 +                    ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
 +                    ReducedTy::UnorderedFields(to_ty),
 +                ) => {
 +                    span_lint_and_then(
 +                        cx,
 +                        TRANSMUTE_UNDEFINED_REPR,
 +                        e.span,
 +                        &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
 +                        |diag| {
 +                            if to_ty_orig.peel_refs() != to_ty {
 +                                diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
 +                            }
 +                        },
 +                    );
 +                    return true;
 +                },
 +                (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
 +                    from_ty = from_sub_ty;
 +                    to_ty = to_sub_ty;
 +                    continue;
 +                },
 +                (
 +                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
 +                    ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_),
 +                )
 +                | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => break,
 +            },
 +        }
 +    }
 +
 +    false
 +}
 +
 +enum ReducedTys<'tcx> {
-                 &ty::Ref(_, from_sub_ty, _) | &ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. }),
-                 &ty::Ref(_, to_sub_ty, _) | &ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. }),
++    FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
++    ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
 +    ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
 +    FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
 +    Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
 +}
 +
++/// Remove references so long as both types are references.
 +fn reduce_refs<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    mut from_ty: Ty<'tcx>,
 +    mut to_ty: Ty<'tcx>,
 +) -> ReducedTys<'tcx> {
 +    loop {
 +        return match (from_ty.kind(), to_ty.kind()) {
 +            (
-             (&ty::Ref(_, unsized_ty, _) | &ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }), _)
++                &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
++                &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
 +            ) => {
 +                from_ty = from_sub_ty;
 +                to_ty = to_sub_ty;
 +                continue;
 +            },
-                 ReducedTys::FromFatPtr { unsized_ty }
++            (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
 +                if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
 +            {
-             (_, &ty::Ref(_, unsized_ty, _) | &ty::RawPtr(TypeAndMut { ty: unsized_ty, .. }))
++                ReducedTys::FromFatPtr { unsized_ty, to_ty }
 +            },
-                 ReducedTys::ToFatPtr { unsized_ty }
++            (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
 +                if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
 +            {
-             (&ty::Ref(_, from_ty, _) | &ty::RawPtr(TypeAndMut { ty: from_ty, .. }), _) => {
++                ReducedTys::ToFatPtr { unsized_ty, from_ty }
 +            },
-             (_, &ty::Ref(_, to_ty, _) | &ty::RawPtr(TypeAndMut { ty: to_ty, .. })) => {
++            (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
 +                ReducedTys::FromPtr { from_ty, to_ty }
 +            },
-     Other(Ty<'tcx>),
++            (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
 +                ReducedTys::ToPtr { from_ty, to_ty }
 +            },
 +            _ => ReducedTys::Other { from_ty, to_ty },
 +        };
 +    }
 +}
 +
 +enum ReducedTy<'tcx> {
++    /// The type can be used for type erasure.
++    TypeErasure,
++    /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
++    /// sized fields with a defined order.
 +    OrderedFields(Ty<'tcx>),
++    /// The type is a struct containing multiple non-zero sized fields with no defined order.
 +    UnorderedFields(Ty<'tcx>),
++    /// The type is a reference to the contained type.
 +    Ref(Ty<'tcx>),
-                 let Some(sized_ty) =  args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else {
++    /// The type is an array of a primitive integer type. These can be used as storage for a value
++    /// of another type.
 +    IntArray,
++    /// Any other type.
++    Other(Ty<'tcx>),
 +}
 +
++/// Reduce structs containing a single non-zero sized field to it's contained type.
 +fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
 +    loop {
 +        ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
 +        return match *ty.kind() {
 +            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::IntArray,
 +            ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
 +                ty = sub_ty;
 +                continue;
 +            },
++            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
 +            ty::Tuple(args) => {
-                 if def.repr.inhibit_struct_field_reordering_opt() {
-                     return ReducedTy::OrderedFields(ty);
-                 }
++                let Some(sized_ty) = args.iter().find(|&ty| !is_zero_sized_ty(cx, ty)) else {
 +                    return ReducedTy::OrderedFields(ty);
 +                };
 +                if args.iter().all(|ty| is_zero_sized_ty(cx, ty)) {
 +                    ty = sized_ty;
 +                    continue;
 +                }
 +                ReducedTy::UnorderedFields(ty)
 +            },
 +            ty::Adt(def, substs) if def.is_struct() => {
-                 let Some(sized_ty) = iter.find(|ty| !is_zero_sized_ty(cx, *ty)) else {
-                     return ReducedTy::OrderedFields(ty);
 +                let mut iter = def
 +                    .non_enum_variant()
 +                    .fields
 +                    .iter()
 +                    .map(|f| cx.tcx.type_of(f.did).subst(cx.tcx, substs));
-                 ReducedTy::UnorderedFields(ty)
++                let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
++                    return ReducedTy::TypeErasure;
 +                };
 +                if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
 +                    ty = sized_ty;
 +                    continue;
 +                }
-             ty::Ref(..) | ty::RawPtr(_) => ReducedTy::Ref(ty),
++                if def.repr.inhibit_struct_field_reordering_opt() {
++                    ReducedTy::OrderedFields(ty)
++                } else {
++                    ReducedTy::UnorderedFields(ty)
++                }
++            },
++            ty::Adt(def, _) if def.is_enum() && (def.variants.is_empty() || is_c_void(cx, ty)) => {
++                ReducedTy::TypeErasure
 +            },
++            ty::Foreign(_) => ReducedTy::TypeErasure,
++            ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
++            ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
 +            _ => ReducedTy::Other(ty),
 +        };
 +    }
 +}
 +
 +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if_chain! {
 +        if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty);
 +        if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
 +        then {
 +            layout.layout.size.bytes() == 0
 +        } else {
 +            false
 +        }
 +    }
 +}
index 63ad65b8afd9f6ffda92e75f326825448cb1df36,0000000000000000000000000000000000000000..7c06906293b167f373356698b2d9af026c33e581
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,113 @@@
- use clippy_utils::{match_def_path, paths};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet;
-         if match_def_path(cx, trait_did, &paths::ANY_TRAIT);
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{self as hir, GenericArg, GenericBounds, GenericParamKind};
 +use rustc_hir::{HirId, Lifetime, MutTy, Mutability, Node, QPath, TyKind};
 +use rustc_lint::LateContext;
++use rustc_span::sym;
 +
 +use super::BORROWED_BOX;
 +
 +pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool {
 +    match mut_ty.ty.kind {
 +        TyKind::Path(ref qpath) => {
 +            let hir_id = mut_ty.ty.hir_id;
 +            let def = cx.qpath_res(qpath, hir_id);
 +            if_chain! {
 +                if let Some(def_id) = def.opt_def_id();
 +                if Some(def_id) == cx.tcx.lang_items().owned_box();
 +                if let QPath::Resolved(None, path) = *qpath;
 +                if let [ref bx] = *path.segments;
 +                if let Some(params) = bx.args;
 +                if !params.parenthesized;
 +                if let Some(inner) = params.args.iter().find_map(|arg| match arg {
 +                    GenericArg::Type(ty) => Some(ty),
 +                    _ => None,
 +                });
 +                then {
 +                    if is_any_trait(cx, inner) {
 +                        // Ignore `Box<Any>` types; see issue #1884 for details.
 +                        return false;
 +                    }
 +
 +                    let ltopt = if lt.is_elided() {
 +                        String::new()
 +                    } else {
 +                        format!("{} ", lt.name.ident().as_str())
 +                    };
 +
 +                    if mut_ty.mutbl == Mutability::Mut {
 +                        // Ignore `&mut Box<T>` types; see issue #2907 for
 +                        // details.
 +                        return false;
 +                    }
 +
 +                    // When trait objects or opaque types have lifetime or auto-trait bounds,
 +                    // we need to add parentheses to avoid a syntax error due to its ambiguity.
 +                    // Originally reported as the issue #3128.
 +                    let inner_snippet = snippet(cx, inner.span, "..");
 +                    let suggestion = match &inner.kind {
 +                        TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => {
 +                            format!("&{}({})", ltopt, &inner_snippet)
 +                        },
 +                        TyKind::Path(qpath)
 +                            if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
 +                                .map_or(false, |bounds| bounds.len() > 1) =>
 +                        {
 +                            format!("&{}({})", ltopt, &inner_snippet)
 +                        },
 +                        _ => format!("&{}{}", ltopt, &inner_snippet),
 +                    };
 +                    span_lint_and_sugg(
 +                        cx,
 +                        BORROWED_BOX,
 +                        hir_ty.span,
 +                        "you seem to be trying to use `&Box<T>`. Consider using just `&T`",
 +                        "try",
 +                        suggestion,
 +                        // To make this `MachineApplicable`, at least one needs to check if it isn't a trait item
 +                        // because the trait impls of it will break otherwise;
 +                        // and there may be other cases that result in invalid code.
 +                        // For example, type coercion doesn't work nicely.
 +                        Applicability::Unspecified,
 +                    );
 +                    return true;
 +                }
 +            };
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +// Returns true if given type is `Any` trait.
 +fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
 +    if_chain! {
 +        if let TyKind::TraitObject(traits, ..) = t.kind;
 +        if !traits.is_empty();
 +        if let Some(trait_did) = traits[0].trait_ref.trait_def_id();
 +        // Only Send/Sync can be used as additional traits, so it is enough to
 +        // check only the first trait.
++        if cx.tcx.is_diagnostic_item(sym::Any, trait_did);
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn get_bounds_if_impl_trait<'tcx>(cx: &LateContext<'tcx>, qpath: &QPath<'_>, id: HirId) -> Option<GenericBounds<'tcx>> {
 +    if_chain! {
 +        if let Some(did) = cx.qpath_res(qpath, id).opt_def_id();
 +        if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
 +        if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
 +        if synthetic;
 +        then {
 +            Some(generic_param.bounds)
 +        } else {
 +            None
 +        }
 +    }
 +}
index 7557e14d11f526917b8faa0da05a3e941c829896,0000000000000000000000000000000000000000..db652766705c4989532ee74bcdc07c50045f9f0e
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,59 @@@
- use clippy_utils::{match_function_call, paths};
- use rustc_hir::{lang_items, Expr};
 +use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::path_res;
 +use clippy_utils::ty::is_type_lang_item;
-         if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) {
++use rustc_hir::{lang_items, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch cases if the user binds `std::mem::drop`
 +    /// to a different name and calls it that way.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct S;
 +    /// drop(std::mem::ManuallyDrop::new(S));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct S;
 +    /// unsafe {
 +    ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub UNDROPPED_MANUALLY_DROPS,
 +    correctness,
 +    "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
 +}
 +
 +declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-             if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) {
++        if_chain! {
++            if let ExprKind::Call(fun, [arg_0, ..]) = expr.kind;
++            if path_res(cx, fun).opt_def_id() == cx.tcx.get_diagnostic_item(sym::mem_drop);
 +            let ty = cx.typeck_results().expr_ty(arg_0);
++            if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop);
++            then {
 +                span_lint_and_help(
 +                    cx,
 +                    UNDROPPED_MANUALLY_DROPS,
 +                    expr.span,
 +                    "the inner value of this ManuallyDrop will not be dropped",
 +                    None,
 +                    "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
 +                );
 +            }
 +        }
 +    }
 +}
index dc0f515bfe5cb1d97e075396e242722eeab43502,0000000000000000000000000000000000000000..4433d5f5bf1463046f27e0feafc039fcc10dd8ea
mode 100644,000000..100644
--- /dev/null
@@@ -1,1316 -1,0 +1,1316 @@@
-     let span = sm.span_extend_to_prev_str(span, "let", false);
 +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;
 +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 intead 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_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!(
 +                &*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 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;
 +            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 collspible",
 +        "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();
 +    for item_def_id in lang_items.items().iter().flatten() {
 +        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(),
 +    )
 +}
index afff6491aba6420b39febc824702f9776656170f,0000000000000000000000000000000000000000..d3ed8da4499f88ee3aec2578e4983ffb1516dc51
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,18 @@@
- version = "0.1.60"
 +[package]
 +name = "clippy_utils"
++version = "0.1.61"
 +edition = "2021"
 +publish = false
 +
 +[dependencies]
 +arrayvec = { version = "0.7", default-features = false }
 +if_chain = "1.0"
 +rustc-semver = "1.1"
 +
 +[features]
 +deny-warnings = []
 +internal = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 4bb401273c4002b535178f015c28f4540d13cef6,0000000000000000000000000000000000000000..397783e309e85b135e5c1d4c25b25e86e1b34219
mode 100644,000000..100644
--- /dev/null
@@@ -1,2178 -1,0 +1,2161 @@@
- #[macro_export]
- macro_rules! unwrap_cargo_metadata {
-     ($cx: ident, $lint: ident, $deps: expr) => {{
-         let mut command = cargo_metadata::MetadataCommand::new();
-         if !$deps {
-             command.no_deps();
-         }
-         match command.exec() {
-             Ok(metadata) => metadata,
-             Err(err) => {
-                 span_lint($cx, $lint, DUMMY_SP, &format!("could not read cargo metadata: {}", err));
-                 return;
-             },
-         }
-     }};
- }
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_else)]
++#![feature(let_chains)]
 +#![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;
 +
 +#[allow(clippy::module_name_repetitions)]
 +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, Attribute, LitKind};
 +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::itemlikevisit::ItemLikeVisitor;
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
 +    ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
 +    MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Target,
 +    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::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
 +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) == Some(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 {
 +    macro_rules! try_res {
 +        ($e:expr) => {
 +            match $e {
 +                Some(e) => e,
 +                None => return Res::Err,
 +            }
 +        };
 +    }
 +    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: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        if let Some(&(index, Target::Impl)) = lang_items::ITEM_REFS.get(&Symbol::intern(name)) {
 +            tcx.lang_items().items()[index]
 +        } else {
 +            None
 +        }
 +    }
 +    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        tcx.crates(())
 +            .iter()
 +            .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 first = try_res!(
 +        find_primitive(tcx, base)
 +            .or_else(|| find_crate(tcx, base))
 +            .and_then(|id| item_child_by_name(tcx, id, first))
 +    );
 +
 +    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
 +            }
 +        });
 +    try_res!(last).expect_non_local()
 +}
 +
 +/// 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[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, _) => {
 +            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);
 +    }
 +}
 +
 +/// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 +/// implementations have.
 +pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
 +    has_attr(attrs, sym::automatically_derived)
 +}
 +
 +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()
 +}
 +
 +#[allow(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
 +}
 +
 +#[allow(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) {
 +        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
 +}
 +
 +// Finds the `#[must_use]` attribute, if any
 +pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
 +    attrs.iter().find(|a| a.has_name(sym::must_use))
 +}
 +
 +// 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| must_use_attr(cx.tcx.get_attrs(did)).is_some())
 +}
 +
 +/// 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,
 +                ..
 +            },
 +            ..,
 +        ) => cx.typeck_results().qpath_res(qpath, *path_hir_id).opt_def_id(),
 +        _ => 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
 +}
 +
 +struct TestItemNamesVisitor<'tcx> {
 +    tcx: TyCtxt<'tcx>,
 +    names: Vec<Symbol>,
 +}
 +
 +impl<'hir> ItemLikeVisitor<'hir> for TestItemNamesVisitor<'hir> {
 +    fn visit_item(&mut self, item: &Item<'_>) {
 +        if 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 = self
 +                        .tcx
 +                        .hir()
 +                        .attrs(item.hir_id())
 +                        .iter()
 +                        .any(|a| a.has_name(sym::rustc_test_marker));
 +                    if has_test_marker {
 +                        self.names.push(item.ident.name);
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    fn visit_trait_item(&mut self, _: &TraitItem<'_>) {}
 +    fn visit_impl_item(&mut self, _: &ImplItem<'_>) {}
 +    fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
 +}
 +
 +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();
 +    match map.entry(module) {
 +        Entry::Occupied(entry) => f(entry.get()),
 +        Entry::Vacant(entry) => {
 +            let mut visitor = TestItemNamesVisitor { tcx, names: Vec::new() };
 +            tcx.hir().visit_item_likes_in_module(module, &mut visitor);
 +            visitor.names.sort_unstable();
 +            f(&*entry.insert(visitor.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 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 5a76ac23332d31d5fd08c8826cd8f9c78ec876ff,0000000000000000000000000000000000000000..e7d4c5a49521d9ae21cec7b51e7512a5606630c0
mode 100644,000000..100644
--- /dev/null
@@@ -1,556 -1,0 +1,583 @@@
 +#![allow(clippy::similar_names)] // `expr` and `expn`
 +
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +use arrayvec::ArrayVec;
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::def_id::DefId;
 +use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 +use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
 +use std::ops::ControlFlow;
 +
++const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
++    sym::assert_eq_macro,
++    sym::assert_macro,
++    sym::assert_ne_macro,
++    sym::debug_assert_eq_macro,
++    sym::debug_assert_macro,
++    sym::debug_assert_ne_macro,
++    sym::eprint_macro,
++    sym::eprintln_macro,
++    sym::format_args_macro,
++    sym::format_macro,
++    sym::print_macro,
++    sym::println_macro,
++    sym::std_panic_macro,
++    sym::write_macro,
++    sym::writeln_macro,
++];
++
++/// Returns true if a given Macro `DefId` is a format macro (e.g. `println!`)
++pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
++    if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
++        FORMAT_MACRO_DIAG_ITEMS.contains(&name)
++    } else {
++        false
++    }
++}
++
 +/// A macro call, like `vec![1, 2, 3]`.
 +///
 +/// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
 +/// Even better is to check if it is a diagnostic item.
 +///
 +/// This structure is similar to `ExpnData` but it precludes desugaring expansions.
 +#[derive(Debug)]
 +pub struct MacroCall {
 +    /// Macro `DefId`
 +    pub def_id: DefId,
 +    /// Kind of macro
 +    pub kind: MacroKind,
 +    /// The expansion produced by the macro call
 +    pub expn: ExpnId,
 +    /// Span of the macro call site
 +    pub span: Span,
 +}
 +
 +impl MacroCall {
 +    pub fn is_local(&self) -> bool {
 +        span_is_local(self.span)
 +    }
 +}
 +
 +/// Returns an iterator of expansions that created the given span
 +pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
 +    std::iter::from_fn(move || {
 +        let ctxt = span.ctxt();
 +        if ctxt == SyntaxContext::root() {
 +            return None;
 +        }
 +        let expn = ctxt.outer_expn();
 +        let data = expn.expn_data();
 +        span = data.call_site;
 +        Some((expn, data))
 +    })
 +}
 +
 +/// Checks whether the span is from the root expansion or a locally defined macro
 +pub fn span_is_local(span: Span) -> bool {
 +    !span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
 +}
 +
 +/// Checks whether the expansion is the root expansion or a locally defined macro
 +pub fn expn_is_local(expn: ExpnId) -> bool {
 +    if expn == ExpnId::root() {
 +        return true;
 +    }
 +    let data = expn.expn_data();
 +    let backtrace = expn_backtrace(data.call_site);
 +    std::iter::once((expn, data))
 +        .chain(backtrace)
 +        .find_map(|(_, data)| data.macro_def_id)
 +        .map_or(true, DefId::is_local)
 +}
 +
 +/// Returns an iterator of macro expansions that created the given span.
 +/// Note that desugaring expansions are skipped.
 +pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
 +    expn_backtrace(span).filter_map(|(expn, data)| match data {
 +        ExpnData {
 +            kind: ExpnKind::Macro(kind, _),
 +            macro_def_id: Some(def_id),
 +            call_site: span,
 +            ..
 +        } => Some(MacroCall {
 +            def_id,
 +            kind,
 +            expn,
 +            span,
 +        }),
 +        _ => None,
 +    })
 +}
 +
 +/// If the macro backtrace of `span` has a macro call at the root expansion
 +/// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
 +pub fn root_macro_call(span: Span) -> Option<MacroCall> {
 +    macro_backtrace(span).last()
 +}
 +
 +/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
 +/// produced by the macro call, as in [`first_node_in_macro`].
 +pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
 +    if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
 +        return None;
 +    }
 +    root_macro_call(node.span())
 +}
 +
 +/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
 +/// macro call, as in [`first_node_in_macro`].
 +pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
 +    let span = node.span();
 +    first_node_in_macro(cx, node)
 +        .into_iter()
 +        .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
 +}
 +
 +/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
 +/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
 +/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
 +/// This is useful for finding macro calls while visiting the HIR without processing the macro call
 +/// at every node within its expansion.
 +///
 +/// If you already have immediate access to the parent node, it is simpler to
 +/// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
 +///
 +/// If a macro call is in statement position, it expands to one or more statements.
 +/// In that case, each statement *and* their immediate descendants will all yield `Some`
 +/// with the `ExpnId` of the containing block.
 +///
 +/// A node may be the "first node" of multiple macro calls in a macro backtrace.
 +/// The expansion of the outermost macro call site is returned in such cases.
 +pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
 +    // get the macro expansion or return `None` if not found
 +    // `macro_backtrace` importantly ignores desugaring expansions
 +    let expn = macro_backtrace(node.span()).next()?.expn;
 +
 +    // get the parent node, possibly skipping over a statement
 +    // if the parent is not found, it is sensible to return `Some(root)`
 +    let hir = cx.tcx.hir();
 +    let mut parent_iter = hir.parent_iter(node.hir_id());
 +    let (parent_id, _) = match parent_iter.next() {
 +        None => return Some(ExpnId::root()),
 +        Some((_, Node::Stmt(_))) => match parent_iter.next() {
 +            None => return Some(ExpnId::root()),
 +            Some(next) => next,
 +        },
 +        Some(next) => next,
 +    };
 +
 +    // get the macro expansion of the parent node
 +    let parent_span = hir.span(parent_id);
 +    let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
 +        // the parent node is not in a macro
 +        return Some(ExpnId::root());
 +    };
 +
 +    if parent_macro_call.expn.is_descendant_of(expn) {
 +        // `node` is input to a macro call
 +        return None;
 +    }
 +
 +    Some(parent_macro_call.expn)
 +}
 +
 +/* Specific Macro Utils */
 +
 +/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
 +pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
 +    matches!(
 +        name.as_str(),
 +        "core_panic_macro"
 +            | "std_panic_macro"
 +            | "core_panic_2015_macro"
 +            | "std_panic_2015_macro"
 +            | "core_panic_2021_macro"
 +    )
 +}
 +
 +pub enum PanicExpn<'a> {
 +    /// No arguments - `panic!()`
 +    Empty,
 +    /// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
 +    Str(&'a Expr<'a>),
 +    /// A single argument that implements `Display` - `panic!("{}", object)`
 +    Display(&'a Expr<'a>),
 +    /// Anything else - `panic!("error {}: {}", a, b)`
 +    Format(FormatArgsExpn<'a>),
 +}
 +
 +impl<'a> PanicExpn<'a> {
 +    pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
 +        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
 +            return None;
 +        }
 +        let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
 +        let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
 +        let result = match path.segments.last().unwrap().ident.as_str() {
 +            "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
 +            "panic" | "panic_str" => Self::Str(arg),
 +            "panic_display" => {
 +                let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
 +                Self::Display(e)
 +            },
 +            "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
 +            _ => return None,
 +        };
 +        Some(result)
 +    }
 +}
 +
 +/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
 +pub fn find_assert_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
 +}
 +
 +/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
 +/// expansion
 +pub fn find_assert_eq_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
 +}
 +
 +fn find_assert_args_inner<'a, const N: usize>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
 +    let macro_id = expn.expn_data().macro_def_id?;
 +    let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
 +        None => (expr, expn),
 +        Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
 +    };
 +    let mut args = ArrayVec::new();
 +    let mut panic_expn = None;
 +    expr_visitor_no_bodies(|e| {
 +        if args.is_full() {
 +            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
 +                panic_expn = PanicExpn::parse(cx, e);
 +            }
 +            panic_expn.is_none()
 +        } else if is_assert_arg(cx, e, expn) {
 +            args.push(e);
 +            false
 +        } else {
 +            true
 +        }
 +    })
 +    .visit_expr(expr);
 +    let args = args.into_inner().ok()?;
 +    // if no `panic!(..)` is found, use `PanicExpn::Empty`
 +    // to indicate that the default assertion message is used
 +    let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
 +    Some((args, panic_expn))
 +}
 +
 +fn find_assert_within_debug_assert<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +    assert_name: Symbol,
 +) -> Option<(&'a Expr<'a>, ExpnId)> {
 +    let mut found = None;
 +    expr_visitor_no_bodies(|e| {
 +        if found.is_some() || !e.span.from_expansion() {
 +            return false;
 +        }
 +        let e_expn = e.span.ctxt().outer_expn();
 +        if e_expn == expn {
 +            return true;
 +        }
 +        if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
 +            found = Some((e, e_expn));
 +        }
 +        false
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
 +    if !expr.span.from_expansion() {
 +        return true;
 +    }
 +    let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
 +        if macro_call.expn == assert_expn {
 +            ControlFlow::Break(false)
 +        } else {
 +            match cx.tcx.item_name(macro_call.def_id) {
 +                // `cfg!(debug_assertions)` in `debug_assert!`
 +                sym::cfg => ControlFlow::CONTINUE,
 +                // assert!(other_macro!(..))
 +                _ => ControlFlow::Break(true),
 +            }
 +        }
 +    });
 +    match result {
 +        ControlFlow::Break(is_assert_arg) => is_assert_arg,
 +        ControlFlow::Continue(()) => true,
 +    }
 +}
 +
 +/// A parsed `format_args!` expansion
 +#[derive(Debug)]
 +pub struct FormatArgsExpn<'tcx> {
 +    /// Span of the first argument, the format string
 +    pub format_string_span: Span,
 +    /// The format string split by formatted args like `{..}`
 +    pub format_string_parts: Vec<Symbol>,
 +    /// Values passed after the format string
 +    pub value_args: Vec<&'tcx Expr<'tcx>>,
 +    /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
 +    pub formatters: Vec<(usize, Symbol)>,
 +    /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
 +    /// then `formatters` represents the format args (`{..}`).
 +    /// If this is non-empty, it represents the format args, and the `position`
 +    /// parameters within the struct expressions are indexes of `formatters`.
 +    pub specs: Vec<&'tcx Expr<'tcx>>,
 +}
 +
 +impl<'tcx> FormatArgsExpn<'tcx> {
 +    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
 +    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        macro_backtrace(expr.span).find(|macro_call| {
 +            matches!(
 +                cx.tcx.item_name(macro_call.def_id),
 +                sym::const_format_args | sym::format_args | sym::format_args_nl
 +            )
 +        })?;
 +        let mut format_string_span: Option<Span> = None;
 +        let mut format_string_parts: Vec<Symbol> = Vec::new();
 +        let mut value_args: Vec<&Expr<'_>> = Vec::new();
 +        let mut formatters: Vec<(usize, Symbol)> = Vec::new();
 +        let mut specs: Vec<&Expr<'_>> = Vec::new();
 +        expr_visitor_no_bodies(|e| {
 +            // if we're still inside of the macro definition...
 +            if e.span.ctxt() == expr.span.ctxt() {
 +                // ArgumnetV1::new_<format_trait>(<value>)
 +                if_chain! {
 +                    if let ExprKind::Call(callee, [val]) = e.kind;
 +                    if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
 +                    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
 +                    if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
 +                    if seg.ident.name.as_str().starts_with("new_");
 +                    then {
 +                        let val_idx = if_chain! {
 +                            if val.span.ctxt() == expr.span.ctxt();
 +                            if let ExprKind::Field(_, field) = val.kind;
 +                            if let Ok(idx) = field.name.as_str().parse();
 +                            then {
 +                                // tuple index
 +                                idx
 +                            } else {
 +                                // assume the value expression is passed directly
 +                                formatters.len()
 +                            }
 +                        };
 +                        let fmt_trait = match seg.ident.name.as_str() {
 +                            "new_display" => "Display",
 +                            "new_debug" => "Debug",
 +                            "new_lower_exp" => "LowerExp",
 +                            "new_upper_exp" => "UpperExp",
 +                            "new_octal" => "Octal",
 +                            "new_pointer" => "Pointer",
 +                            "new_binary" => "Binary",
 +                            "new_lower_hex" => "LowerHex",
 +                            "new_upper_hex" => "UpperHex",
 +                            _ => unreachable!(),
 +                        };
 +                        formatters.push((val_idx, Symbol::intern(fmt_trait)));
 +                    }
 +                }
 +                if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
 +                    if path.segments.last().unwrap().ident.name == sym::Argument {
 +                        specs.push(e);
 +                    }
 +                }
 +                // walk through the macro expansion
 +                return true;
 +            }
 +            // assume that the first expr with a differing context represents
 +            // (and has the span of) the format string
 +            if format_string_span.is_none() {
 +                format_string_span = Some(e.span);
 +                let span = e.span;
 +                // walk the expr and collect string literals which are format string parts
 +                expr_visitor_no_bodies(|e| {
 +                    if e.span.ctxt() != span.ctxt() {
 +                        // defensive check, probably doesn't happen
 +                        return false;
 +                    }
 +                    if let ExprKind::Lit(lit) = &e.kind {
 +                        if let LitKind::Str(symbol, _s) = lit.node {
 +                            format_string_parts.push(symbol);
 +                        }
 +                    }
 +                    true
 +                })
 +                .visit_expr(e);
 +            } else {
 +                // assume that any further exprs with a differing context are value args
 +                value_args.push(e);
 +            }
 +            // don't walk anything not from the macro expansion (e.a. inputs)
 +            false
 +        })
 +        .visit_expr(expr);
 +        Some(FormatArgsExpn {
 +            format_string_span: format_string_span?,
 +            format_string_parts,
 +            value_args,
 +            formatters,
 +            specs,
 +        })
 +    }
 +
 +    /// Finds a nested call to `format_args!` within a `format!`-like macro call
 +    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
 +        let mut format_args = None;
 +        expr_visitor_no_bodies(|e| {
 +            if format_args.is_some() {
 +                return false;
 +            }
 +            let e_ctxt = e.span.ctxt();
 +            if e_ctxt == expr.span.ctxt() {
 +                return true;
 +            }
 +            if e_ctxt.outer_expn().is_descendant_of(expn_id) {
 +                format_args = FormatArgsExpn::parse(cx, e);
 +            }
 +            false
 +        })
 +        .visit_expr(expr);
 +        format_args
 +    }
 +
 +    /// Returns a vector of `FormatArgsArg`.
 +    pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
 +        if self.specs.is_empty() {
 +            let args = std::iter::zip(&self.value_args, &self.formatters)
 +                .map(|(value, &(_, format_trait))| FormatArgsArg {
 +                    value,
 +                    format_trait,
 +                    spec: None,
 +                })
 +                .collect();
 +            return Some(args);
 +        }
 +        self.specs
 +            .iter()
 +            .map(|spec| {
 +                if_chain! {
 +                    // struct `core::fmt::rt::v1::Argument`
 +                    if let ExprKind::Struct(_, fields, _) = spec.kind;
 +                    if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
 +                    if let ExprKind::Lit(lit) = &position_field.expr.kind;
 +                    if let LitKind::Int(position, _) = lit.node;
 +                    if let Ok(i) = usize::try_from(position);
 +                    if let Some(&(j, format_trait)) = self.formatters.get(i);
 +                    then {
 +                        Some(FormatArgsArg {
 +                            value: self.value_args[j],
 +                            format_trait,
 +                            spec: Some(spec),
 +                        })
 +                    } else {
 +                        None
 +                    }
 +                }
 +            })
 +            .collect()
 +    }
 +
 +    /// Source callsite span of all inputs
 +    pub fn inputs_span(&self) -> Span {
 +        match *self.value_args {
 +            [] => self.format_string_span,
 +            [.., last] => self
 +                .format_string_span
 +                .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
 +        }
 +    }
 +}
 +
 +/// Type representing a `FormatArgsExpn`'s format arguments
 +pub struct FormatArgsArg<'tcx> {
 +    /// An element of `value_args` according to `position`
 +    pub value: &'tcx Expr<'tcx>,
 +    /// An element of `args` according to `position`
 +    pub format_trait: Symbol,
 +    /// An element of `specs`
 +    pub spec: Option<&'tcx Expr<'tcx>>,
 +}
 +
 +impl<'tcx> FormatArgsArg<'tcx> {
 +    /// Returns true if any formatting parameters are used that would have an effect on strings,
 +    /// like `{:+2}` instead of just `{}`.
 +    pub fn has_string_formatting(&self) -> bool {
 +        self.spec.map_or(false, |spec| {
 +            // `!` because these conditions check that `self` is unformatted.
 +            !if_chain! {
 +                // struct `core::fmt::rt::v1::Argument`
 +                if let ExprKind::Struct(_, fields, _) = spec.kind;
 +                if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
 +                // struct `core::fmt::rt::v1::FormatSpec`
 +                if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
 +                if subfields.iter().all(|field| match field.ident.name {
 +                    sym::precision | sym::width => match field.expr.kind {
 +                        ExprKind::Path(QPath::Resolved(_, path)) => {
 +                            path.segments.last().unwrap().ident.name == sym::Implied
 +                        }
 +                        _ => false,
 +                    }
 +                    _ => true,
 +                });
 +                then { true } else { false }
 +            }
 +        })
 +    }
 +}
 +
 +/// A node with a `HirId` and a `Span`
 +pub trait HirNode {
 +    fn hir_id(&self) -> HirId;
 +    fn span(&self) -> Span;
 +}
 +
 +macro_rules! impl_hir_node {
 +    ($($t:ident),*) => {
 +        $(impl HirNode for hir::$t<'_> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn span(&self) -> Span {
 +                self.span
 +            }
 +        })*
 +    };
 +}
 +
 +impl_hir_node!(Expr, Pat);
 +
 +impl HirNode for hir::Item<'_> {
 +    fn hir_id(&self) -> HirId {
 +        self.hir_id()
 +    }
 +
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
index 288c56e9fd737bd386c6afe35fbaea7d1746018c,0000000000000000000000000000000000000000..2778e30388e263ae9aca921617cf023cb69ef2bf
mode 100644,000000..100644
--- /dev/null
@@@ -1,213 -1,0 +1,173 @@@
- pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
 +//! 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.
 +
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const ASSERT_EQ_MACRO: [&str; 3] = ["core", "macros", "assert_eq"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const ASSERT_MACRO: [&str; 4] = ["core", "macros", "builtin", "assert"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const ASSERT_NE_MACRO: [&str; 3] = ["core", "macros", "assert_ne"];
 +#[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"];
- /// Preferably use the diagnostic item `sym::Borrow` where possible
- pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
- pub const BORROW_MUT_TRAIT: [&str; 3] = ["core", "borrow", "BorrowMut"];
 +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
- pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
- pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
 +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 DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
- pub const DROP: [&str; 3] = ["core", "mem", "drop"];
- pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
 +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +pub const CSTRING_AS_C_STR: [&str; 5] = ["std", "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"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const EPRINT_MACRO: [&str; 3] = ["std", "macros", "eprint"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const EPRINTLN_MACRO: [&str; 3] = ["std", "macros", "eprintln"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const FORMAT_ARGS_MACRO: [&str; 4] = ["core", "macros", "builtin", "format_args"];
 +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_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
 +pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
- pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 +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"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
 +#[allow(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 MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
- pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
- pub const MEM_MANUALLY_DROP: [&str; 4] = ["core", "mem", "manually_drop", "ManuallyDrop"];
- pub const MEM_MAYBEUNINIT: [&str; 4] = ["core", "mem", "maybe_uninit", "MaybeUninit"];
- pub const MEM_MAYBEUNINIT_UNINIT: [&str; 5] = ["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"];
- pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
- pub const MEM_SIZE_OF: [&str; 3] = ["core", "mem", "size_of"];
- pub const MEM_SIZE_OF_VAL: [&str; 3] = ["core", "mem", "size_of_val"];
 +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_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 +#[allow(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 LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
- pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
 +pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
- pub const PARKING_LOT_MUTEX_GUARD: [&str; 2] = ["parking_lot", "MutexGuard"];
- pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 2] = ["parking_lot", "RwLockReadGuard"];
- pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 2] = ["parking_lot", "RwLockWriteGuard"];
 +/// 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"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const PRINT_MACRO: [&str; 3] = ["std", "macros", "print"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const PRINTLN_MACRO: [&str; 3] = ["std", "macros", "println"];
++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"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const WRITE_MACRO: [&str; 3] = ["core", "macros", "write"];
- #[allow(clippy::invalid_paths)] // `check_path` does not seem to work for macros
- pub const WRITELN_MACRO: [&str; 3] = ["core", "macros", "writeln"];
 +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_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"];
 +#[allow(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"];
 +#[allow(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"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 +#[allow(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"];
 +#[allow(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 +#[allow(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"];
 +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_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 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"];
 +#[allow(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"];
 +#[allow(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 0d39226d970350f8e9a8e8a133d317aa4082d2a9,0000000000000000000000000000000000000000..0646d1524a767310ee26d879649a16a441d8ee5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,517 -1,0 +1,588 @@@
-     self, AdtDef, Binder, FnSig, IntTy, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use rustc_ast::ast::Mutability;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, 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::{
++    self, AdtDef, Binder, FnSig, IntTy, 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, must_use_attr, path_res};
 +
 +// 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(ty: Ty<'_>, other_ty: Ty<'_>) -> 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(ty: Ty<'_>, adt: &AdtDef) -> 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)
 +        })
 +}
 +
 +/// 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 {
 +    // Clippy shouldn't have infer types
 +    assert!(!ty.needs_infer());
 +
 +    let ty = cx.tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
 +    let ty_params = cx.tcx.mk_substs(ty_params.iter());
 +    cx.tcx.infer_ctxt().enter(|infcx| {
 +        infcx
 +            .type_implements_trait(trait_id, ty, ty_params, cx.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, _) => must_use_attr(cx.tcx.get_attrs(adt.did)).is_some(),
 +        ty::Foreign(ref did) => must_use_attr(cx.tcx.get_attrs(*did)).is_some(),
 +        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(ref def_id, _) => {
 +            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
 +                if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
 +                    if must_use_attr(cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        ty::Dynamic(binder, _) => {
 +            for predicate in binder.iter() {
 +                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
 +                    if must_use_attr(cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
 +                        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,
 +    }
 +}
 +
 +/// 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.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.
++#[allow(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.variants[i];
++    match variant.discr {
++        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
++        VariantDiscr::Relative(x) => match adt.variants[(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 f065f0bffc7bf70fc934bec08ab33408bad2874c,0000000000000000000000000000000000000000..4d2c57619912f024722c427fb5ca9c4fa000be01
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-02-10"
 +[toolchain]
++channel = "nightly-2022-02-24"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index a82ff182839314d625cd0078cbe2d17fda1fe670,0000000000000000000000000000000000000000..6bc74bc1e9aada60c2755bc51415ee8edb986854
mode 100644,000000..100644
--- /dev/null
@@@ -1,350 -1,0 +1,355 @@@
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
 +#![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] = &[
 +    "clippy_utils",
 +    "derive_new",
 +    "futures",
 +    "if_chain",
 +    "itertools",
 +    "quote",
 +    "regex",
 +    "serde",
 +    "serde_derive",
 +    "syn",
 +    "tokio",
 +    "parking_lot",
 +];
 +
 +// 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 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 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\
 +        help: Try adding to dev-dependencies in Cargo.toml",
 +        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 config = base_config("ui");
 +    // 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(|_| num_cpus::get().to_string()),
++    );
 +    compiletest::run_tests(&config);
 +}
 +
 +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();
 +}
 +
 +/// 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 0251fada9e85a6ea8aa5dbfc954292835fab394c,0000000000000000000000000000000000000000..9f283337c7e132d8d6d23de3c28ac50614d243ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,129 @@@
 +#![allow(dead_code)]
 +
 +//! Used to test that certain lints don't trigger in imported external macros
 +
 +#[macro_export]
 +macro_rules! foofoo {
 +    () => {
 +        loop {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! must_use_unit {
 +    () => {
 +        #[must_use]
 +        fn foo() {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! try_err {
 +    () => {
 +        pub fn try_err_fn() -> Result<i32, i32> {
 +            let err: i32 = 1;
 +            // To avoid warnings during rustfix
 +            if true { Err(err)? } else { Ok(2) }
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! string_add {
 +    () => {
 +        let y = "".to_owned();
 +        let z = y + "...";
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! take_external {
 +    ($s:expr) => {
 +        std::mem::replace($s, Default::default())
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! option_env_unwrap_external {
 +    ($env: expr) => {
 +        option_env!($env).unwrap()
 +    };
 +    ($env: expr, $message: expr) => {
 +        option_env!($env).expect($message)
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ref_arg_binding {
 +    () => {
 +        let ref _y = 42;
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! ref_arg_function {
 +    () => {
 +        fn fun_example(ref _x: usize) {}
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! as_conv_with_arg {
 +    (0u32 as u64) => {
 +        ()
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! as_conv {
 +    () => {
 +        0u32 as u64
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! large_enum_variant {
 +    () => {
 +        enum LargeEnumInMacro {
 +            A(i32),
 +            B([i32; 8000]),
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! field_reassign_with_default {
 +    () => {
 +        #[derive(Default)]
 +        struct A {
 +            pub i: i32,
 +            pub j: i64,
 +        }
 +        fn lint() {
 +            let mut a: A = Default::default();
 +            a.i = 42;
 +            a;
 +        }
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! default_numeric_fallback {
 +    () => {
 +        let x = 22;
 +    };
 +}
 +
 +#[macro_export]
 +macro_rules! mut_mut {
 +    () => {
 +        let mut_mut_ty: &mut &mut u32 = &mut &mut 1u32;
 +    };
 +}
++
++#[macro_export]
++macro_rules! ptr_as_ptr_cast {
++    ($ptr: ident) => {
++        $ptr as *const i32
++    };
++}
index dd6640a387a23877548f96c8b6a40185b27c7cfd,0000000000000000000000000000000000000000..57e5b55045b95de03495fa9a9ca22db68a6657b1
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,192 @@@
- use std::sync::Mutex;
 +#![warn(clippy::await_holding_lock)]
 +
- async fn bad(x: &Mutex<u32>) -> u32 {
-     let guard = x.lock().unwrap();
-     baz().await
- }
++// When adding or modifying a test, please do the same for parking_lot::Mutex.
++mod std_mutex {
++    use super::baz;
++    use std::sync::{Mutex, RwLock};
 +
- async fn good(x: &Mutex<u32>) -> u32 {
-     {
++    pub async fn bad(x: &Mutex<u32>) -> u32 {
++        let guard = x.lock().unwrap();
++        baz().await
++    }
 +
-         let y = *guard + 1;
++    pub async fn good(x: &Mutex<u32>) -> u32 {
++        {
++            let guard = x.lock().unwrap();
++            let y = *guard + 1;
++        }
++        baz().await;
 +        let guard = x.lock().unwrap();
-     baz().await;
-     let guard = x.lock().unwrap();
-     47
- }
++        47
 +    }
- async fn baz() -> u32 {
-     42
- }
 +
- async fn also_bad(x: &Mutex<u32>) -> u32 {
-     let first = baz().await;
++    pub async fn bad_rw(x: &RwLock<u32>) -> u32 {
++        let guard = x.read().unwrap();
++        baz().await
++    }
 +
-     let guard = x.lock().unwrap();
++    pub async fn bad_rw_write(x: &RwLock<u32>) -> u32 {
++        let mut guard = x.write().unwrap();
++        baz().await
++    }
 +
-     let second = baz().await;
++    pub async fn good_rw(x: &RwLock<u32>) -> u32 {
++        {
++            let guard = x.read().unwrap();
++            let y = *guard + 1;
++        }
++        {
++            let mut guard = x.write().unwrap();
++            *guard += 1;
++        }
++        baz().await;
++        let guard = x.read().unwrap();
++        47
++    }
 +
-     let third = baz().await;
++    pub async fn also_bad(x: &Mutex<u32>) -> u32 {
++        let first = baz().await;
 +
-     first + second + third
++        let guard = x.lock().unwrap();
 +
- async fn not_good(x: &Mutex<u32>) -> u32 {
-     let first = baz().await;
++        let second = baz().await;
++
++        let third = baz().await;
++
++        first + second + third
++    }
++
++    pub async fn not_good(x: &Mutex<u32>) -> u32 {
++        let first = baz().await;
++
++        let second = {
++            let guard = x.lock().unwrap();
++            baz().await
++        };
++
++        let third = baz().await;
++
++        first + second + third
++    }
++
++    #[allow(clippy::manual_async_fn)]
++    pub fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
++        async move {
++            let guard = x.lock().unwrap();
++            baz().await
++        }
++    }
 +}
 +
-     let second = {
-         let guard = x.lock().unwrap();
++// When adding or modifying a test, please do the same for std::Mutex.
++mod parking_lot_mutex {
++    use super::baz;
++    use parking_lot::{Mutex, RwLock};
 +
-     };
++    pub async fn bad(x: &Mutex<u32>) -> u32 {
++        let guard = x.lock();
 +        baz().await
-     let third = baz().await;
++    }
 +
-     first + second + third
- }
++    pub async fn good(x: &Mutex<u32>) -> u32 {
++        {
++            let guard = x.lock();
++            let y = *guard + 1;
++        }
++        baz().await;
++        let guard = x.lock();
++        47
++    }
 +
- #[allow(clippy::manual_async_fn)]
- fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
-     async move {
-         let guard = x.lock().unwrap();
++    pub async fn bad_rw(x: &RwLock<u32>) -> u32 {
++        let guard = x.read();
++        baz().await
++    }
 +
-     let m = Mutex::new(100);
-     good(&m);
-     bad(&m);
-     also_bad(&m);
-     not_good(&m);
-     block_bad(&m);
++    pub async fn bad_rw_write(x: &RwLock<u32>) -> u32 {
++        let mut guard = x.write();
 +        baz().await
 +    }
++
++    pub async fn good_rw(x: &RwLock<u32>) -> u32 {
++        {
++            let guard = x.read();
++            let y = *guard + 1;
++        }
++        {
++            let mut guard = x.write();
++            *guard += 1;
++        }
++        baz().await;
++        let guard = x.read();
++        47
++    }
++
++    pub async fn also_bad(x: &Mutex<u32>) -> u32 {
++        let first = baz().await;
++
++        let guard = x.lock();
++
++        let second = baz().await;
++
++        let third = baz().await;
++
++        first + second + third
++    }
++
++    pub async fn not_good(x: &Mutex<u32>) -> u32 {
++        let first = baz().await;
++
++        let second = {
++            let guard = x.lock();
++            baz().await
++        };
++
++        let third = baz().await;
++
++        first + second + third
++    }
++
++    #[allow(clippy::manual_async_fn)]
++    pub fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
++        async move {
++            let guard = x.lock();
++            baz().await
++        }
++    }
++}
++
++async fn baz() -> u32 {
++    42
++}
++
++async fn no_await(x: std::sync::Mutex<u32>) {
++    let mut guard = x.lock().unwrap();
++    *guard += 1;
++}
++
++// FIXME: FP, because the `MutexGuard` is dropped before crossing the await point. This is
++// something the needs to be fixed in rustc. There's already drop-tracking, but this is currently
++// disabled, see rust-lang/rust#93751. This case isn't picked up by drop-tracking though. If the
++// `*guard += 1` is removed it is picked up.
++async fn dropped_before_await(x: std::sync::Mutex<u32>) {
++    let mut guard = x.lock().unwrap();
++    *guard += 1;
++    drop(guard);
++    baz().await;
 +}
 +
 +fn main() {
++    let m = std::sync::Mutex::new(100);
++    std_mutex::good(&m);
++    std_mutex::bad(&m);
++    std_mutex::also_bad(&m);
++    std_mutex::not_good(&m);
++    std_mutex::block_bad(&m);
++
++    let m = parking_lot::Mutex::new(100);
++    parking_lot_mutex::good(&m);
++    parking_lot_mutex::bad(&m);
++    parking_lot_mutex::also_bad(&m);
++    parking_lot_mutex::not_good(&m);
 +}
index ddfb104cdfbd07ab1207f133e075182b3d409d0e,0000000000000000000000000000000000000000..976da8d924247897317bed03461c579077a855cd
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,208 @@@
- error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
-   --> $DIR/await_holding_lock.rs:6:9
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:9:13
 +   |
- LL |     let guard = x.lock().unwrap();
-    |         ^^^^^
++LL |         let guard = x.lock().unwrap();
++   |             ^^^^^
 +   |
 +   = note: `-D clippy::await-holding-lock` implied by `-D warnings`
- note: these are all the await points this lock is held through
-   --> $DIR/await_holding_lock.rs:6:5
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:9:9
 +   |
- LL | /     let guard = x.lock().unwrap();
- LL | |     baz().await
- LL | | }
-    | |_^
++LL | /         let guard = x.lock().unwrap();
++LL | |         baz().await
++LL | |     }
++   | |_____^
 +
- error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
-   --> $DIR/await_holding_lock.rs:27:9
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:24:13
 +   |
- LL |     let guard = x.lock().unwrap();
-    |         ^^^^^
++LL |         let guard = x.read().unwrap();
++   |             ^^^^^
 +   |
- note: these are all the await points this lock is held through
-   --> $DIR/await_holding_lock.rs:27:5
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:24:9
 +   |
- LL | /     let guard = x.lock().unwrap();
++LL | /         let guard = x.read().unwrap();
++LL | |         baz().await
++LL | |     }
++   | |_____^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:29:13
++   |
++LL |         let mut guard = x.write().unwrap();
++   |             ^^^^^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:29:9
++   |
++LL | /         let mut guard = x.write().unwrap();
++LL | |         baz().await
++LL | |     }
++   | |_____^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:50:13
++   |
++LL |         let guard = x.lock().unwrap();
++   |             ^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:50:9
++   |
++LL | /         let guard = x.lock().unwrap();
 +LL | |
- LL | |     let second = baz().await;
++LL | |         let second = baz().await;
 +LL | |
 +...  |
- LL | |     first + second + third
- LL | | }
-    | |_^
++LL | |         first + second + third
++LL | |     }
++   | |_____^
 +
- error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
-   --> $DIR/await_holding_lock.rs:40:13
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:63:17
 +   |
- LL |         let guard = x.lock().unwrap();
++LL |             let guard = x.lock().unwrap();
++   |                 ^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:63:13
++   |
++LL | /             let guard = x.lock().unwrap();
++LL | |             baz().await
++LL | |         };
++   | |_________^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:75:17
++   |
++LL |             let guard = x.lock().unwrap();
++   |                 ^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:75:13
++   |
++LL | /             let guard = x.lock().unwrap();
++LL | |             baz().await
++LL | |         }
++   | |_________^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:87:13
++   |
++LL |         let guard = x.lock();
 +   |             ^^^^^
 +   |
- note: these are all the await points this lock is held through
-   --> $DIR/await_holding_lock.rs:40:9
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:87:9
 +   |
- LL | /         let guard = x.lock().unwrap();
++LL | /         let guard = x.lock();
 +LL | |         baz().await
- LL | |     };
++LL | |     }
 +   | |_____^
 +
- error: this MutexGuard is held across an 'await' point. Consider using an async-aware Mutex type or ensuring the MutexGuard is dropped before calling await
-   --> $DIR/await_holding_lock.rs:52:13
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:102:13
 +   |
- LL |         let guard = x.lock().unwrap();
++LL |         let guard = x.read();
 +   |             ^^^^^
 +   |
- note: these are all the await points this lock is held through
-   --> $DIR/await_holding_lock.rs:52:9
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:102:9
 +   |
- LL | /         let guard = x.lock().unwrap();
++LL | /         let guard = x.read();
 +LL | |         baz().await
 +LL | |     }
 +   | |_____^
 +
- error: aborting due to 4 previous errors
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:107:13
++   |
++LL |         let mut guard = x.write();
++   |             ^^^^^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:107:9
++   |
++LL | /         let mut guard = x.write();
++LL | |         baz().await
++LL | |     }
++   | |_____^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:128:13
++   |
++LL |         let guard = x.lock();
++   |             ^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:128:9
++   |
++LL | /         let guard = x.lock();
++LL | |
++LL | |         let second = baz().await;
++LL | |
++...  |
++LL | |         first + second + third
++LL | |     }
++   | |_____^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:141:17
++   |
++LL |             let guard = x.lock();
++   |                 ^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:141:13
++   |
++LL | /             let guard = x.lock();
++LL | |             baz().await
++LL | |         };
++   | |_________^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:153:17
++   |
++LL |             let guard = x.lock();
++   |                 ^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:153:13
++   |
++LL | /             let guard = x.lock();
++LL | |             baz().await
++LL | |         }
++   | |_________^
++
++error: this `MutexGuard` is held across an `await` point
++  --> $DIR/await_holding_lock.rs:173:9
++   |
++LL |     let mut guard = x.lock().unwrap();
++   |         ^^^^^^^^^
++   |
++   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
++note: these are all the `await` points this lock is held through
++  --> $DIR/await_holding_lock.rs:173:5
++   |
++LL | /     let mut guard = x.lock().unwrap();
++LL | |     *guard += 1;
++LL | |     drop(guard);
++LL | |     baz().await;
++LL | | }
++   | |_^
++
++error: aborting due to 13 previous errors
 +
index 67cc0032be2f46742725351dd9beca969d458b4e,0000000000000000000000000000000000000000..4339fca735dd4554eabcc0d7a9ad6a0963fc2d31
mode 100644,000000..100644
--- /dev/null
@@@ -1,95 -1,0 +1,101 @@@
- error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
++error: this `RefCell` reference is held across an `await` point
 +  --> $DIR/await_holding_refcell_ref.rs:6:9
 +   |
 +LL |     let b = x.borrow();
 +   |         ^
 +   |
 +   = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings`
- note: these are all the await points this ref is held through
++   = help: ensure the reference is dropped before calling `await`
++note: these are all the `await` points this reference is held through
 +  --> $DIR/await_holding_refcell_ref.rs:6:5
 +   |
 +LL | /     let b = x.borrow();
 +LL | |     baz().await
 +LL | | }
 +   | |_^
 +
- error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
++error: this `RefCell` reference is held across an `await` point
 +  --> $DIR/await_holding_refcell_ref.rs:11:9
 +   |
 +LL |     let b = x.borrow_mut();
 +   |         ^
 +   |
- note: these are all the await points this ref is held through
++   = help: ensure the reference is dropped before calling `await`
++note: these are all the `await` points this reference is held through
 +  --> $DIR/await_holding_refcell_ref.rs:11:5
 +   |
 +LL | /     let b = x.borrow_mut();
 +LL | |     baz().await
 +LL | | }
 +   | |_^
 +
- error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
++error: this `RefCell` reference is held across an `await` point
 +  --> $DIR/await_holding_refcell_ref.rs:32:9
 +   |
 +LL |     let b = x.borrow_mut();
 +   |         ^
 +   |
- note: these are all the await points this ref is held through
++   = help: ensure the reference is dropped before calling `await`
++note: these are all the `await` points this reference is held through
 +  --> $DIR/await_holding_refcell_ref.rs:32:5
 +   |
 +LL | /     let b = x.borrow_mut();
 +LL | |
 +LL | |     let second = baz().await;
 +LL | |
 +...  |
 +LL | |     first + second + third
 +LL | | }
 +   | |_^
 +
- error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
++error: this `RefCell` reference is held across an `await` point
 +  --> $DIR/await_holding_refcell_ref.rs:44:9
 +   |
 +LL |     let b = x.borrow_mut();
 +   |         ^
 +   |
- note: these are all the await points this ref is held through
++   = help: ensure the reference is dropped before calling `await`
++note: these are all the `await` points this reference is held through
 +  --> $DIR/await_holding_refcell_ref.rs:44:5
 +   |
 +LL | /     let b = x.borrow_mut();
 +LL | |
 +LL | |     let second = baz().await;
 +LL | |
 +...  |
 +LL | |     first + second + third
 +LL | | }
 +   | |_^
 +
- error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
++error: this `RefCell` reference is held across an `await` point
 +  --> $DIR/await_holding_refcell_ref.rs:59:13
 +   |
 +LL |         let b = x.borrow_mut();
 +   |             ^
 +   |
- note: these are all the await points this ref is held through
++   = help: ensure the reference is dropped before calling `await`
++note: these are all the `await` points this reference is held through
 +  --> $DIR/await_holding_refcell_ref.rs:59:9
 +   |
 +LL | /         let b = x.borrow_mut();
 +LL | |         baz().await
 +LL | |     };
 +   | |_____^
 +
- error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await
++error: this `RefCell` reference is held across an `await` point
 +  --> $DIR/await_holding_refcell_ref.rs:71:13
 +   |
 +LL |         let b = x.borrow_mut();
 +   |             ^
 +   |
- note: these are all the await points this ref is held through
++   = help: ensure the reference is dropped before calling `await`
++note: these are all the `await` points this reference is held through
 +  --> $DIR/await_holding_refcell_ref.rs:71:9
 +   |
 +LL | /         let b = x.borrow_mut();
 +LL | |         baz().await
 +LL | |     }
 +   | |_____^
 +
 +error: aborting due to 6 previous errors
 +
index ebc1ed5587fe30a9b8f6675e6ac51b79f1a9725f,0000000000000000000000000000000000000000..2e31ad3172ee8cef1a5271c27c2fd8367b7d417c
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,254 @@@
++#![feature(repr128)]
++#![allow(incomplete_features)]
++
 +#[warn(
 +    clippy::cast_precision_loss,
 +    clippy::cast_possible_truncation,
 +    clippy::cast_sign_loss,
 +    clippy::cast_possible_wrap
 +)]
 +#[allow(clippy::no_effect, clippy::unnecessary_operation)]
 +fn main() {
 +    // Test clippy::cast_precision_loss
 +    let x0 = 1i32;
 +    x0 as f32;
 +    let x1 = 1i64;
 +    x1 as f32;
 +    x1 as f64;
 +    let x2 = 1u32;
 +    x2 as f32;
 +    let x3 = 1u64;
 +    x3 as f32;
 +    x3 as f64;
 +    // Test clippy::cast_possible_truncation
 +    1f32 as i32;
 +    1f32 as u32;
 +    1f64 as f32;
 +    1i32 as i8;
 +    1i32 as u8;
 +    1f64 as isize;
 +    1f64 as usize;
 +    // Test clippy::cast_possible_wrap
 +    1u8 as i8;
 +    1u16 as i16;
 +    1u32 as i32;
 +    1u64 as i64;
 +    1usize as isize;
 +    // Test clippy::cast_sign_loss
 +    1i32 as u32;
 +    -1i32 as u32;
 +    1isize as usize;
 +    -1isize as usize;
 +    0i8 as u8;
 +    i8::MAX as u8;
 +    i16::MAX as u16;
 +    i32::MAX as u32;
 +    i64::MAX as u64;
 +    i128::MAX as u128;
 +
 +    (-1i8).abs() as u8;
 +    (-1i16).abs() as u16;
 +    (-1i32).abs() as u32;
 +    (-1i64).abs() as u64;
 +    (-1isize).abs() as usize;
 +
 +    (-1i8).checked_abs().unwrap() as u8;
 +    (-1i16).checked_abs().unwrap() as u16;
 +    (-1i32).checked_abs().unwrap() as u32;
 +    (-1i64).checked_abs().unwrap() as u64;
 +    (-1isize).checked_abs().unwrap() as usize;
 +
 +    (-1i8).rem_euclid(1i8) as u8;
 +    (-1i8).rem_euclid(1i8) as u16;
 +    (-1i16).rem_euclid(1i16) as u16;
 +    (-1i16).rem_euclid(1i16) as u32;
 +    (-1i32).rem_euclid(1i32) as u32;
 +    (-1i32).rem_euclid(1i32) as u64;
 +    (-1i64).rem_euclid(1i64) as u64;
 +    (-1i64).rem_euclid(1i64) as u128;
 +    (-1isize).rem_euclid(1isize) as usize;
 +    (1i8).rem_euclid(-1i8) as u8;
 +    (1i8).rem_euclid(-1i8) as u16;
 +    (1i16).rem_euclid(-1i16) as u16;
 +    (1i16).rem_euclid(-1i16) as u32;
 +    (1i32).rem_euclid(-1i32) as u32;
 +    (1i32).rem_euclid(-1i32) as u64;
 +    (1i64).rem_euclid(-1i64) as u64;
 +    (1i64).rem_euclid(-1i64) as u128;
 +    (1isize).rem_euclid(-1isize) as usize;
 +
 +    (-1i8).checked_rem_euclid(1i8).unwrap() as u8;
 +    (-1i8).checked_rem_euclid(1i8).unwrap() as u16;
 +    (-1i16).checked_rem_euclid(1i16).unwrap() as u16;
 +    (-1i16).checked_rem_euclid(1i16).unwrap() as u32;
 +    (-1i32).checked_rem_euclid(1i32).unwrap() as u32;
 +    (-1i32).checked_rem_euclid(1i32).unwrap() as u64;
 +    (-1i64).checked_rem_euclid(1i64).unwrap() as u64;
 +    (-1i64).checked_rem_euclid(1i64).unwrap() as u128;
 +    (-1isize).checked_rem_euclid(1isize).unwrap() as usize;
 +    (1i8).checked_rem_euclid(-1i8).unwrap() as u8;
 +    (1i8).checked_rem_euclid(-1i8).unwrap() as u16;
 +    (1i16).checked_rem_euclid(-1i16).unwrap() as u16;
 +    (1i16).checked_rem_euclid(-1i16).unwrap() as u32;
 +    (1i32).checked_rem_euclid(-1i32).unwrap() as u32;
 +    (1i32).checked_rem_euclid(-1i32).unwrap() as u64;
 +    (1i64).checked_rem_euclid(-1i64).unwrap() as u64;
 +    (1i64).checked_rem_euclid(-1i64).unwrap() as u128;
 +    (1isize).checked_rem_euclid(-1isize).unwrap() as usize;
 +
 +    // no lint for `cast_possible_truncation`
 +    // with `signum` method call (see issue #5395)
 +    let x: i64 = 5;
 +    let _ = x.signum() as i32;
 +
 +    let s = x.signum();
 +    let _ = s as i32;
 +
 +    // Test for signed min
 +    (-99999999999i64).min(1) as i8; // should be linted because signed
 +
 +    // Test for various operations that remove enough bits for the result to fit
 +    (999999u64 & 1) as u8;
 +    (999999u64 % 15) as u8;
 +    (999999u64 / 0x1_0000_0000_0000) as u16;
 +    ({ 999999u64 >> 56 }) as u8;
 +    ({
 +        let x = 999999u64;
 +        x.min(1)
 +    }) as u8;
 +    999999u64.clamp(0, 255) as u8;
 +    999999u64.clamp(0, 256) as u8; // should still be linted
++
++    #[derive(Clone, Copy)]
++    enum E1 {
++        A,
++        B,
++        C,
++    }
++    impl E1 {
++        fn test(self) {
++            let _ = self as u8; // Don't lint. `0..=2` fits in u8
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    enum E2 {
++        A = 255,
++        B,
++    }
++    impl E2 {
++        fn test(self) {
++            let _ = self as u8;
++            let _ = Self::B as u8;
++            let _ = self as i16; // Don't lint. `255..=256` fits in i16
++            let _ = Self::A as u8; // Don't lint.
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    enum E3 {
++        A = -1,
++        B,
++        C = 50,
++    }
++    impl E3 {
++        fn test(self) {
++            let _ = self as i8; // Don't lint. `-1..=50` fits in i8
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    enum E4 {
++        A = -128,
++        B,
++    }
++    impl E4 {
++        fn test(self) {
++            let _ = self as i8; // Don't lint. `-128..=-127` fits in i8
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    enum E5 {
++        A = -129,
++        B = 127,
++    }
++    impl E5 {
++        fn test(self) {
++            let _ = self as i8;
++            let _ = Self::A as i8;
++            let _ = self as i16; // Don't lint. `-129..=127` fits in i16
++            let _ = Self::B as u8; // Don't lint.
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    #[repr(u32)]
++    enum E6 {
++        A = u16::MAX as u32,
++        B,
++    }
++    impl E6 {
++        fn test(self) {
++            let _ = self as i16;
++            let _ = Self::A as u16; // Don't lint. `2^16-1` fits in u16
++            let _ = self as u32; // Don't lint. `2^16-1..=2^16` fits in u32
++            let _ = Self::A as u16; // Don't lint.
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    #[repr(u64)]
++    enum E7 {
++        A = u32::MAX as u64,
++        B,
++    }
++    impl E7 {
++        fn test(self) {
++            let _ = self as usize;
++            let _ = Self::A as usize; // Don't lint.
++            let _ = self as u64; // Don't lint. `2^32-1..=2^32` fits in u64
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    #[repr(i128)]
++    enum E8 {
++        A = i128::MIN,
++        B,
++        C = 0,
++        D = i128::MAX,
++    }
++    impl E8 {
++        fn test(self) {
++            let _ = self as i128; // Don't lint. `-(2^127)..=2^127-1` fits it i128
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    #[repr(u128)]
++    enum E9 {
++        A,
++        B = u128::MAX,
++    }
++    impl E9 {
++        fn test(self) {
++            let _ = Self::A as u8; // Don't lint.
++            let _ = self as u128; // Don't lint. `0..=2^128-1` fits in u128
++        }
++    }
++
++    #[derive(Clone, Copy)]
++    #[repr(usize)]
++    enum E10 {
++        A,
++        B = u32::MAX as usize,
++    }
++    impl E10 {
++        fn test(self) {
++            let _ = self as u16;
++            let _ = Self::B as u32; // Don't lint.
++            let _ = self as u64; // Don't lint.
++        }
++    }
 +}
index edf8790cf33d861c3978669145d5e500f2232314,0000000000000000000000000000000000000000..7a68c0984f140dda78c8c6d5b927b6867b76beac
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,198 @@@
-   --> $DIR/cast.rs:11:5
 +error: casting `i32` to `f32` causes a loss of precision (`i32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-   --> $DIR/cast.rs:13:5
++  --> $DIR/cast.rs:14:5
 +   |
 +LL |     x0 as f32;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-precision-loss` implied by `-D warnings`
 +
 +error: casting `i64` to `f32` causes a loss of precision (`i64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-   --> $DIR/cast.rs:14:5
++  --> $DIR/cast.rs:16:5
 +   |
 +LL |     x1 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `i64` to `f64` causes a loss of precision (`i64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-   --> $DIR/cast.rs:16:5
++  --> $DIR/cast.rs:17:5
 +   |
 +LL |     x1 as f64;
 +   |     ^^^^^^^^^
 +
 +error: casting `u32` to `f32` causes a loss of precision (`u32` is 32 bits wide, but `f32`'s mantissa is only 23 bits wide)
-   --> $DIR/cast.rs:18:5
++  --> $DIR/cast.rs:19:5
 +   |
 +LL |     x2 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `u64` to `f32` causes a loss of precision (`u64` is 64 bits wide, but `f32`'s mantissa is only 23 bits wide)
-   --> $DIR/cast.rs:19:5
++  --> $DIR/cast.rs:21:5
 +   |
 +LL |     x3 as f32;
 +   |     ^^^^^^^^^
 +
 +error: casting `u64` to `f64` causes a loss of precision (`u64` is 64 bits wide, but `f64`'s mantissa is only 52 bits wide)
-   --> $DIR/cast.rs:21:5
++  --> $DIR/cast.rs:22:5
 +   |
 +LL |     x3 as f64;
 +   |     ^^^^^^^^^
 +
 +error: casting `f32` to `i32` may truncate the value
-   --> $DIR/cast.rs:22:5
++  --> $DIR/cast.rs:24:5
 +   |
 +LL |     1f32 as i32;
 +   |     ^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-possible-truncation` implied by `-D warnings`
 +
 +error: casting `f32` to `u32` may truncate the value
-   --> $DIR/cast.rs:22:5
++  --> $DIR/cast.rs:25:5
 +   |
 +LL |     1f32 as u32;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `f32` to `u32` may lose the sign of the value
-   --> $DIR/cast.rs:23:5
++  --> $DIR/cast.rs:25:5
 +   |
 +LL |     1f32 as u32;
 +   |     ^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-sign-loss` implied by `-D warnings`
 +
 +error: casting `f64` to `f32` may truncate the value
-   --> $DIR/cast.rs:24:5
++  --> $DIR/cast.rs:26:5
 +   |
 +LL |     1f64 as f32;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `i32` to `i8` may truncate the value
-   --> $DIR/cast.rs:25:5
++  --> $DIR/cast.rs:27:5
 +   |
 +LL |     1i32 as i8;
 +   |     ^^^^^^^^^^
 +
 +error: casting `i32` to `u8` may truncate the value
-   --> $DIR/cast.rs:26:5
++  --> $DIR/cast.rs:28:5
 +   |
 +LL |     1i32 as u8;
 +   |     ^^^^^^^^^^
 +
 +error: casting `f64` to `isize` may truncate the value
-   --> $DIR/cast.rs:27:5
++  --> $DIR/cast.rs:29:5
 +   |
 +LL |     1f64 as isize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `f64` to `usize` may truncate the value
-   --> $DIR/cast.rs:27:5
++  --> $DIR/cast.rs:30:5
 +   |
 +LL |     1f64 as usize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `f64` to `usize` may lose the sign of the value
-   --> $DIR/cast.rs:29:5
++  --> $DIR/cast.rs:30:5
 +   |
 +LL |     1f64 as usize;
 +   |     ^^^^^^^^^^^^^
 +
 +error: casting `u8` to `i8` may wrap around the value
-   --> $DIR/cast.rs:30:5
++  --> $DIR/cast.rs:32:5
 +   |
 +LL |     1u8 as i8;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
 +
 +error: casting `u16` to `i16` may wrap around the value
-   --> $DIR/cast.rs:31:5
++  --> $DIR/cast.rs:33:5
 +   |
 +LL |     1u16 as i16;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `u32` to `i32` may wrap around the value
-   --> $DIR/cast.rs:32:5
++  --> $DIR/cast.rs:34:5
 +   |
 +LL |     1u32 as i32;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `u64` to `i64` may wrap around the value
-   --> $DIR/cast.rs:33:5
++  --> $DIR/cast.rs:35:5
 +   |
 +LL |     1u64 as i64;
 +   |     ^^^^^^^^^^^
 +
 +error: casting `usize` to `isize` may wrap around the value
-   --> $DIR/cast.rs:36:5
++  --> $DIR/cast.rs:36:5
 +   |
 +LL |     1usize as isize;
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: casting `i32` to `u32` may lose the sign of the value
-   --> $DIR/cast.rs:38:5
++  --> $DIR/cast.rs:39:5
 +   |
 +LL |     -1i32 as u32;
 +   |     ^^^^^^^^^^^^
 +
 +error: casting `isize` to `usize` may lose the sign of the value
-   --> $DIR/cast.rs:105:5
++  --> $DIR/cast.rs:41:5
 +   |
 +LL |     -1isize as usize;
 +   |     ^^^^^^^^^^^^^^^^
 +
 +error: casting `i64` to `i8` may truncate the value
-   --> $DIR/cast.rs:117:5
++  --> $DIR/cast.rs:108:5
 +   |
 +LL |     (-99999999999i64).min(1) as i8; // should be linted because signed
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: casting `u64` to `u8` may truncate the value
- error: aborting due to 24 previous errors
++  --> $DIR/cast.rs:120:5
 +   |
 +LL |     999999u64.clamp(0, 256) as u8; // should still be linted
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: casting `main::E2` to `u8` may truncate the value
++  --> $DIR/cast.rs:141:21
++   |
++LL |             let _ = self as u8;
++   |                     ^^^^^^^^^^
++
++error: casting `main::E2::B` to `u8` will truncate the value
++  --> $DIR/cast.rs:142:21
++   |
++LL |             let _ = Self::B as u8;
++   |                     ^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::cast-enum-truncation` implied by `-D warnings`
++
++error: casting `main::E5` to `i8` may truncate the value
++  --> $DIR/cast.rs:178:21
++   |
++LL |             let _ = self as i8;
++   |                     ^^^^^^^^^^
++
++error: casting `main::E5::A` to `i8` will truncate the value
++  --> $DIR/cast.rs:179:21
++   |
++LL |             let _ = Self::A as i8;
++   |                     ^^^^^^^^^^^^^
++
++error: casting `main::E6` to `i16` may truncate the value
++  --> $DIR/cast.rs:193:21
++   |
++LL |             let _ = self as i16;
++   |                     ^^^^^^^^^^^
++
++error: casting `main::E7` to `usize` may truncate the value on targets with 32-bit wide pointers
++  --> $DIR/cast.rs:208:21
++   |
++LL |             let _ = self as usize;
++   |                     ^^^^^^^^^^^^^
++
++error: casting `main::E10` to `u16` may truncate the value
++  --> $DIR/cast.rs:249:21
++   |
++LL |             let _ = self as u16;
++   |                     ^^^^^^^^^^^
++
++error: aborting due to 31 previous errors
 +
index d74e2611ee1fdad6c0596fe35ed44ef353317c6a,0000000000000000000000000000000000000000..9b03c9b47832f60e1626b6477d404ec8fab87157
mode 100644,000000..100644
--- /dev/null
@@@ -1,19 -1,0 +1,42 @@@
 +#![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);
++}
++
++mod issue7274 {
++    trait Thing<'b> {
++        fn foo(&self);
++    }
++
++    macro_rules! define_thing {
++        ($thing:ident, $body:expr) => {
++            impl<'a> Thing<'a> for $thing {
++                fn foo<'b>(&self) {
++                    $body
++                }
++            }
++        };
++    }
++
++    struct MyThing;
++    define_thing!(MyThing, {
++        dbg!(2);
++    });
 +}
index 0abe953af26139b40754ae010550e106450f42ae,0000000000000000000000000000000000000000..8ee1b328720d919f2e9629bae91bd8ae5ca8e4bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,113 @@@
- error: aborting due to 7 previous errors
 +error: `dbg!` macro is intended as a debugging tool
 +  --> $DIR/dbg_macro.rs:4: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:8: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:9: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:11: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:16: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:17: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:18: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:19: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:20: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:40:9
++   |
++LL |         dbg!(2);
++   |         ^^^^^^^
++   |
++help: ensure to avoid having uses of it in version control
++   |
++LL |         2;
++   |         ~
++
++error: aborting due to 10 previous errors
 +
index 9114d8754dcc8f4b0ad598aa2b0faaaeecd882fb,0000000000000000000000000000000000000000..264dd4efaeb8699648f3818c20f845704ac963c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,99 @@@
-         "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
-         s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19,
 +// run-rustfix
 +
 +#![allow(unused_imports, dead_code)]
 +#![deny(clippy::default_trait_access)]
 +
 +use std::default;
 +use std::default::Default as D2;
 +use std::string;
 +
 +fn main() {
 +    let s1: String = std::string::String::default();
 +
 +    let s2 = String::default();
 +
 +    let s3: String = std::string::String::default();
 +
 +    let s4: String = std::string::String::default();
 +
 +    let s5 = string::String::default();
 +
 +    let s6: String = std::string::String::default();
 +
 +    let s7 = std::string::String::default();
 +
 +    let s8: String = DefaultFactory::make_t_badly();
 +
 +    let s9: String = DefaultFactory::make_t_nicely();
 +
 +    let s10 = DerivedDefault::default();
 +
 +    let s11: GenericDerivedDefault<String> = GenericDerivedDefault::default();
 +
 +    let s12 = GenericDerivedDefault::<String>::default();
 +
 +    let s13 = TupleDerivedDefault::default();
 +
 +    let s14: TupleDerivedDefault = TupleDerivedDefault::default();
 +
 +    let s15: ArrayDerivedDefault = ArrayDerivedDefault::default();
 +
 +    let s16 = ArrayDerivedDefault::default();
 +
 +    let s17: TupleStructDerivedDefault = TupleStructDerivedDefault::default();
 +
 +    let s18 = TupleStructDerivedDefault::default();
 +
 +    let s19 = <DerivedDefault as Default>::default();
 +
++    let s20 = UpdateSyntax {
++        s: "foo",
++        ..Default::default()
++    };
++
 +    println!(
++        "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]",
++        s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20,
 +    );
 +}
 +
 +struct DefaultFactory;
 +
 +impl DefaultFactory {
 +    pub fn make_t_badly<T: Default>() -> T {
 +        Default::default()
 +    }
 +
 +    pub fn make_t_nicely<T: Default>() -> T {
 +        T::default()
 +    }
 +}
 +
 +#[derive(Debug, Default)]
 +struct DerivedDefault {
 +    pub s: String,
 +}
 +
 +#[derive(Debug, Default)]
 +struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
 +    pub s: T,
 +}
 +
 +#[derive(Debug, Default)]
 +struct TupleDerivedDefault {
 +    pub s: (String, String),
 +}
 +
 +#[derive(Debug, Default)]
 +struct ArrayDerivedDefault {
 +    pub s: [String; 10],
 +}
 +
 +#[derive(Debug, Default)]
 +struct TupleStructDerivedDefault(String);
++
++#[derive(Debug, Default)]
++struct UpdateSyntax {
++    pub s: &'static str,
++    pub u: u64,
++}
index 8a5f0d6a74976665c8181638cde258c82ab50716,0000000000000000000000000000000000000000..a0930fab8e7c89db4214ab966975f9dd0381c506
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,99 @@@
-         "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}], [{:?}]",
-         s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19,
 +// run-rustfix
 +
 +#![allow(unused_imports, dead_code)]
 +#![deny(clippy::default_trait_access)]
 +
 +use std::default;
 +use std::default::Default as D2;
 +use std::string;
 +
 +fn main() {
 +    let s1: String = Default::default();
 +
 +    let s2 = String::default();
 +
 +    let s3: String = D2::default();
 +
 +    let s4: String = std::default::Default::default();
 +
 +    let s5 = string::String::default();
 +
 +    let s6: String = default::Default::default();
 +
 +    let s7 = std::string::String::default();
 +
 +    let s8: String = DefaultFactory::make_t_badly();
 +
 +    let s9: String = DefaultFactory::make_t_nicely();
 +
 +    let s10 = DerivedDefault::default();
 +
 +    let s11: GenericDerivedDefault<String> = Default::default();
 +
 +    let s12 = GenericDerivedDefault::<String>::default();
 +
 +    let s13 = TupleDerivedDefault::default();
 +
 +    let s14: TupleDerivedDefault = Default::default();
 +
 +    let s15: ArrayDerivedDefault = Default::default();
 +
 +    let s16 = ArrayDerivedDefault::default();
 +
 +    let s17: TupleStructDerivedDefault = Default::default();
 +
 +    let s18 = TupleStructDerivedDefault::default();
 +
 +    let s19 = <DerivedDefault as Default>::default();
 +
++    let s20 = UpdateSyntax {
++        s: "foo",
++        ..Default::default()
++    };
++
 +    println!(
++        "[{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}] [{:?}]",
++        s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15, s16, s17, s18, s19, s20,
 +    );
 +}
 +
 +struct DefaultFactory;
 +
 +impl DefaultFactory {
 +    pub fn make_t_badly<T: Default>() -> T {
 +        Default::default()
 +    }
 +
 +    pub fn make_t_nicely<T: Default>() -> T {
 +        T::default()
 +    }
 +}
 +
 +#[derive(Debug, Default)]
 +struct DerivedDefault {
 +    pub s: String,
 +}
 +
 +#[derive(Debug, Default)]
 +struct GenericDerivedDefault<T: Default + std::fmt::Debug> {
 +    pub s: T,
 +}
 +
 +#[derive(Debug, Default)]
 +struct TupleDerivedDefault {
 +    pub s: (String, String),
 +}
 +
 +#[derive(Debug, Default)]
 +struct ArrayDerivedDefault {
 +    pub s: [String; 10],
 +}
 +
 +#[derive(Debug, Default)]
 +struct TupleStructDerivedDefault(String);
++
++#[derive(Debug, Default)]
++struct UpdateSyntax {
++    pub s: &'static str,
++    pub u: u64,
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b26276218b78c7e0c110cb4c1831689a27372afb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-rustfix
++
++#![warn(clippy::deref_by_slicing)]
++
++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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6aa1408ba17698854ad1f9bcf361694b6aa5343e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++// run-rustfix
++
++#![warn(clippy::deref_by_slicing)]
++
++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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffd76de378df1d314b7d95fa04cf96f6efaac800
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: slicing when dereferencing would work
++  --> $DIR/deref_by_slicing.rs:9: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:10:13
++   |
++LL |     let _ = &mut vec[..];
++   |             ^^^^^^^^^^^^ help: dereference the original value instead: `&mut *vec`
++
++error: slicing when dereferencing would work
++  --> $DIR/deref_by_slicing.rs:13:13
++   |
++LL |     let _ = &ref_vec[..];
++   |             ^^^^^^^^^^^^ help: dereference the original value instead: `&**ref_vec`
++
++error: slicing when dereferencing would work
++  --> $DIR/deref_by_slicing.rs:14: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:15: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:18:13
++   |
++LL |     let _ = &s[..];
++   |             ^^^^^^ help: dereference the original value instead: `&*s`
++
++error: slicing when dereferencing would work
++  --> $DIR/deref_by_slicing.rs:21: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:25: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:28: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 618f80cdcf84923d9ca035b35e177c9cc22079e4,0000000000000000000000000000000000000000..5aedbea381f2317d80c661cc20f7ffbc07748eae
mode 100644,000000..100644
--- /dev/null
@@@ -1,258 -1,0 +1,277 @@@
 +// run-rustfix
 +
 +#![allow(
 +    unused,
 +    clippy::no_effect,
 +    clippy::redundant_closure_call,
 +    clippy::needless_pass_by_value,
 +    clippy::option_map_unit_fn,
 +    clippy::needless_borrow
 +)]
 +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 +
 +use std::path::{Path, PathBuf};
 +
 +macro_rules! mac {
 +    () => {
 +        foobar()
 +    };
 +}
 +
 +macro_rules! closure_mac {
 +    () => {
 +        |n| foo(n)
 +    };
 +}
 +
 +fn main() {
 +    let a = Some(1u8).map(foo);
 +    let c = Some(1u8).map(|a| {1+2; foo}(a));
 +    true.then(|| mac!()); // don't lint function in macro expansion
 +    Some(1).map(closure_mac!()); // don't lint closure in macro expansion
 +    let _: Option<Vec<u8>> = true.then(std::vec::Vec::new); // special case vec!
 +    let d = Some(1u8).map(|a| foo(foo2(a))); //is adjusted?
 +    all(&[1, 2, 3], &&2, below); //is adjusted
 +    unsafe {
 +        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
 +    }
 +
 +    // See #815
 +    let e = Some(1u8).map(divergent);
 +    let e = Some(1u8).map(generic);
 +    let e = Some(1u8).map(generic);
 +    // See #515
 +    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
 +        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
 +
 +    // issue #7224
 +    let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
 +}
 +
 +trait TestTrait {
 +    fn trait_foo(self) -> bool;
 +    fn trait_foo_ref(&self) -> bool;
 +}
 +
 +struct TestStruct<'a> {
 +    some_ref: &'a i32,
 +}
 +
 +impl<'a> TestStruct<'a> {
 +    fn foo(self) -> bool {
 +        false
 +    }
 +    unsafe fn foo_unsafe(self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<'a> TestTrait for TestStruct<'a> {
 +    fn trait_foo(self) -> bool {
 +        false
 +    }
 +    fn trait_foo_ref(&self) -> bool {
 +        false
 +    }
 +}
 +
 +impl<'a> std::ops::Deref for TestStruct<'a> {
 +    type Target = char;
 +    fn deref(&self) -> &char {
 +        &'a'
 +    }
 +}
 +
 +fn test_redundant_closures_containing_method_calls() {
 +    let i = 10;
 +    let e = Some(TestStruct { some_ref: &i }).map(TestStruct::foo);
 +    let e = Some(TestStruct { some_ref: &i }).map(TestTrait::trait_foo);
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
 +    let e = Some(&mut vec![1, 2, 3]).map(std::vec::Vec::clear);
 +    unsafe {
 +        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
 +    }
 +    let e = Some("str").map(std::string::ToString::to_string);
 +    let e = Some('a').map(char::to_uppercase);
 +    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
 +    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(char::to_ascii_uppercase).collect();
 +    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
 +    let c = Some(TestStruct { some_ref: &i })
 +        .as_ref()
 +        .map(|c| c.to_ascii_uppercase());
 +
 +    fn test_different_borrow_levels<T>(t: &[&T])
 +    where
 +        T: TestTrait,
 +    {
 +        t.iter().filter(|x| x.trait_foo_ref());
 +        t.iter().map(|x| x.trait_foo_ref());
 +    }
 +}
 +
 +struct Thunk<T>(Box<dyn FnMut() -> T>);
 +
 +impl<T> Thunk<T> {
 +    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
 +        let mut option = Some(f);
 +        // This should not trigger redundant_closure (#1439)
 +        Thunk(Box::new(move || option.take().unwrap()()))
 +    }
 +
 +    fn unwrap(self) -> T {
 +        let Thunk(mut f) = self;
 +        f()
 +    }
 +}
 +
 +fn foobar() {
 +    let thunk = Thunk::new(|| println!("Hello, world!"));
 +    thunk.unwrap()
 +}
 +
 +fn foo(_: u8) {}
 +
 +fn foo2(_: u8) -> u8 {
 +    1u8
 +}
 +
 +fn all<X, F>(x: &[X], y: &X, f: F) -> bool
 +where
 +    F: Fn(&X, &X) -> bool,
 +{
 +    x.iter().all(|e| f(e, y))
 +}
 +
 +fn below(x: &u8, y: &u8) -> bool {
 +    x < y
 +}
 +
 +unsafe fn unsafe_fn(_: u8) {}
 +
 +fn divergent(_: u8) -> ! {
 +    unimplemented!()
 +}
 +
 +fn generic<T>(_: T) -> u8 {
 +    0
 +}
 +
 +fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
 +    requires_fn_once(x);
 +}
 +fn requires_fn_once<T: FnOnce()>(_: T) {}
 +
 +fn test_redundant_closure_with_function_pointer() {
 +    type FnPtrType = fn(u8);
 +    let foo_ptr: FnPtrType = foo;
 +    let a = Some(1u8).map(foo_ptr);
 +}
 +
 +fn test_redundant_closure_with_another_closure() {
 +    let closure = |a| println!("{}", a);
 +    let a = Some(1u8).map(closure);
 +}
 +
 +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
 +    // Currently f is called when result of make_lazy is called.
 +    // If the closure is removed, f will be called when make_lazy itself is
 +    // called. This changes semantics, so the closure must stay.
 +    Box::new(move |x| f()(x))
 +}
 +
 +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
 +    f(&mut "Hello".to_owned())
 +}
 +fn test_difference_in_mutability() {
 +    call(|s| s.clone());
 +}
 +
 +struct Bar;
 +impl std::ops::Deref for Bar {
 +    type Target = str;
 +    fn deref(&self) -> &str {
 +        "hi"
 +    }
 +}
 +
 +fn test_deref_with_trait_method() {
 +    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 +}
 +
 +fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
 +    let mut res = Vec::new();
 +    let mut add_to_res = |n| res.push(n);
 +    x.into_iter().for_each(&mut add_to_res);
 +    y.into_iter().for_each(&mut add_to_res);
 +    z.into_iter().for_each(add_to_res);
 +}
 +
 +fn mutable_closure_in_loop() {
 +    let mut value = 0;
 +    let mut closure = |n| value += n;
 +    for _ in 0..5 {
 +        Some(1).map(&mut closure);
 +    }
 +}
 +
 +fn late_bound_lifetimes() {
 +    fn take_asref_path<P: AsRef<Path>>(path: P) {}
 +
 +    fn map_str<F>(thunk: F)
 +    where
 +        F: FnOnce(&str),
 +    {
 +    }
 +
 +    fn map_str_to_path<F>(thunk: F)
 +    where
 +        F: FnOnce(&str) -> &Path,
 +    {
 +    }
 +    map_str(|s| take_asref_path(s));
 +    map_str_to_path(std::convert::AsRef::as_ref);
 +}
 +
 +mod type_param_bound {
 +    trait Trait {
 +        fn fun();
 +    }
 +
 +    fn take<T: 'static>(_: T) {}
 +
 +    fn test<X: Trait>() {
 +        // don't lint, but it's questionable that rust requires a cast
 +        take(|| X::fun());
 +        take(X::fun as fn());
 +    }
 +}
 +
 +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
 +fn arc_fp() {
 +    let rc = std::rc::Rc::new(|| 7);
 +    let arc = std::sync::Arc::new(|n| n + 1);
 +    let ref_arc = &std::sync::Arc::new(|_| 5);
 +
 +    true.then(|| rc());
 +    (0..5).map(|n| arc(n));
 +    Some(4).map(|n| ref_arc(n));
 +}
++
++// #8460 Don't replace closures with params bounded as `ref`
++mod bind_by_ref {
++    struct A;
++    struct B;
++
++    impl From<&A> for B {
++        fn from(A: &A) -> Self {
++            B
++        }
++    }
++
++    fn test() {
++        // should not lint
++        Some(A).map(|a| B::from(&a));
++        // should not lint
++        Some(A).map(|ref a| B::from(a));
++    }
++}
index a759e6eb514b42bacb0254c704fe251902711890,0000000000000000000000000000000000000000..5fdf7fb9771697e2330265458903ac5c12528f94
mode 100644,000000..100644
--- /dev/null
@@@ -1,258 -1,0 +1,277 @@@
 +// run-rustfix
 +
 +#![allow(
 +    unused,
 +    clippy::no_effect,
 +    clippy::redundant_closure_call,
 +    clippy::needless_pass_by_value,
 +    clippy::option_map_unit_fn,
 +    clippy::needless_borrow
 +)]
 +#![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 +
 +use std::path::{Path, PathBuf};
 +
 +macro_rules! mac {
 +    () => {
 +        foobar()
 +    };
 +}
 +
 +macro_rules! closure_mac {
 +    () => {
 +        |n| foo(n)
 +    };
 +}
 +
 +fn main() {
 +    let a = Some(1u8).map(|a| foo(a));
 +    let c = Some(1u8).map(|a| {1+2; foo}(a));
 +    true.then(|| mac!()); // don't lint function in macro expansion
 +    Some(1).map(closure_mac!()); // don't lint closure in macro expansion
 +    let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
 +    let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
 +    all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
 +    unsafe {
 +        Some(1u8).map(|a| unsafe_fn(a)); // unsafe fn
 +    }
 +
 +    // See #815
 +    let e = Some(1u8).map(|a| divergent(a));
 +    let e = Some(1u8).map(|a| generic(a));
 +    let e = Some(1u8).map(generic);
 +    // See #515
 +    let a: Option<Box<dyn (::std::ops::Deref<Target = [i32]>)>> =
 +        Some(vec![1i32, 2]).map(|v| -> Box<dyn (::std::ops::Deref<Target = [i32]>)> { Box::new(v) });
 +
 +    // issue #7224
 +    let _: Option<Vec<u32>> = Some(0).map(|_| vec![]);
 +}
 +
 +trait TestTrait {
 +    fn trait_foo(self) -> bool;
 +    fn trait_foo_ref(&self) -> bool;
 +}
 +
 +struct TestStruct<'a> {
 +    some_ref: &'a i32,
 +}
 +
 +impl<'a> TestStruct<'a> {
 +    fn foo(self) -> bool {
 +        false
 +    }
 +    unsafe fn foo_unsafe(self) -> bool {
 +        true
 +    }
 +}
 +
 +impl<'a> TestTrait for TestStruct<'a> {
 +    fn trait_foo(self) -> bool {
 +        false
 +    }
 +    fn trait_foo_ref(&self) -> bool {
 +        false
 +    }
 +}
 +
 +impl<'a> std::ops::Deref for TestStruct<'a> {
 +    type Target = char;
 +    fn deref(&self) -> &char {
 +        &'a'
 +    }
 +}
 +
 +fn test_redundant_closures_containing_method_calls() {
 +    let i = 10;
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
 +    let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo_ref());
 +    let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
 +    unsafe {
 +        let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo_unsafe());
 +    }
 +    let e = Some("str").map(|s| s.to_string());
 +    let e = Some('a').map(|s| s.to_uppercase());
 +    let e: std::vec::Vec<usize> = vec!['a', 'b', 'c'].iter().map(|c| c.len_utf8()).collect();
 +    let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
 +    let e = Some(PathBuf::new()).as_ref().and_then(|s| s.to_str());
 +    let c = Some(TestStruct { some_ref: &i })
 +        .as_ref()
 +        .map(|c| c.to_ascii_uppercase());
 +
 +    fn test_different_borrow_levels<T>(t: &[&T])
 +    where
 +        T: TestTrait,
 +    {
 +        t.iter().filter(|x| x.trait_foo_ref());
 +        t.iter().map(|x| x.trait_foo_ref());
 +    }
 +}
 +
 +struct Thunk<T>(Box<dyn FnMut() -> T>);
 +
 +impl<T> Thunk<T> {
 +    fn new<F: 'static + FnOnce() -> T>(f: F) -> Thunk<T> {
 +        let mut option = Some(f);
 +        // This should not trigger redundant_closure (#1439)
 +        Thunk(Box::new(move || option.take().unwrap()()))
 +    }
 +
 +    fn unwrap(self) -> T {
 +        let Thunk(mut f) = self;
 +        f()
 +    }
 +}
 +
 +fn foobar() {
 +    let thunk = Thunk::new(|| println!("Hello, world!"));
 +    thunk.unwrap()
 +}
 +
 +fn foo(_: u8) {}
 +
 +fn foo2(_: u8) -> u8 {
 +    1u8
 +}
 +
 +fn all<X, F>(x: &[X], y: &X, f: F) -> bool
 +where
 +    F: Fn(&X, &X) -> bool,
 +{
 +    x.iter().all(|e| f(e, y))
 +}
 +
 +fn below(x: &u8, y: &u8) -> bool {
 +    x < y
 +}
 +
 +unsafe fn unsafe_fn(_: u8) {}
 +
 +fn divergent(_: u8) -> ! {
 +    unimplemented!()
 +}
 +
 +fn generic<T>(_: T) -> u8 {
 +    0
 +}
 +
 +fn passes_fn_mut(mut x: Box<dyn FnMut()>) {
 +    requires_fn_once(|| x());
 +}
 +fn requires_fn_once<T: FnOnce()>(_: T) {}
 +
 +fn test_redundant_closure_with_function_pointer() {
 +    type FnPtrType = fn(u8);
 +    let foo_ptr: FnPtrType = foo;
 +    let a = Some(1u8).map(|a| foo_ptr(a));
 +}
 +
 +fn test_redundant_closure_with_another_closure() {
 +    let closure = |a| println!("{}", a);
 +    let a = Some(1u8).map(|a| closure(a));
 +}
 +
 +fn make_lazy(f: impl Fn() -> fn(u8) -> u8) -> impl Fn(u8) -> u8 {
 +    // Currently f is called when result of make_lazy is called.
 +    // If the closure is removed, f will be called when make_lazy itself is
 +    // called. This changes semantics, so the closure must stay.
 +    Box::new(move |x| f()(x))
 +}
 +
 +fn call<F: FnOnce(&mut String) -> String>(f: F) -> String {
 +    f(&mut "Hello".to_owned())
 +}
 +fn test_difference_in_mutability() {
 +    call(|s| s.clone());
 +}
 +
 +struct Bar;
 +impl std::ops::Deref for Bar {
 +    type Target = str;
 +    fn deref(&self) -> &str {
 +        "hi"
 +    }
 +}
 +
 +fn test_deref_with_trait_method() {
 +    let _ = [Bar].iter().map(|s| s.to_string()).collect::<Vec<_>>();
 +}
 +
 +fn mutable_closure_used_again(x: Vec<i32>, y: Vec<i32>, z: Vec<i32>) {
 +    let mut res = Vec::new();
 +    let mut add_to_res = |n| res.push(n);
 +    x.into_iter().for_each(|x| add_to_res(x));
 +    y.into_iter().for_each(|x| add_to_res(x));
 +    z.into_iter().for_each(|x| add_to_res(x));
 +}
 +
 +fn mutable_closure_in_loop() {
 +    let mut value = 0;
 +    let mut closure = |n| value += n;
 +    for _ in 0..5 {
 +        Some(1).map(|n| closure(n));
 +    }
 +}
 +
 +fn late_bound_lifetimes() {
 +    fn take_asref_path<P: AsRef<Path>>(path: P) {}
 +
 +    fn map_str<F>(thunk: F)
 +    where
 +        F: FnOnce(&str),
 +    {
 +    }
 +
 +    fn map_str_to_path<F>(thunk: F)
 +    where
 +        F: FnOnce(&str) -> &Path,
 +    {
 +    }
 +    map_str(|s| take_asref_path(s));
 +    map_str_to_path(|s| s.as_ref());
 +}
 +
 +mod type_param_bound {
 +    trait Trait {
 +        fn fun();
 +    }
 +
 +    fn take<T: 'static>(_: T) {}
 +
 +    fn test<X: Trait>() {
 +        // don't lint, but it's questionable that rust requires a cast
 +        take(|| X::fun());
 +        take(X::fun as fn());
 +    }
 +}
 +
 +// #8073 Don't replace closure with `Arc<F>` or `Rc<F>`
 +fn arc_fp() {
 +    let rc = std::rc::Rc::new(|| 7);
 +    let arc = std::sync::Arc::new(|n| n + 1);
 +    let ref_arc = &std::sync::Arc::new(|_| 5);
 +
 +    true.then(|| rc());
 +    (0..5).map(|n| arc(n));
 +    Some(4).map(|n| ref_arc(n));
 +}
++
++// #8460 Don't replace closures with params bounded as `ref`
++mod bind_by_ref {
++    struct A;
++    struct B;
++
++    impl From<&A> for B {
++        fn from(A: &A) -> Self {
++            B
++        }
++    }
++
++    fn test() {
++        // should not lint
++        Some(A).map(|a| B::from(&a));
++        // should not lint
++        Some(A).map(|ref a| B::from(a));
++    }
++}
index b45cc849eaec42b5796e35d196f55aee7d312e46,0000000000000000000000000000000000000000..cee9e2372c2271f5a07e5fb30feb72a5bfa9e9d6
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,103 @@@
 +// 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],
++}
++
 +fn main() {
 +    large_enum_variant!();
 +}
index 899f97ce2e1e91ba4f3ce50e9cfb39ce2cd8c0b2,0000000000000000000000000000000000000000..cbf2ac972e2b2102d60318d55b129ee66af73e47
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,131 @@@
- error: aborting due to 7 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: aborting due to 8 previous errors
 +
index c61eb9216643e14159ca5d6fb26b93764b05e264,0000000000000000000000000000000000000000..ddfa1e741ada439e347229fe090e7ad508a9dffe
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,43 @@@
- fn main() {}
 +// run-rustfix
 +
 +#![allow(unused)]
 +#![warn(clippy::match_as_ref)]
 +
 +fn match_as_ref() {
 +    let owned: Option<()> = None;
 +    let borrowed: Option<&()> = owned.as_ref();
 +
 +    let mut mut_owned: Option<()> = None;
 +    let borrow_mut: Option<&mut ()> = mut_owned.as_mut();
 +}
 +
 +mod issue4437 {
 +    use std::{error::Error, fmt, num::ParseIntError};
 +
 +    #[derive(Debug)]
 +    struct E {
 +        source: Option<ParseIntError>,
 +    }
 +
 +    impl Error for E {
 +        fn source(&self) -> Option<&(dyn Error + 'static)> {
 +            self.source.as_ref().map(|x| x as _)
 +        }
 +    }
 +
 +    impl fmt::Display for E {
 +        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +            unimplemented!()
 +        }
 +    }
 +}
 +
++fn main() {
++    // Don't lint
++    let _ = match Some(0) {
++        #[cfg(feature = "foo")]
++        Some(ref x) if *x > 50 => None,
++        Some(ref x) => Some(x),
++        None => None,
++    };
++}
index 2fbd0b255faae6d8c7f18a986620cef94aefdb49,0000000000000000000000000000000000000000..025d475ae13dba80d01d37a0c4f73689fd68d6cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,52 @@@
- fn main() {}
 +// run-rustfix
 +
 +#![allow(unused)]
 +#![warn(clippy::match_as_ref)]
 +
 +fn match_as_ref() {
 +    let owned: Option<()> = None;
 +    let borrowed: Option<&()> = match owned {
 +        None => None,
 +        Some(ref v) => Some(v),
 +    };
 +
 +    let mut mut_owned: Option<()> = None;
 +    let borrow_mut: Option<&mut ()> = match mut_owned {
 +        None => None,
 +        Some(ref mut v) => Some(v),
 +    };
 +}
 +
 +mod issue4437 {
 +    use std::{error::Error, fmt, num::ParseIntError};
 +
 +    #[derive(Debug)]
 +    struct E {
 +        source: Option<ParseIntError>,
 +    }
 +
 +    impl Error for E {
 +        fn source(&self) -> Option<&(dyn Error + 'static)> {
 +            match self.source {
 +                Some(ref s) => Some(s),
 +                None => None,
 +            }
 +        }
 +    }
 +
 +    impl fmt::Display for E {
 +        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +            unimplemented!()
 +        }
 +    }
 +}
 +
++fn main() {
++    // Don't lint
++    let _ = match Some(0) {
++        #[cfg(feature = "foo")]
++        Some(ref x) if *x > 50 => None,
++        Some(ref x) => Some(x),
++        None => None,
++    };
++}
index 9ed55ca7ae7f9cef1bc2eb858a853d7a9a33d351,0000000000000000000000000000000000000000..bcc999a49428daf281a2317026c5ef83da37a2b5
mode 100644,000000..100644
--- /dev/null
@@@ -1,55 -1,0 +1,63 @@@
 +#![deny(clippy::match_bool)]
 +
 +fn match_bool() {
 +    let test: bool = true;
 +
 +    match test {
 +        true => 0,
 +        false => 42,
 +    };
 +
 +    let option = 1;
 +    match option == 1 {
 +        true => 1,
 +        false => 0,
 +    };
 +
 +    match test {
 +        true => (),
 +        false => {
 +            println!("Noooo!");
 +        },
 +    };
 +
 +    match test {
 +        false => {
 +            println!("Noooo!");
 +        },
 +        _ => (),
 +    };
 +
 +    match test && test {
 +        false => {
 +            println!("Noooo!");
 +        },
 +        _ => (),
 +    };
 +
 +    match test {
 +        false => {
 +            println!("Noooo!");
 +        },
 +        true => {
 +            println!("Yes!");
 +        },
 +    };
 +
 +    // Not linted
 +    match option {
 +        1..=10 => 1,
 +        11..=20 => 2,
 +        _ => 3,
 +    };
++
++    // Don't lint
++    let _ = match test {
++        #[cfg(feature = "foo")]
++        true if option == 5 => 10,
++        true => 0,
++        false => 1,
++    };
 +}
 +
 +fn main() {}
index c611f76bf96055704ebb37927cf3ec16ae2765d4,0000000000000000000000000000000000000000..36f233f33460720759355d78d1c0cf26623d7713
mode 100644,000000..100644
--- /dev/null
@@@ -1,149 -1,0 +1,164 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = matches!(x, Some(0));
 +
 +    // Lint
 +    let _w = matches!(x, Some(_));
 +
 +    // Turn into is_none
 +    let _z = x.is_none();
 +
 +    // Lint
 +    let _zz = !matches!(x, Some(r) if r == 0);
 +
 +    // Lint
 +    let _zzz = matches!(x, Some(5));
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        let _ans = !matches!(x, E::B(_) | E::C);
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = matches!(z, Some(3));
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = matches!(&z, Some(3));
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = matches!(&z, AnEnum::X);
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
++
++    {
++        enum E {
++            A,
++            B,
++            C,
++        }
++
++        let _ = match E::A {
++            E::B => true,
++            #[cfg(feature = "foo")]
++            E::A => true,
++            _ => false,
++        };
++    }
 +}
index 2deeb84e74138151920ca91279cef789ebad2e79,0000000000000000000000000000000000000000..750f69fa5088f6fa45898030bfca0d56ee486a81
mode 100644,000000..100644
--- /dev/null
@@@ -1,184 -1,0 +1,199 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = match x {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +
 +    // Lint
 +    let _w = match x {
 +        Some(_) => true,
 +        _ => false,
 +    };
 +
 +    // Turn into is_none
 +    let _z = match x {
 +        Some(_) => false,
 +        None => true,
 +    };
 +
 +    // Lint
 +    let _zz = match x {
 +        Some(r) if r == 0 => false,
 +        _ => true,
 +    };
 +
 +    // Lint
 +    let _zzz = if let Some(5) = x { true } else { false };
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = match &z {
 +                AnEnum::X => true,
 +                _ => false,
 +            };
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
++
++    {
++        enum E {
++            A,
++            B,
++            C,
++        }
++
++        let _ = match E::A {
++            E::B => true,
++            #[cfg(feature = "foo")]
++            E::A => true,
++            _ => false,
++        };
++    }
 +}
index da4e3020d5b83d6f20c9788b67d7901fa7c95a51,0000000000000000000000000000000000000000..67e1d518483c2cf6b61ca152b368506619cc9008
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,177 @@@
- fn main() {}
 +#![warn(clippy::match_same_arms)]
 +#![allow(clippy::blacklisted_name)]
 +
 +fn bar<T>(_: T) {}
 +fn foo() -> bool {
 +    unimplemented!()
 +}
 +
 +fn match_same_arms() {
 +    let _ = match 42 {
 +        42 => {
 +            foo();
 +            let mut a = 42 + [23].len() as i32;
 +            if true {
 +                a += 7;
 +            }
 +            a = -31 - a;
 +            a
 +        },
 +        _ => {
 +            //~ ERROR match arms have same body
 +            foo();
 +            let mut a = 42 + [23].len() as i32;
 +            if true {
 +                a += 7;
 +            }
 +            a = -31 - a;
 +            a
 +        },
 +    };
 +
 +    let _ = match 42 {
 +        42 => foo(),
 +        51 => foo(), //~ ERROR match arms have same body
 +        _ => true,
 +    };
 +
 +    let _ = match Some(42) {
 +        Some(_) => 24,
 +        None => 24, //~ ERROR match arms have same body
 +    };
 +
 +    let _ = match Some(42) {
 +        Some(foo) => 24,
 +        None => 24,
 +    };
 +
 +    let _ = match Some(42) {
 +        Some(42) => 24,
 +        Some(a) => 24, // bindings are different
 +        None => 0,
 +    };
 +
 +    let _ = match Some(42) {
 +        Some(a) if a > 0 => 24,
 +        Some(a) => 24, // one arm has a guard
 +        None => 0,
 +    };
 +
 +    match (Some(42), Some(42)) {
 +        (Some(a), None) => bar(a),
 +        (None, Some(a)) => bar(a), //~ ERROR match arms have same body
 +        _ => (),
 +    }
 +
 +    match (Some(42), Some(42)) {
 +        (Some(a), ..) => bar(a),
 +        (.., Some(a)) => bar(a), //~ ERROR match arms have same body
 +        _ => (),
 +    }
 +
 +    let _ = match Some(()) {
 +        Some(()) => 0.0,
 +        None => -0.0,
 +    };
 +
 +    match (Some(42), Some("")) {
 +        (Some(a), None) => bar(a),
 +        (None, Some(a)) => bar(a), // bindings have different types
 +        _ => (),
 +    }
 +
 +    let x: Result<i32, &str> = Ok(3);
 +
 +    // No warning because of the guard.
 +    match x {
 +        Ok(x) if x * x == 64 => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => println!("err"),
 +    }
 +
 +    // This used to be a false positive; see issue #1996.
 +    match x {
 +        Ok(3) => println!("ok"),
 +        Ok(x) if x * x == 64 => println!("ok 64"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => println!("err"),
 +    }
 +
 +    match (x, Some(1i32)) {
 +        (Ok(x), Some(_)) => println!("ok {}", x),
 +        (Ok(_), Some(x)) => println!("ok {}", x),
 +        _ => println!("err"),
 +    }
 +
 +    // No warning; different types for `x`.
 +    match (x, Some(1.0f64)) {
 +        (Ok(x), Some(_)) => println!("ok {}", x),
 +        (Ok(_), Some(x)) => println!("ok {}", x),
 +        _ => println!("err"),
 +    }
 +
 +    // False negative #2251.
 +    match x {
 +        Ok(_tmp) => println!("ok"),
 +        Ok(3) => println!("ok"),
 +        Ok(_) => println!("ok"),
 +        Err(_) => {
 +            unreachable!();
 +        },
 +    }
 +
 +    // False positive #1390
 +    macro_rules! empty {
 +        ($e:expr) => {};
 +    }
 +    match 0 {
 +        0 => {
 +            empty!(0);
 +        },
 +        1 => {
 +            empty!(1);
 +        },
 +        x => {
 +            empty!(x);
 +        },
 +    };
 +
 +    // still lint if the tokens are the same
 +    match 0 {
 +        0 => {
 +            empty!(0);
 +        },
 +        1 => {
 +            empty!(0);
 +        },
 +        x => {
 +            empty!(x);
 +        },
 +    }
 +
 +    match_expr_like_matches_macro_priority();
 +}
 +
 +fn match_expr_like_matches_macro_priority() {
 +    enum E {
 +        A,
 +        B,
 +        C,
 +    }
 +    let x = E::A;
 +    let _ans = match x {
 +        E::A => false,
 +        E::B => false,
 +        _ => true,
 +    };
 +}
 +
++fn main() {
++    let _ = match Some(0) {
++        Some(0) => 0,
++        Some(1) => 1,
++        #[cfg(feature = "foo")]
++        Some(2) => 2,
++        _ => 1,
++    };
++}
index b4ec525ada09a763059d8837fc06d572051f1a4b,0000000000000000000000000000000000000000..b8dc8179f7d7d1c841a5df8475ebed31a52f0014
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,113 @@@
-     // False negative
 +// run-rustfix
 +
 +#![warn(clippy::match_single_binding)]
 +#![allow(unused_variables, clippy::toplevel_ref_arg)]
 +
 +struct Point {
 +    x: i32,
 +    y: i32,
 +}
 +
 +fn coords() -> Point {
 +    Point { x: 1, y: 2 }
 +}
 +
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            _ => println!("whatever"),
 +        }
 +    };
 +}
 +
 +fn main() {
 +    let a = 1;
 +    let b = 2;
 +    let c = 3;
 +    // Lint
 +    let (x, y, z) = (a, b, c);
 +    {
 +        println!("{} {} {}", x, y, z);
 +    }
 +    // Lint
 +    let (x, y, z) = (a, b, c);
 +    println!("{} {} {}", x, y, z);
 +    // Ok
 +    foo!(a);
 +    // Ok
 +    match a {
 +        2 => println!("2"),
 +        _ => println!("Not 2"),
 +    }
 +    // Ok
 +    let d = Some(5);
 +    match d {
 +        Some(d) => println!("{}", d),
 +        _ => println!("None"),
 +    }
 +    // Lint
 +    println!("whatever");
 +    // Lint
 +    {
 +        let x = 29;
 +        println!("x has a value of {}", x);
 +    }
 +    // Lint
 +    {
 +        let e = 5 * a;
 +        if e >= 5 {
 +            println!("e is superior to 5");
 +        }
 +    }
 +    // Lint
 +    let p = Point { x: 0, y: 7 };
 +    let Point { x, y } = p;
 +    println!("Coords: ({}, {})", x, y);
 +    // Lint
 +    let Point { x: x1, y: y1 } = p;
 +    println!("Coords: ({}, {})", x1, y1);
 +    // Lint
 +    let x = 5;
 +    let ref r = x;
 +    println!("Got a reference to {}", r);
 +    // Lint
 +    let mut x = 5;
 +    let ref mut mr = x;
 +    println!("Got a mutable reference to {}", mr);
 +    // Lint
 +    let Point { x, y } = coords();
 +    let product = x * y;
 +    // Lint
 +    let v = vec![Some(1), Some(2), Some(3), Some(4)];
 +    #[allow(clippy::let_and_return)]
 +    let _ = v
 +        .iter()
 +        .map(|i| {
 +            let unwrapped = i.unwrap();
 +            unwrapped
 +        })
 +        .collect::<Vec<u8>>();
 +    // Ok
 +    let x = 1;
 +    match x {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Disabled branch"),
 +        _ => println!("Enabled branch"),
 +    }
 +
 +    // Ok
 +    let x = 1;
 +    let y = 1;
 +    match match y {
 +        0 => 1,
 +        _ => 2,
 +    } {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Array index start"),
 +        _ => println!("Not an array index start"),
 +    }
-     match x {
-         // =>
-         _ => println!("Not an array index start"),
-     }
++
++    // Lint
 +    let x = 1;
++    println!("Not an array index start");
 +}
index e04c4018b98ddbb52e282768d113bb7e25ab4f63,0000000000000000000000000000000000000000..fe63dcd63f2bb97d5fcd69f62f3b4d74f5a0c037
mode 100644,000000..100644
--- /dev/null
@@@ -1,127 -1,0 +1,128 @@@
-     // False negative
 +// run-rustfix
 +
 +#![warn(clippy::match_single_binding)]
 +#![allow(unused_variables, clippy::toplevel_ref_arg)]
 +
 +struct Point {
 +    x: i32,
 +    y: i32,
 +}
 +
 +fn coords() -> Point {
 +    Point { x: 1, y: 2 }
 +}
 +
 +macro_rules! foo {
 +    ($param:expr) => {
 +        match $param {
 +            _ => println!("whatever"),
 +        }
 +    };
 +}
 +
 +fn main() {
 +    let a = 1;
 +    let b = 2;
 +    let c = 3;
 +    // Lint
 +    match (a, b, c) {
 +        (x, y, z) => {
 +            println!("{} {} {}", x, y, z);
 +        },
 +    }
 +    // Lint
 +    match (a, b, c) {
 +        (x, y, z) => println!("{} {} {}", x, y, z),
 +    }
 +    // Ok
 +    foo!(a);
 +    // Ok
 +    match a {
 +        2 => println!("2"),
 +        _ => println!("Not 2"),
 +    }
 +    // Ok
 +    let d = Some(5);
 +    match d {
 +        Some(d) => println!("{}", d),
 +        _ => println!("None"),
 +    }
 +    // Lint
 +    match a {
 +        _ => println!("whatever"),
 +    }
 +    // Lint
 +    match a {
 +        _ => {
 +            let x = 29;
 +            println!("x has a value of {}", x);
 +        },
 +    }
 +    // Lint
 +    match a {
 +        _ => {
 +            let e = 5 * a;
 +            if e >= 5 {
 +                println!("e is superior to 5");
 +            }
 +        },
 +    }
 +    // Lint
 +    let p = Point { x: 0, y: 7 };
 +    match p {
 +        Point { x, y } => println!("Coords: ({}, {})", x, y),
 +    }
 +    // Lint
 +    match p {
 +        Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
 +    }
 +    // Lint
 +    let x = 5;
 +    match x {
 +        ref r => println!("Got a reference to {}", r),
 +    }
 +    // Lint
 +    let mut x = 5;
 +    match x {
 +        ref mut mr => println!("Got a mutable reference to {}", mr),
 +    }
 +    // Lint
 +    let product = match coords() {
 +        Point { x, y } => x * y,
 +    };
 +    // Lint
 +    let v = vec![Some(1), Some(2), Some(3), Some(4)];
 +    #[allow(clippy::let_and_return)]
 +    let _ = v
 +        .iter()
 +        .map(|i| match i.unwrap() {
 +            unwrapped => unwrapped,
 +        })
 +        .collect::<Vec<u8>>();
 +    // Ok
 +    let x = 1;
 +    match x {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Disabled branch"),
 +        _ => println!("Enabled branch"),
 +    }
 +
 +    // Ok
 +    let x = 1;
 +    let y = 1;
 +    match match y {
 +        0 => 1,
 +        _ => 2,
 +    } {
 +        #[cfg(disabled_feature)]
 +        0 => println!("Array index start"),
 +        _ => println!("Not an array index start"),
 +    }
++
++    // Lint
 +    let x = 1;
 +    match x {
 +        // =>
 +        _ => println!("Not an array index start"),
 +    }
 +}
index 291fa77dc2ee1e78186bcb933163d35e8fddd8e9,0000000000000000000000000000000000000000..d939291f53c40758821783da7bdbc38255ff8542
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,180 @@@
- error: aborting due to 11 previous errors
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:28:5
 +   |
 +LL | /     match (a, b, c) {
 +LL | |         (x, y, z) => {
 +LL | |             println!("{} {} {}", x, y, z);
 +LL | |         },
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::match-single-binding` implied by `-D warnings`
 +help: consider using `let` statement
 +   |
 +LL ~     let (x, y, z) = (a, b, c);
 +LL +     {
 +LL +         println!("{} {} {}", x, y, z);
 +LL +     }
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:34:5
 +   |
 +LL | /     match (a, b, c) {
 +LL | |         (x, y, z) => println!("{} {} {}", x, y, z),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~     let (x, y, z) = (a, b, c);
 +LL +     println!("{} {} {}", x, y, z);
 +   |
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:51:5
 +   |
 +LL | /     match a {
 +LL | |         _ => println!("whatever"),
 +LL | |     }
 +   | |_____^ help: consider using the match body instead: `println!("whatever");`
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:55:5
 +   |
 +LL | /     match a {
 +LL | |         _ => {
 +LL | |             let x = 29;
 +LL | |             println!("x has a value of {}", x);
 +LL | |         },
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using the match body instead
 +   |
 +LL ~     {
 +LL +         let x = 29;
 +LL +         println!("x has a value of {}", x);
 +LL +     }
 +   |
 +
 +error: this match could be replaced by its body itself
 +  --> $DIR/match_single_binding.rs:62:5
 +   |
 +LL | /     match a {
 +LL | |         _ => {
 +LL | |             let e = 5 * a;
 +LL | |             if e >= 5 {
 +...  |
 +LL | |         },
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using the match body instead
 +   |
 +LL ~     {
 +LL +         let e = 5 * a;
 +LL +         if e >= 5 {
 +LL +             println!("e is superior to 5");
 +LL +         }
 +LL +     }
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:72:5
 +   |
 +LL | /     match p {
 +LL | |         Point { x, y } => println!("Coords: ({}, {})", x, y),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~     let Point { x, y } = p;
 +LL +     println!("Coords: ({}, {})", x, y);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:76:5
 +   |
 +LL | /     match p {
 +LL | |         Point { x: x1, y: y1 } => println!("Coords: ({}, {})", x1, y1),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~     let Point { x: x1, y: y1 } = p;
 +LL +     println!("Coords: ({}, {})", x1, y1);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:81:5
 +   |
 +LL | /     match x {
 +LL | |         ref r => println!("Got a reference to {}", r),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~     let ref r = x;
 +LL +     println!("Got a reference to {}", r);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:86:5
 +   |
 +LL | /     match x {
 +LL | |         ref mut mr => println!("Got a mutable reference to {}", mr),
 +LL | |     }
 +   | |_____^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~     let ref mut mr = x;
 +LL +     println!("Got a mutable reference to {}", mr);
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:90:5
 +   |
 +LL | /     let product = match coords() {
 +LL | |         Point { x, y } => x * y,
 +LL | |     };
 +   | |______^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~     let Point { x, y } = coords();
 +LL +     let product = x * y;
 +   |
 +
 +error: this match could be written as a `let` statement
 +  --> $DIR/match_single_binding.rs:98:18
 +   |
 +LL |           .map(|i| match i.unwrap() {
 +   |  __________________^
 +LL | |             unwrapped => unwrapped,
 +LL | |         })
 +   | |_________^
 +   |
 +help: consider using `let` statement
 +   |
 +LL ~         .map(|i| {
 +LL +             let unwrapped = i.unwrap();
 +LL +             unwrapped
 +LL ~         })
 +   |
 +
++error: this match could be replaced by its body itself
++  --> $DIR/match_single_binding.rs:124:5
++   |
++LL | /     match x {
++LL | |         // =>
++LL | |         _ => println!("Not an array index start"),
++LL | |     }
++   | |_____^ help: consider using the match body instead: `println!("Not an array index start");`
++
++error: aborting due to 12 previous errors
 +
index 4b2e7444dcf63251932ba94ee3c4ace4e59be4dc,0000000000000000000000000000000000000000..e94f99c95f481145edad86d5c112673dcd72d082
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,214 @@@
 +#![allow(dead_code, clippy::missing_safety_doc)]
 +#![warn(clippy::new_without_default)]
 +
 +pub struct Foo;
 +
 +impl Foo {
 +    pub fn new() -> Foo {
 +        Foo
 +    }
 +}
 +
 +pub struct Bar;
 +
 +impl Bar {
 +    pub fn new() -> Self {
 +        Bar
 +    }
 +}
 +
 +pub struct Ok;
 +
 +impl Ok {
 +    pub fn new() -> Self {
 +        Ok
 +    }
 +}
 +
 +impl Default for Ok {
 +    fn default() -> Self {
 +        Ok
 +    }
 +}
 +
 +pub struct Params;
 +
 +impl Params {
 +    pub fn new(_: u32) -> Self {
 +        Params
 +    }
 +}
 +
 +pub struct GenericsOk<T> {
 +    bar: T,
 +}
 +
 +impl<U> Default for GenericsOk<U> {
 +    fn default() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +impl<'c, V> GenericsOk<V> {
 +    pub fn new() -> GenericsOk<V> {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct LtOk<'a> {
 +    foo: &'a bool,
 +}
 +
 +impl<'b> Default for LtOk<'b> {
 +    fn default() -> Self {
 +        unimplemented!();
 +    }
 +}
 +
 +impl<'c> LtOk<'c> {
 +    pub fn new() -> LtOk<'c> {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct LtKo<'a> {
 +    foo: &'a bool,
 +}
 +
 +impl<'c> LtKo<'c> {
 +    pub fn new() -> LtKo<'c> {
 +        unimplemented!()
 +    }
 +    // FIXME: that suggestion is missing lifetimes
 +}
 +
 +struct Private;
 +
 +impl Private {
 +    fn new() -> Private {
 +        unimplemented!()
 +    } // We don't lint private items
 +}
 +
++struct PrivateStruct;
++
++impl PrivateStruct {
++    pub fn new() -> PrivateStruct {
++        unimplemented!()
++    } // We don't lint public items on private structs
++}
++
++pub struct PrivateItem;
++
++impl PrivateItem {
++    fn new() -> PrivateItem {
++        unimplemented!()
++    } // We don't lint private items on public structs
++}
++
 +struct Const;
 +
 +impl Const {
 +    pub const fn new() -> Const {
 +        Const
 +    } // const fns can't be implemented via Default
 +}
 +
 +pub struct IgnoreGenericNew;
 +
 +impl IgnoreGenericNew {
 +    pub fn new<T>() -> Self {
 +        IgnoreGenericNew
 +    } // the derived Default does not make sense here as the result depends on T
 +}
 +
 +pub trait TraitWithNew: Sized {
 +    fn new() -> Self {
 +        panic!()
 +    }
 +}
 +
 +pub struct IgnoreUnsafeNew;
 +
 +impl IgnoreUnsafeNew {
 +    pub unsafe fn new() -> Self {
 +        IgnoreUnsafeNew
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct OptionRefWrapper<'a, T>(Option<&'a T>);
 +
 +impl<'a, T> OptionRefWrapper<'a, T> {
 +    pub fn new() -> Self {
 +        OptionRefWrapper(None)
 +    }
 +}
 +
 +pub struct Allow(Foo);
 +
 +impl Allow {
 +    #[allow(clippy::new_without_default)]
 +    pub fn new() -> Self {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct AllowDerive;
 +
 +impl AllowDerive {
 +    #[allow(clippy::new_without_default)]
 +    pub fn new() -> Self {
 +        unimplemented!()
 +    }
 +}
 +
 +pub struct NewNotEqualToDerive {
 +    foo: i32,
 +}
 +
 +impl NewNotEqualToDerive {
 +    // This `new` implementation is not equal to a derived `Default`, so do not suggest deriving.
 +    pub fn new() -> Self {
 +        NewNotEqualToDerive { foo: 1 }
 +    }
 +}
 +
 +// see #6933
 +pub struct FooGenerics<T>(std::marker::PhantomData<T>);
 +impl<T> FooGenerics<T> {
 +    pub fn new() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +
 +pub struct BarGenerics<T>(std::marker::PhantomData<T>);
 +impl<T: Copy> BarGenerics<T> {
 +    pub fn new() -> Self {
 +        Self(Default::default())
 +    }
 +}
 +
 +pub mod issue7220 {
 +    pub struct Foo<T> {
 +        _bar: *mut T,
 +    }
 +
 +    impl<T> Foo<T> {
 +        pub fn new() -> Self {
 +            todo!()
 +        }
 +    }
 +}
 +
++// see issue #8152
++// This should not create any lints
++pub struct DocHidden;
++impl DocHidden {
++    #[doc(hidden)]
++    pub fn new() -> Self {
++        DocHidden
++    }
++}
++
 +fn main() {}
index 14ddb66f2324c18b6ddd10e5e9655015e5fd72a0,0000000000000000000000000000000000000000..19572dfe8b0753bdb597ca3959314eede3bd0685
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,123 @@@
-   --> $DIR/new_without_default.rs:156:5
 +error: you should consider adding a `Default` implementation for `Foo`
 +  --> $DIR/new_without_default.rs:7:5
 +   |
 +LL | /     pub fn new() -> Foo {
 +LL | |         Foo
 +LL | |     }
 +   | |_____^
 +   |
 +   = note: `-D clippy::new-without-default` implied by `-D warnings`
 +help: try adding this
 +   |
 +LL + impl Default for Foo {
 +LL +     fn default() -> Self {
 +LL +         Self::new()
 +LL +     }
 +LL + }
 +   |
 +
 +error: you should consider adding a `Default` implementation for `Bar`
 +  --> $DIR/new_without_default.rs:15:5
 +   |
 +LL | /     pub fn new() -> Self {
 +LL | |         Bar
 +LL | |     }
 +   | |_____^
 +   |
 +help: try adding this
 +   |
 +LL + impl Default for Bar {
 +LL +     fn default() -> Self {
 +LL +         Self::new()
 +LL +     }
 +LL + }
 +   |
 +
 +error: you should consider adding a `Default` implementation for `LtKo<'c>`
 +  --> $DIR/new_without_default.rs:79:5
 +   |
 +LL | /     pub fn new() -> LtKo<'c> {
 +LL | |         unimplemented!()
 +LL | |     }
 +   | |_____^
 +   |
 +help: try adding this
 +   |
 +LL + impl<'c> Default for LtKo<'c> {
 +LL +     fn default() -> Self {
 +LL +         Self::new()
 +LL +     }
 +LL + }
 +   |
 +
 +error: you should consider adding a `Default` implementation for `NewNotEqualToDerive`
-   --> $DIR/new_without_default.rs:164:5
++  --> $DIR/new_without_default.rs:172:5
 +   |
 +LL | /     pub fn new() -> Self {
 +LL | |         NewNotEqualToDerive { foo: 1 }
 +LL | |     }
 +   | |_____^
 +   |
 +help: try adding this
 +   |
 +LL + impl Default for NewNotEqualToDerive {
 +LL +     fn default() -> Self {
 +LL +         Self::new()
 +LL +     }
 +LL + }
 +   |
 +
 +error: you should consider adding a `Default` implementation for `FooGenerics<T>`
-   --> $DIR/new_without_default.rs:171:5
++  --> $DIR/new_without_default.rs:180:5
 +   |
 +LL | /     pub fn new() -> Self {
 +LL | |         Self(Default::default())
 +LL | |     }
 +   | |_____^
 +   |
 +help: try adding this
 +   |
 +LL + impl<T> Default for FooGenerics<T> {
 +LL +     fn default() -> Self {
 +LL +         Self::new()
 +LL +     }
 +LL + }
 +   |
 +
 +error: you should consider adding a `Default` implementation for `BarGenerics<T>`
-   --> $DIR/new_without_default.rs:182:9
++  --> $DIR/new_without_default.rs:187:5
 +   |
 +LL | /     pub fn new() -> Self {
 +LL | |         Self(Default::default())
 +LL | |     }
 +   | |_____^
 +   |
 +help: try adding this
 +   |
 +LL + impl<T: Copy> Default for BarGenerics<T> {
 +LL +     fn default() -> Self {
 +LL +         Self::new()
 +LL +     }
 +LL + }
 +   |
 +
 +error: you should consider adding a `Default` implementation for `Foo<T>`
++  --> $DIR/new_without_default.rs:198:9
 +   |
 +LL | /         pub fn new() -> Self {
 +LL | |             todo!()
 +LL | |         }
 +   | |_________^
 +   |
 +help: try adding this
 +   |
 +LL ~     impl<T> Default for Foo<T> {
 +LL +         fn default() -> Self {
 +LL +             Self::new()
 +LL +         }
 +LL +     }
 +LL + 
 + ...
 +
 +error: aborting due to 7 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64e8868660980fb1c62a20456e7f438d2d9e6225
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++#![allow(unused, clippy::print_literal, clippy::write_literal)]
++#![warn(clippy::print_in_format_impl)]
++use std::fmt::{Debug, Display, Error, Formatter};
++
++macro_rules! indirect {
++    () => {{ println!() }};
++}
++
++macro_rules! nested {
++    ($($tt:tt)*) => {
++        $($tt)*
++    };
++}
++
++struct Foo;
++impl Debug for Foo {
++    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
++        static WORKS_WITH_NESTED_ITEMS: bool = true;
++
++        print!("{}", 1);
++        println!("{}", 2);
++        eprint!("{}", 3);
++        eprintln!("{}", 4);
++        nested! {
++            println!("nested");
++        };
++
++        write!(f, "{}", 5);
++        writeln!(f, "{}", 6);
++        indirect!();
++
++        Ok(())
++    }
++}
++
++impl Display for Foo {
++    fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
++        print!("Display");
++        write!(f, "Display");
++
++        Ok(())
++    }
++}
++
++struct UnnamedFormatter;
++impl Debug for UnnamedFormatter {
++    fn fmt(&self, _: &mut Formatter) -> Result<(), Error> {
++        println!("UnnamedFormatter");
++        Ok(())
++    }
++}
++
++fn main() {
++    print!("outside fmt");
++    println!("outside fmt");
++    eprint!("outside fmt");
++    eprintln!("outside fmt");
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..63b7179bca7dcf715783ca67a8a1d7fb85d0cab0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: use of `print!` in `Debug` impl
++  --> $DIR/print_in_format_impl.rs:20:9
++   |
++LL |         print!("{}", 1);
++   |         ^^^^^^^^^^^^^^^ help: replace with: `write!(f, ..)`
++   |
++   = note: `-D clippy::print-in-format-impl` implied by `-D warnings`
++
++error: use of `println!` in `Debug` impl
++  --> $DIR/print_in_format_impl.rs:21:9
++   |
++LL |         println!("{}", 2);
++   |         ^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(f, ..)`
++
++error: use of `eprint!` in `Debug` impl
++  --> $DIR/print_in_format_impl.rs:22:9
++   |
++LL |         eprint!("{}", 3);
++   |         ^^^^^^^^^^^^^^^^ help: replace with: `write!(f, ..)`
++
++error: use of `eprintln!` in `Debug` impl
++  --> $DIR/print_in_format_impl.rs:23:9
++   |
++LL |         eprintln!("{}", 4);
++   |         ^^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(f, ..)`
++
++error: use of `println!` in `Debug` impl
++  --> $DIR/print_in_format_impl.rs:25:13
++   |
++LL |             println!("nested");
++   |             ^^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(f, ..)`
++
++error: use of `print!` in `Display` impl
++  --> $DIR/print_in_format_impl.rs:38:9
++   |
++LL |         print!("Display");
++   |         ^^^^^^^^^^^^^^^^^ help: replace with: `write!(f, ..)`
++
++error: use of `println!` in `Debug` impl
++  --> $DIR/print_in_format_impl.rs:48:9
++   |
++LL |         println!("UnnamedFormatter");
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `writeln!(..)`
++
++error: aborting due to 7 previous errors
++
index 00b99da2631c630aa1ef6e227e50c4c7f678f027,0000000000000000000000000000000000000000..97990fedd51f3e851b065f396233205e3bb75da9
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,196 @@@
 +#![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
 +#![warn(clippy::ptr_arg)]
 +
 +use std::borrow::Cow;
 +use std::path::PathBuf;
 +
 +fn do_vec(x: &Vec<i64>) {
 +    //Nothing here
 +}
 +
 +fn do_vec_mut(x: &mut Vec<i64>) {
 +    //Nothing here
 +}
 +
 +fn do_str(x: &String) {
 +    //Nothing here either
 +}
 +
 +fn do_str_mut(x: &mut String) {
 +    //Nothing here either
 +}
 +
 +fn do_path(x: &PathBuf) {
 +    //Nothing here either
 +}
 +
 +fn do_path_mut(x: &mut PathBuf) {
 +    //Nothing here either
 +}
 +
 +fn main() {}
 +
 +trait Foo {
 +    type Item;
 +    fn do_vec(x: &Vec<i64>);
 +    fn do_item(x: &Self::Item);
 +}
 +
 +struct Bar;
 +
 +// no error, in trait impl (#425)
 +impl Foo for Bar {
 +    type Item = Vec<u8>;
 +    fn do_vec(x: &Vec<i64>) {}
 +    fn do_item(x: &Vec<u8>) {}
 +}
 +
 +fn cloned(x: &Vec<u8>) -> Vec<u8> {
 +    let e = x.clone();
 +    let f = e.clone(); // OK
 +    let g = x;
 +    let h = g.clone();
 +    let i = (e).clone();
 +    x.clone()
 +}
 +
 +fn str_cloned(x: &String) -> String {
 +    let a = x.clone();
 +    let b = x.clone();
 +    let c = b.clone();
 +    let d = a.clone().clone().clone();
 +    x.clone()
 +}
 +
 +fn path_cloned(x: &PathBuf) -> PathBuf {
 +    let a = x.clone();
 +    let b = x.clone();
 +    let c = b.clone();
 +    let d = a.clone().clone().clone();
 +    x.clone()
 +}
 +
 +fn false_positive_capacity(x: &Vec<u8>, y: &String) {
 +    let a = x.capacity();
 +    let b = y.clone();
 +    let c = y.as_str();
 +}
 +
 +fn false_positive_capacity_too(x: &String) -> String {
 +    if x.capacity() > 1024 {
 +        panic!("Too large!");
 +    }
 +    x.clone()
 +}
 +
 +#[allow(dead_code)]
 +fn test_cow_with_ref(c: &Cow<[i32]>) {}
 +
 +fn test_cow(c: Cow<[i32]>) {
 +    let _c = c;
 +}
 +
 +trait Foo2 {
 +    fn do_string(&self);
 +}
 +
 +// no error for &self references where self is of type String (#2293)
 +impl Foo2 for String {
 +    fn do_string(&self) {}
 +}
 +
 +// Check that the allow attribute on parameters is honored
 +mod issue_5644 {
 +    use std::borrow::Cow;
 +    use std::path::PathBuf;
 +
 +    fn allowed(
 +        #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
 +        #[allow(clippy::ptr_arg)] _s: &String,
 +        #[allow(clippy::ptr_arg)] _p: &PathBuf,
 +        #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
 +    ) {
 +    }
 +
 +    struct S {}
 +    impl S {
 +        fn allowed(
 +            #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
 +            #[allow(clippy::ptr_arg)] _s: &String,
 +            #[allow(clippy::ptr_arg)] _p: &PathBuf,
 +            #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
 +        ) {
 +        }
 +    }
 +
 +    trait T {
 +        fn allowed(
 +            #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
 +            #[allow(clippy::ptr_arg)] _s: &String,
 +            #[allow(clippy::ptr_arg)] _p: &PathBuf,
 +            #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
 +        ) {
 +        }
 +    }
 +}
 +
 +mod issue6509 {
 +    use std::path::PathBuf;
 +
 +    fn foo_vec(vec: &Vec<u8>) {
 +        let _ = vec.clone().pop();
 +        let _ = vec.clone().clone();
 +    }
 +
 +    fn foo_path(path: &PathBuf) {
 +        let _ = path.clone().pop();
 +        let _ = path.clone().clone();
 +    }
 +
 +    fn foo_str(str: &PathBuf) {
 +        let _ = str.clone().pop();
 +        let _ = str.clone().clone();
 +    }
 +}
 +
 +fn mut_vec_slice_methods(v: &mut Vec<u32>) {
 +    v.copy_within(1..5, 10);
 +}
 +
 +fn mut_vec_vec_methods(v: &mut Vec<u32>) {
 +    v.clear();
 +}
 +
 +fn vec_contains(v: &Vec<u32>) -> bool {
 +    [vec![], vec![0]].as_slice().contains(v)
 +}
 +
 +fn fn_requires_vec(v: &Vec<u32>) -> bool {
 +    vec_contains(v)
 +}
 +
 +fn impl_fn_requires_vec(v: &Vec<u32>, f: impl Fn(&Vec<u32>)) {
 +    f(v);
 +}
 +
 +fn dyn_fn_requires_vec(v: &Vec<u32>, f: &dyn Fn(&Vec<u32>)) {
 +    f(v);
 +}
 +
 +// No error for types behind an alias (#7699)
 +type A = Vec<u8>;
 +fn aliased(a: &A) {}
 +
 +// Issue #8366
 +pub trait Trait {
 +    fn f(v: &mut Vec<i32>);
 +    fn f2(v: &mut Vec<i32>) {}
 +}
++
++// Issue #8463
++fn two_vecs(a: &mut Vec<u32>, b: &mut Vec<u32>) {
++    a.push(0);
++    a.push(0);
++    a.push(0);
++    b.push(1);
++}
index 8346a9454f4eee45040c9bcae8905de7cd64c494,0000000000000000000000000000000000000000..bea6be66a8e025ef2f4468b5bb41e556c4c4614c
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,65 @@@
 +// run-rustfix
++// aux-build:macro_rules.rs
 +
 +#![warn(clippy::ptr_as_ptr)]
 +#![feature(custom_inner_attributes)]
 +
++extern crate macro_rules;
++
++macro_rules! cast_it {
++    ($ptr: ident) => {
++        $ptr.cast::<i32>()
++    };
++}
++
 +fn main() {
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr.cast::<i32>();
 +    let _ = mut_ptr.cast::<i32>();
 +
 +    // Make sure the lint can handle the difference in their operator precedences.
 +    unsafe {
 +        let ptr_ptr: *const *const u32 = &ptr;
 +        let _ = (*ptr_ptr).cast::<i32>();
 +    }
 +
 +    // Changes in mutability. Do not lint this.
 +    let _ = ptr as *mut i32;
 +    let _ = mut_ptr as *const i32;
 +
 +    // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this.
 +    let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
 +    let _ = ptr_of_array as *const [u32];
 +    let _ = ptr_of_array as *const dyn std::fmt::Debug;
 +
 +    // Ensure the lint doesn't produce unnecessary turbofish for inferred types.
 +    let _: *const i32 = ptr.cast();
 +    let _: *mut i32 = mut_ptr.cast();
++
++    // Make sure the lint is triggered inside a macro
++    let _ = cast_it!(ptr);
++
++    // Do not lint inside macros from external crates
++    let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 +}
 +
 +fn _msrv_1_37() {
 +    #![clippy::msrv = "1.37"]
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    // `pointer::cast` was stabilized in 1.38. Do not lint this
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +}
 +
 +fn _msrv_1_38() {
 +    #![clippy::msrv = "1.38"]
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr.cast::<i32>();
 +    let _ = mut_ptr.cast::<i32>();
 +}
index b68d4bc0aaca1f91cc3ed1244953124fe29aaf49,0000000000000000000000000000000000000000..ca2616b0069a07003d5d99e6505f0c86922b9e4c
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,65 @@@
 +// run-rustfix
++// aux-build:macro_rules.rs
 +
 +#![warn(clippy::ptr_as_ptr)]
 +#![feature(custom_inner_attributes)]
 +
++extern crate macro_rules;
++
++macro_rules! cast_it {
++    ($ptr: ident) => {
++        $ptr as *const i32
++    };
++}
++
 +fn main() {
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +
 +    // Make sure the lint can handle the difference in their operator precedences.
 +    unsafe {
 +        let ptr_ptr: *const *const u32 = &ptr;
 +        let _ = *ptr_ptr as *const i32;
 +    }
 +
 +    // Changes in mutability. Do not lint this.
 +    let _ = ptr as *mut i32;
 +    let _ = mut_ptr as *const i32;
 +
 +    // `pointer::cast` cannot perform unsized coercions unlike `as`. Do not lint this.
 +    let ptr_of_array: *const [u32; 4] = &[1, 2, 3, 4];
 +    let _ = ptr_of_array as *const [u32];
 +    let _ = ptr_of_array as *const dyn std::fmt::Debug;
 +
 +    // Ensure the lint doesn't produce unnecessary turbofish for inferred types.
 +    let _: *const i32 = ptr as *const _;
 +    let _: *mut i32 = mut_ptr as _;
++
++    // Make sure the lint is triggered inside a macro
++    let _ = cast_it!(ptr);
++
++    // Do not lint inside macros from external crates
++    let _ = macro_rules::ptr_as_ptr_cast!(ptr);
 +}
 +
 +fn _msrv_1_37() {
 +    #![clippy::msrv = "1.37"]
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    // `pointer::cast` was stabilized in 1.38. Do not lint this
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +}
 +
 +fn _msrv_1_38() {
 +    #![clippy::msrv = "1.38"]
 +    let ptr: *const u32 = &42_u32;
 +    let mut_ptr: *mut u32 = &mut 42_u32;
 +
 +    let _ = ptr as *const i32;
 +    let _ = mut_ptr as *mut i32;
 +}
index 854906dc111dfec2bcd5a4dc3852ee99f5a2ec74,0000000000000000000000000000000000000000..c58c55cfd83a15c5ac671f19f37ff4f64be212a8
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,57 @@@
-   --> $DIR/ptr_as_ptr.rs:10:13
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:11:13
++  --> $DIR/ptr_as_ptr.rs:19:13
 +   |
 +LL |     let _ = ptr as *const i32;
 +   |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 +   |
 +   = note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
 +
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:16:17
++  --> $DIR/ptr_as_ptr.rs:20:13
 +   |
 +LL |     let _ = mut_ptr as *mut i32;
 +   |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 +
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:29:25
++  --> $DIR/ptr_as_ptr.rs:25:17
 +   |
 +LL |         let _ = *ptr_ptr as *const i32;
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
 +
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:30:23
++  --> $DIR/ptr_as_ptr.rs:38:25
 +   |
 +LL |     let _: *const i32 = ptr as *const _;
 +   |                         ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
 +
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:48:13
++  --> $DIR/ptr_as_ptr.rs:39:23
 +   |
 +LL |     let _: *mut i32 = mut_ptr as _;
 +   |                       ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
 +
 +error: `as` casting between raw pointers without changing its mutability
-   --> $DIR/ptr_as_ptr.rs:49:13
++  --> $DIR/ptr_as_ptr.rs:11:9
++   |
++LL |         $ptr as *const i32
++   |         ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
++...
++LL |     let _ = cast_it!(ptr);
++   |             ------------- in this macro invocation
++   |
++   = note: this error originates in the macro `cast_it` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: `as` casting between raw pointers without changing its mutability
++  --> $DIR/ptr_as_ptr.rs:63:13
 +   |
 +LL |     let _ = ptr as *const i32;
 +   |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 +
 +error: `as` casting between raw pointers without changing its mutability
- error: aborting due to 7 previous errors
++  --> $DIR/ptr_as_ptr.rs:64:13
 +   |
 +LL |     let _ = mut_ptr as *mut i32;
 +   |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 +
++error: aborting due to 8 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9241bf7ed74025b8508ace83be7a2819fe66d09e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,321 @@@
++#![warn(clippy::recursive_format_impl)]
++#![allow(
++    clippy::inherent_to_string_shadow_display,
++    clippy::to_string_in_format_args,
++    clippy::deref_addrof
++)]
++
++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 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6171696ed69d4f7432cb0bd7488bc4f00ae18f80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++error: using `self.to_string` in `fmt::Display` implementation will cause infinite recursion
++  --> $DIR/recursive_format_impl.rs:29:25
++   |
++LL |         write!(f, "{}", self.to_string())
++   |                         ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::recursive-format-impl` implied by `-D warnings`
++
++error: unnecessary use of `to_string`
++  --> $DIR/recursive_format_impl.rs:61:50
++   |
++LL |             Self::E(string) => write!(f, "E {}", string.to_string()),
++   |                                                  ^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::unnecessary-to-owned` implied by `-D warnings`
++   = note: this error originates in the macro `$crate::format_args` (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:73: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:82: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:88: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:97: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
++   |
++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:177: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:193: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
++   |
++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
++   |
++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 11 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8dd8d3092378e1dd225e7c8171f345e91b354498
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++// run-rustfix
++
++#![allow(unused, clippy::deref_by_slicing)]
++#![warn(clippy::redundant_slicing)]
++
++use std::io::Read;
++
++fn main() {
++    let slice: &[u32] = &[0];
++    let _ = slice; // Redundant slice
++
++    let v = vec![0];
++    let _ = &v[..]; // Ok, results in `&[_]`
++    let _ = (&*v); // Outer borrow is redundant
++
++    static S: &[u8] = &[0, 1, 2];
++    let _ = &mut &S[..]; // Ok, re-borrows slice
++
++    let mut vec = vec![0];
++    let mut_slice = &mut vec[..]; // Ok, results in `&mut [_]`
++    let _ = &mut mut_slice[..]; // Ok, re-borrows slice
++
++    let ref_vec = &vec;
++    let _ = &ref_vec[..]; // Ok, results in `&[_]`
++
++    macro_rules! m {
++        ($e:expr) => {
++            $e
++        };
++    }
++    let _ = slice;
++
++    macro_rules! m2 {
++        ($e:expr) => {
++            &$e[..]
++        };
++    }
++    let _ = m2!(slice); // Don't lint in a macro
++
++    let slice_ref = &slice;
++    let _ = &slice_ref[..]; // Ok, derefs slice
++
++    // Issue #7972
++    let bytes: &[u8] = &[];
++    let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Ok, re-borrows slice
++}
index 554b6ba36ae0dd0451598c3beabb8d5390a38a99,0000000000000000000000000000000000000000..51c16dd8d65a2b67fb33256d564259193790dafd
mode 100644,000000..100644
--- /dev/null
@@@ -1,32 -1,0 +1,46 @@@
- #![allow(unused)]
++// run-rustfix
++
++#![allow(unused, clippy::deref_by_slicing)]
 +#![warn(clippy::redundant_slicing)]
 +
++use std::io::Read;
++
 +fn main() {
 +    let slice: &[u32] = &[0];
-     let _ = &slice[..];
++    let _ = &slice[..]; // Redundant slice
 +
 +    let v = vec![0];
-     let _ = &v[..]; // Changes the type
-     let _ = &(&v[..])[..]; // Outer borrow is redundant
++    let _ = &v[..]; // Ok, results in `&[_]`
++    let _ = &(&*v)[..]; // Outer borrow is redundant
 +
 +    static S: &[u8] = &[0, 1, 2];
-     let err = &mut &S[..]; // Should reborrow instead of slice
++    let _ = &mut &S[..]; // Ok, re-borrows slice
 +
 +    let mut vec = vec![0];
-     let mut_slice = &mut *vec;
-     let _ = &mut mut_slice[..]; // Should reborrow instead of slice
++    let mut_slice = &mut vec[..]; // Ok, results in `&mut [_]`
++    let _ = &mut mut_slice[..]; // Ok, re-borrows slice
++
++    let ref_vec = &vec;
++    let _ = &ref_vec[..]; // Ok, results in `&[_]`
 +
 +    macro_rules! m {
 +        ($e:expr) => {
 +            $e
 +        };
 +    }
 +    let _ = &m!(slice)[..];
 +
 +    macro_rules! m2 {
 +        ($e:expr) => {
 +            &$e[..]
 +        };
 +    }
 +    let _ = m2!(slice); // Don't lint in a macro
++
++    let slice_ref = &slice;
++    let _ = &slice_ref[..]; // Ok, derefs slice
++
++    // Issue #7972
++    let bytes: &[u8] = &[];
++    let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Ok, re-borrows slice
 +}
index bbd10eafbbe7807e3e9b8f8e96fc8ada4936b999,0000000000000000000000000000000000000000..82367143c07fa40f395768d038a734e0bfeaa619
mode 100644,000000..100644
--- /dev/null
@@@ -1,34 -1,0 +1,22 @@@
-   --> $DIR/redundant_slicing.rs:6:13
 +error: redundant slicing of the whole range
- LL |     let _ = &slice[..];
++  --> $DIR/redundant_slicing.rs:10:13
 +   |
-   --> $DIR/redundant_slicing.rs:10:13
-    |
- LL |     let _ = &(&v[..])[..]; // Outer borrow is redundant
-    |             ^^^^^^^^^^^^^ help: use the original value instead: `(&v[..])`
- error: redundant slicing of the whole range
-   --> $DIR/redundant_slicing.rs:13:20
-    |
- LL |     let err = &mut &S[..]; // Should reborrow instead of slice
-    |                    ^^^^^^ help: reborrow the original value instead: `&*S`
- error: redundant slicing of the whole range
-   --> $DIR/redundant_slicing.rs:17:13
++LL |     let _ = &slice[..]; // Redundant slice
 +   |             ^^^^^^^^^^ help: use the original value instead: `slice`
 +   |
 +   = note: `-D clippy::redundant-slicing` implied by `-D warnings`
 +
 +error: redundant slicing of the whole range
- LL |     let _ = &mut mut_slice[..]; // Should reborrow instead of slice
-    |             ^^^^^^^^^^^^^^^^^^ help: reborrow the original value instead: `&mut *mut_slice`
++  --> $DIR/redundant_slicing.rs:14:13
 +   |
-   --> $DIR/redundant_slicing.rs:24:13
++LL |     let _ = &(&*v)[..]; // Outer borrow is redundant
++   |             ^^^^^^^^^^ help: use the original value instead: `(&*v)`
 +
 +error: redundant slicing of the whole range
- error: aborting due to 5 previous errors
++  --> $DIR/redundant_slicing.rs:31:13
 +   |
 +LL |     let _ = &m!(slice)[..];
 +   |             ^^^^^^^^^^^^^^ help: use the original value instead: `slice`
 +
++error: aborting due to 3 previous errors
 +
index 8bddec576ed14ceb3eb1448d4f8f509ff4c5ed30,0000000000000000000000000000000000000000..24a0c812291982371e67496695f2eb1b8275abe5
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,71 @@@
 +//! Test for Clippy lint renames.
 +// run-rustfix
 +
 +#![allow(dead_code)]
 +// allow the new lint name here, to test if the new name works
 +#![allow(clippy::module_name_repetitions)]
 +#![allow(clippy::new_without_default)]
 +#![allow(clippy::redundant_static_lifetimes)]
 +#![allow(clippy::cognitive_complexity)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::box_collection)]
 +#![allow(clippy::blocks_in_if_conditions)]
 +#![allow(clippy::map_unwrap_or)]
 +#![allow(clippy::unwrap_used)]
 +#![allow(clippy::expect_used)]
 +#![allow(clippy::for_loops_over_fallibles)]
 +#![allow(clippy::useless_conversion)]
 +#![allow(clippy::invisible_characters)]
 +#![allow(clippy::single_char_add_str)]
 +#![allow(clippy::match_result_ok)]
 +#![allow(clippy::disallowed_types)]
 +#![allow(clippy::disallowed_methods)]
++#![allow(clippy::recursive_format_impl)]
 +// uplifted lints
 +#![allow(invalid_value)]
 +#![allow(array_into_iter)]
 +#![allow(unused_labels)]
 +#![allow(drop_bounds)]
 +#![allow(temporary_cstring_as_ptr)]
 +#![allow(non_fmt_panics)]
 +#![allow(unknown_lints)]
 +#![allow(invalid_atomic_ordering)]
 +#![allow(enum_intrinsics_non_enums)]
 +// warn for the old lint name here, to test if the renaming worked
 +#![warn(clippy::module_name_repetitions)]
 +#![warn(clippy::new_without_default)]
 +#![warn(clippy::redundant_static_lifetimes)]
 +#![warn(clippy::cognitive_complexity)]
 +#![warn(clippy::bind_instead_of_map)]
 +#![warn(clippy::box_collection)]
 +#![warn(clippy::blocks_in_if_conditions)]
 +#![warn(clippy::blocks_in_if_conditions)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::map_unwrap_or)]
 +#![warn(clippy::unwrap_used)]
 +#![warn(clippy::unwrap_used)]
 +#![warn(clippy::expect_used)]
 +#![warn(clippy::expect_used)]
 +#![warn(clippy::for_loops_over_fallibles)]
 +#![warn(clippy::for_loops_over_fallibles)]
 +#![warn(clippy::useless_conversion)]
 +#![warn(clippy::invisible_characters)]
 +#![warn(clippy::single_char_add_str)]
 +#![warn(clippy::match_result_ok)]
 +#![warn(clippy::disallowed_types)]
 +#![warn(clippy::disallowed_methods)]
 +#![warn(clippy::needless_borrow)]
++#![warn(clippy::recursive_format_impl)]
 +// uplifted lints
 +#![warn(invalid_value)]
 +#![warn(array_into_iter)]
 +#![warn(unused_labels)]
 +#![warn(drop_bounds)]
 +#![warn(temporary_cstring_as_ptr)]
 +#![warn(non_fmt_panics)]
 +#![warn(unknown_lints)]
 +#![warn(invalid_atomic_ordering)]
 +#![warn(enum_intrinsics_non_enums)]
 +
 +fn main() {}
index d2010d71d2c118681cb7b8f56ddc347af2dfb57e,0000000000000000000000000000000000000000..ea64234c680d37f5ef8b7951100723a5a33a3054
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,71 @@@
 +//! Test for Clippy lint renames.
 +// run-rustfix
 +
 +#![allow(dead_code)]
 +// allow the new lint name here, to test if the new name works
 +#![allow(clippy::module_name_repetitions)]
 +#![allow(clippy::new_without_default)]
 +#![allow(clippy::redundant_static_lifetimes)]
 +#![allow(clippy::cognitive_complexity)]
 +#![allow(clippy::bind_instead_of_map)]
 +#![allow(clippy::box_collection)]
 +#![allow(clippy::blocks_in_if_conditions)]
 +#![allow(clippy::map_unwrap_or)]
 +#![allow(clippy::unwrap_used)]
 +#![allow(clippy::expect_used)]
 +#![allow(clippy::for_loops_over_fallibles)]
 +#![allow(clippy::useless_conversion)]
 +#![allow(clippy::invisible_characters)]
 +#![allow(clippy::single_char_add_str)]
 +#![allow(clippy::match_result_ok)]
 +#![allow(clippy::disallowed_types)]
 +#![allow(clippy::disallowed_methods)]
++#![allow(clippy::recursive_format_impl)]
 +// uplifted lints
 +#![allow(invalid_value)]
 +#![allow(array_into_iter)]
 +#![allow(unused_labels)]
 +#![allow(drop_bounds)]
 +#![allow(temporary_cstring_as_ptr)]
 +#![allow(non_fmt_panics)]
 +#![allow(unknown_lints)]
 +#![allow(invalid_atomic_ordering)]
 +#![allow(enum_intrinsics_non_enums)]
 +// warn for the old lint name here, to test if the renaming worked
 +#![warn(clippy::stutter)]
 +#![warn(clippy::new_without_default_derive)]
 +#![warn(clippy::const_static_lifetime)]
 +#![warn(clippy::cyclomatic_complexity)]
 +#![warn(clippy::option_and_then_some)]
 +#![warn(clippy::box_vec)]
 +#![warn(clippy::block_in_if_condition_expr)]
 +#![warn(clippy::block_in_if_condition_stmt)]
 +#![warn(clippy::option_map_unwrap_or)]
 +#![warn(clippy::option_map_unwrap_or_else)]
 +#![warn(clippy::result_map_unwrap_or_else)]
 +#![warn(clippy::option_unwrap_used)]
 +#![warn(clippy::result_unwrap_used)]
 +#![warn(clippy::option_expect_used)]
 +#![warn(clippy::result_expect_used)]
 +#![warn(clippy::for_loop_over_option)]
 +#![warn(clippy::for_loop_over_result)]
 +#![warn(clippy::identity_conversion)]
 +#![warn(clippy::zero_width_space)]
 +#![warn(clippy::single_char_push_str)]
 +#![warn(clippy::if_let_some_result)]
 +#![warn(clippy::disallowed_type)]
 +#![warn(clippy::disallowed_method)]
 +#![warn(clippy::ref_in_deref)]
++#![warn(clippy::to_string_in_display)]
 +// uplifted lints
 +#![warn(clippy::invalid_ref)]
 +#![warn(clippy::into_iter_on_array)]
 +#![warn(clippy::unused_label)]
 +#![warn(clippy::drop_bounds)]
 +#![warn(clippy::temporary_cstring_as_ptr)]
 +#![warn(clippy::panic_params)]
 +#![warn(clippy::unknown_clippy_lints)]
 +#![warn(clippy::invalid_atomic_ordering)]
 +#![warn(clippy::mem_discriminant_non_enum)]
 +
 +fn main() {}
index 45cb8b786f5f3d89ba5c357d47343d0a4311ee11,0000000000000000000000000000000000000000..8b132a7838470cbd90aba6f1e1c36be73274e8bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,208 @@@
-   --> $DIR/rename.rs:34:9
 +error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-   --> $DIR/rename.rs:35:9
++  --> $DIR/rename.rs:35:9
 +   |
 +LL | #![warn(clippy::stutter)]
 +   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 +   |
 +   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
 +
 +error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-   --> $DIR/rename.rs:36:9
++  --> $DIR/rename.rs:36:9
 +   |
 +LL | #![warn(clippy::new_without_default_derive)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 +
 +error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-   --> $DIR/rename.rs:37:9
++  --> $DIR/rename.rs:37:9
 +   |
 +LL | #![warn(clippy::const_static_lifetime)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 +
 +error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-   --> $DIR/rename.rs:38:9
++  --> $DIR/rename.rs:38:9
 +   |
 +LL | #![warn(clippy::cyclomatic_complexity)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 +
 +error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-   --> $DIR/rename.rs:39:9
++  --> $DIR/rename.rs:39:9
 +   |
 +LL | #![warn(clippy::option_and_then_some)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 +
 +error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-   --> $DIR/rename.rs:40:9
++  --> $DIR/rename.rs:40:9
 +   |
 +LL | #![warn(clippy::box_vec)]
 +   |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 +
 +error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_if_conditions`
-   --> $DIR/rename.rs:41:9
++  --> $DIR/rename.rs:41:9
 +   |
 +LL | #![warn(clippy::block_in_if_condition_expr)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 +
 +error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_if_conditions`
-   --> $DIR/rename.rs:42:9
++  --> $DIR/rename.rs:42:9
 +   |
 +LL | #![warn(clippy::block_in_if_condition_stmt)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_if_conditions`
 +
 +error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-   --> $DIR/rename.rs:43:9
++  --> $DIR/rename.rs:43:9
 +   |
 +LL | #![warn(clippy::option_map_unwrap_or)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
 +error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-   --> $DIR/rename.rs:44:9
++  --> $DIR/rename.rs:44:9
 +   |
 +LL | #![warn(clippy::option_map_unwrap_or_else)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
 +error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-   --> $DIR/rename.rs:45:9
++  --> $DIR/rename.rs:45:9
 +   |
 +LL | #![warn(clippy::result_map_unwrap_or_else)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 +
 +error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-   --> $DIR/rename.rs:46:9
++  --> $DIR/rename.rs:46:9
 +   |
 +LL | #![warn(clippy::option_unwrap_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 +
 +error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-   --> $DIR/rename.rs:47:9
++  --> $DIR/rename.rs:47:9
 +   |
 +LL | #![warn(clippy::result_unwrap_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 +
 +error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-   --> $DIR/rename.rs:48:9
++  --> $DIR/rename.rs:48:9
 +   |
 +LL | #![warn(clippy::option_expect_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 +
 +error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-   --> $DIR/rename.rs:49:9
++  --> $DIR/rename.rs:49:9
 +   |
 +LL | #![warn(clippy::result_expect_used)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 +
 +error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
-   --> $DIR/rename.rs:50:9
++  --> $DIR/rename.rs:50:9
 +   |
 +LL | #![warn(clippy::for_loop_over_option)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 +
 +error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
-   --> $DIR/rename.rs:51:9
++  --> $DIR/rename.rs:51:9
 +   |
 +LL | #![warn(clippy::for_loop_over_result)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
 +
 +error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-   --> $DIR/rename.rs:52:9
++  --> $DIR/rename.rs:52:9
 +   |
 +LL | #![warn(clippy::identity_conversion)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 +
 +error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-   --> $DIR/rename.rs:53:9
++  --> $DIR/rename.rs:53:9
 +   |
 +LL | #![warn(clippy::zero_width_space)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 +
 +error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-   --> $DIR/rename.rs:54:9
++  --> $DIR/rename.rs:54:9
 +   |
 +LL | #![warn(clippy::single_char_push_str)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 +
 +error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-   --> $DIR/rename.rs:55:9
++  --> $DIR/rename.rs:55:9
 +   |
 +LL | #![warn(clippy::if_let_some_result)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 +
 +error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-   --> $DIR/rename.rs:56:9
++  --> $DIR/rename.rs:56:9
 +   |
 +LL | #![warn(clippy::disallowed_type)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 +
 +error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-   --> $DIR/rename.rs:57:9
++  --> $DIR/rename.rs:57:9
 +   |
 +LL | #![warn(clippy::disallowed_method)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 +
 +error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
- error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
++  --> $DIR/rename.rs:58:9
 +   |
 +LL | #![warn(clippy::ref_in_deref)]
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 +
-   --> $DIR/rename.rs:60:9
++error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
 +  --> $DIR/rename.rs:59:9
 +   |
++LL | #![warn(clippy::to_string_in_display)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
++
++error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
++  --> $DIR/rename.rs:61:9
++   |
 +LL | #![warn(clippy::invalid_ref)]
 +   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 +
 +error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-   --> $DIR/rename.rs:61:9
++  --> $DIR/rename.rs:62:9
 +   |
 +LL | #![warn(clippy::into_iter_on_array)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 +
 +error: lint `clippy::unused_label` has been renamed to `unused_labels`
-   --> $DIR/rename.rs:62:9
++  --> $DIR/rename.rs:63:9
 +   |
 +LL | #![warn(clippy::unused_label)]
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 +
 +error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-   --> $DIR/rename.rs:63:9
++  --> $DIR/rename.rs:64:9
 +   |
 +LL | #![warn(clippy::drop_bounds)]
 +   |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 +
 +error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-   --> $DIR/rename.rs:64:9
++  --> $DIR/rename.rs:65:9
 +   |
 +LL | #![warn(clippy::temporary_cstring_as_ptr)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 +
 +error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-   --> $DIR/rename.rs:65:9
++  --> $DIR/rename.rs:66:9
 +   |
 +LL | #![warn(clippy::panic_params)]
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 +
 +error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-   --> $DIR/rename.rs:66:9
++  --> $DIR/rename.rs:67:9
 +   |
 +LL | #![warn(clippy::unknown_clippy_lints)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 +
 +error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-   --> $DIR/rename.rs:67:9
++  --> $DIR/rename.rs:68:9
 +   |
 +LL | #![warn(clippy::invalid_atomic_ordering)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 +
 +error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
- error: aborting due to 33 previous errors
++  --> $DIR/rename.rs:69:9
 +   |
 +LL | #![warn(clippy::mem_discriminant_non_enum)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 +
++error: aborting due to 34 previous errors
 +
index bd371888046361ceea6c5ce3b9c866301df7e5a4,0000000000000000000000000000000000000000..dd148edf5292d0c0e130d75f4736abbbce9a8404
mode 100644,000000..100644
--- /dev/null
@@@ -1,237 -1,0 +1,245 @@@
 +#![warn(clippy::single_match)]
 +
 +fn dummy() {}
 +
 +fn single_match() {
 +    let x = Some(1u8);
 +
 +    match x {
 +        Some(y) => {
 +            println!("{:?}", y);
 +        },
 +        _ => (),
 +    };
 +
 +    let x = Some(1u8);
 +    match x {
 +        // Note the missing block braces.
 +        // We suggest `if let Some(y) = x { .. }` because the macro
 +        // is expanded before we can do anything.
 +        Some(y) => println!("{:?}", y),
 +        _ => (),
 +    }
 +
 +    let z = (1u8, 1u8);
 +    match z {
 +        (2..=3, 7..=9) => dummy(),
 +        _ => {},
 +    };
 +
 +    // Not linted (pattern guards used)
 +    match x {
 +        Some(y) if y == 0 => println!("{:?}", y),
 +        _ => (),
 +    }
 +
 +    // Not linted (no block with statements in the single arm)
 +    match z {
 +        (2..=3, 7..=9) => println!("{:?}", z),
 +        _ => println!("nope"),
 +    }
 +}
 +
 +enum Foo {
 +    Bar,
 +    Baz(u8),
 +}
 +use std::borrow::Cow;
 +use Foo::*;
 +
 +fn single_match_know_enum() {
 +    let x = Some(1u8);
 +    let y: Result<_, i8> = Ok(1i8);
 +
 +    match x {
 +        Some(y) => dummy(),
 +        None => (),
 +    };
 +
 +    match y {
 +        Ok(y) => dummy(),
 +        Err(..) => (),
 +    };
 +
 +    let c = Cow::Borrowed("");
 +
 +    match c {
 +        Cow::Borrowed(..) => dummy(),
 +        Cow::Owned(..) => (),
 +    };
 +
 +    let z = Foo::Bar;
 +    // no warning
 +    match z {
 +        Bar => println!("42"),
 +        Baz(_) => (),
 +    }
 +
 +    match z {
 +        Baz(_) => println!("42"),
 +        Bar => (),
 +    }
 +}
 +
 +// issue #173
 +fn if_suggestion() {
 +    let x = "test";
 +    match x {
 +        "test" => println!(),
 +        _ => (),
 +    }
 +
 +    #[derive(PartialEq, Eq)]
 +    enum Foo {
 +        A,
 +        B,
 +        C(u32),
 +    }
 +
 +    let x = Foo::A;
 +    match x {
 +        Foo::A => println!(),
 +        _ => (),
 +    }
 +
 +    const FOO_C: Foo = Foo::C(0);
 +    match x {
 +        FOO_C => println!(),
 +        _ => (),
 +    }
 +
 +    match &&x {
 +        Foo::A => println!(),
 +        _ => (),
 +    }
 +
 +    let x = &x;
 +    match &x {
 +        Foo::A => println!(),
 +        _ => (),
 +    }
 +
 +    enum Bar {
 +        A,
 +        B,
 +    }
 +    impl PartialEq for Bar {
 +        fn eq(&self, rhs: &Self) -> bool {
 +            matches!((self, rhs), (Self::A, Self::A) | (Self::B, Self::B))
 +        }
 +    }
 +    impl Eq for Bar {}
 +
 +    let x = Bar::A;
 +    match x {
 +        Bar::A => println!(),
 +        _ => (),
 +    }
 +
 +    // issue #7038
 +    struct X;
 +    let x = Some(X);
 +    match x {
 +        None => println!(),
 +        _ => (),
 +    };
 +}
 +
 +// See: issue #8282
 +fn ranges() {
 +    enum E {
 +        V,
 +    }
 +    let x = (Some(E::V), Some(42));
 +
 +    // Don't lint, because the `E` enum can be extended with additional fields later. Thus, the
 +    // proposed replacement to `if let Some(E::V)` may hide non-exhaustive warnings that appeared
 +    // because of `match` construction.
 +    match x {
 +        (Some(E::V), _) => {},
 +        (None, _) => {},
 +    }
 +
 +    // lint
 +    match x {
 +        (Some(_), _) => {},
 +        (None, _) => {},
 +    }
 +
 +    // lint
 +    match x {
 +        (Some(E::V), _) => todo!(),
 +        (_, _) => {},
 +    }
 +
 +    // lint
 +    match (Some(42), Some(E::V), Some(42)) {
 +        (.., Some(E::V), _) => {},
 +        (..) => {},
 +    }
 +
 +    // Don't lint, see above.
 +    match (Some(E::V), Some(E::V), Some(E::V)) {
 +        (.., Some(E::V), _) => {},
 +        (.., None, _) => {},
 +    }
 +
 +    // Don't lint, see above.
 +    match (Some(E::V), Some(E::V), Some(E::V)) {
 +        (Some(E::V), ..) => {},
 +        (None, ..) => {},
 +    }
 +
 +    // Don't lint, see above.
 +    match (Some(E::V), Some(E::V), Some(E::V)) {
 +        (_, Some(E::V), ..) => {},
 +        (_, None, ..) => {},
 +    }
 +}
 +
 +fn skip_type_aliases() {
 +    enum OptionEx {
 +        Some(i32),
 +        None,
 +    }
 +    enum ResultEx {
 +        Err(i32),
 +        Ok(i32),
 +    }
 +
 +    use OptionEx::{None, Some};
 +    use ResultEx::{Err, Ok};
 +
 +    // don't lint
 +    match Err(42) {
 +        Ok(_) => dummy(),
 +        Err(_) => (),
 +    };
 +
 +    // don't lint
 +    match Some(1i32) {
 +        Some(_) => dummy(),
 +        None => (),
 +    };
 +}
 +
 +macro_rules! single_match {
 +    ($num:literal) => {
 +        match $num {
 +            15 => println!("15"),
 +            _ => (),
 +        }
 +    };
 +}
 +
 +fn main() {
 +    single_match!(5);
++
++    // Don't lint
++    let _ = match Some(0) {
++        #[cfg(feature = "foo")]
++        Some(10) => 11,
++        Some(x) => x,
++        _ => 0,
++    };
 +}
index 71539940fbf270e23d043b52d792f2e505c67906,0000000000000000000000000000000000000000..b163d6056343d5b34e2e57f520c083a629db24ed
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,91 @@@
- #![allow(clippy::unit_arg)]
 +#![warn(clippy::transmute_undefined_repr)]
-         let _: () = core::mem::transmute(value::<Empty>());
-         let _: Empty = core::mem::transmute(value::<()>());
++#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref)]
++
++use core::ffi::c_void;
++use core::mem::{size_of, transmute};
 +
 +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 _: Ty<u32> = core::mem::transmute(value::<u32>());
-         let _: Ty<u32> = core::mem::transmute(value::<u32>());
++        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 _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
-         let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
++        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
 +
-         let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
-         let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
++        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
 +
-         let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
-         let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
++        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 _: Ty<&()> = core::mem::transmute(value::<&()>());
-         let _: &() = core::mem::transmute(value::<Ty<&()>>());
++        let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure
++        let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure
 +
-         let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
-         let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
++        let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure
++        let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure
 +
-         let _: Ty<usize> = core::mem::transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
-         let _: &Ty2<u32, i32> = core::mem::transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
++        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 _: Ty<[u8; 8]> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
-         let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty<[u8; 8]>>()); // 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
 +    }
 +}
index 040c63c7afa625202d558097aec418e11a13a1b2,0000000000000000000000000000000000000000..42d544fc954c5b31034bb0dc2f0ace0f16ee1444
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,64 @@@
-   --> $DIR/transmute_undefined_repr.rs:23:33
 +error: transmute from `Ty2<u32, i32>` which has an undefined layout
- LL |         let _: Ty2C<u32, i32> = core::mem::transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
-    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++  --> $DIR/transmute_undefined_repr.rs:26:33
 +   |
-   --> $DIR/transmute_undefined_repr.rs:24:32
++LL |         let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
++   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
 +
 +error: transmute into `Ty2<u32, i32>` which has an undefined layout
- LL |         let _: Ty2<u32, i32> = core::mem::transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
-    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++  --> $DIR/transmute_undefined_repr.rs:27:32
 +   |
-   --> $DIR/transmute_undefined_repr.rs:29:32
++LL |         let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
- LL |         let _: Ty2<u32, f32> = core::mem::transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
-    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++  --> $DIR/transmute_undefined_repr.rs:32:32
 +   |
-   --> $DIR/transmute_undefined_repr.rs:30:36
++LL |         let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
++   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
 +error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
- LL |         let _: Ty<Ty2<u32, i32>> = core::mem::transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
-    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++  --> $DIR/transmute_undefined_repr.rs:33:36
 +   |
- error: transmute to `&Ty2<u32, f32>` which has an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:35:33
++LL |         let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
++   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
- LL |         let _: &Ty2<u32, f32> = core::mem::transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
-    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
++  --> $DIR/transmute_undefined_repr.rs:38:33
 +   |
- error: transmute from `&Ty2<u32, f32>` which has an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:36:37
++LL |         let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
++   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: two instances of the same generic type (`Ty2`) may have different layouts
++
++error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
++  --> $DIR/transmute_undefined_repr.rs:39:37
++   |
++LL |         let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
++   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
- LL |         let _: Ty<&Ty2<u32, i32>> = core::mem::transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
-    |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
++  --> $DIR/transmute_undefined_repr.rs:56:45
 +   |
- error: aborting due to 6 previous errors
++LL |         let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
++   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: two instances of the same generic type (`Ty2`) may have different layouts
++
++error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
++  --> $DIR/transmute_undefined_repr.rs:57:37
++   |
++LL |         let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
++   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
++error: aborting due to 8 previous errors
 +