]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '91496c2ac6abf6454c413bb23e8becf6b6dc20ea' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Thu, 21 Oct 2021 11:11:36 +0000 (13:11 +0200)
committerflip1995 <philipp.krones@embecosm.com>
Thu, 21 Oct 2021 11:11:36 +0000 (13:11 +0200)
127 files changed:
1  2 
src/tools/clippy/CHANGELOG.md
src/tools/clippy/Cargo.toml
src/tools/clippy/clippy_dev/Cargo.toml
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/Cargo.toml
src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/default.rs
src/tools/clippy/clippy_lints/src/disallowed_type.rs
src/tools/clippy/clippy_lints/src/doc.rs
src/tools/clippy/clippy_lints/src/eq_op.rs
src/tools/clippy/clippy_lints/src/equatable_if_let.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/functions/mod.rs
src/tools/clippy/clippy_lints/src/identity_op.rs
src/tools/clippy/clippy_lints/src/if_then_panic.rs
src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
src/tools/clippy/clippy_lints/src/inherent_to_string.rs
src/tools/clippy/clippy_lints/src/let_underscore.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_nursery.rs
src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
src/tools/clippy/clippy_lints/src/lib.register_perf.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/match_result_ok.rs
src/tools/clippy/clippy_lints/src/match_str_case_mismatch.rs
src/tools/clippy/clippy_lints/src/matches.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
src/tools/clippy/clippy_lints/src/missing_inline.rs
src/tools/clippy/clippy_lints/src/mut_mut.rs
src/tools/clippy/clippy_lints/src/no_effect.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/semicolon_if_nothing_returned.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_char.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/uninit_vec.rs
src/tools/clippy/clippy_lints/src/unnecessary_sort_by.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/higher.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/doc/basics.md
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
src/tools/clippy/tests/ui-toml/toml_disallowed_type/clippy.toml
src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.rs
src/tools/clippy/tests/ui-toml/toml_disallowed_type/conf_disallowed_type.stderr
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/cfg_attr_rustfmt.fixed
src/tools/clippy/tests/ui/cfg_attr_rustfmt.rs
src/tools/clippy/tests/ui/doc_unsafe.rs
src/tools/clippy/tests/ui/equatable_if_let.fixed
src/tools/clippy/tests/ui/equatable_if_let.rs
src/tools/clippy/tests/ui/equatable_if_let.stderr
src/tools/clippy/tests/ui/expect_fun_call.fixed
src/tools/clippy/tests/ui/expect_fun_call.rs
src/tools/clippy/tests/ui/expect_fun_call.stderr
src/tools/clippy/tests/ui/field_reassign_with_default.rs
src/tools/clippy/tests/ui/field_reassign_with_default.stderr
src/tools/clippy/tests/ui/fn_to_numeric_cast_any.rs
src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr
src/tools/clippy/tests/ui/format.fixed
src/tools/clippy/tests/ui/format.rs
src/tools/clippy/tests/ui/format_args.fixed
src/tools/clippy/tests/ui/format_args.rs
src/tools/clippy/tests/ui/format_args.stderr
src/tools/clippy/tests/ui/format_args_unfixable.rs
src/tools/clippy/tests/ui/format_args_unfixable.stderr
src/tools/clippy/tests/ui/implicit_saturating_sub.fixed
src/tools/clippy/tests/ui/implicit_saturating_sub.rs
src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
src/tools/clippy/tests/ui/match_overlapping_arm.rs
src/tools/clippy/tests/ui/match_overlapping_arm.stderr
src/tools/clippy/tests/ui/match_ref_pats.rs
src/tools/clippy/tests/ui/match_ref_pats.stderr
src/tools/clippy/tests/ui/match_str_case_mismatch.rs
src/tools/clippy/tests/ui/match_str_case_mismatch.stderr
src/tools/clippy/tests/ui/mut_mut.rs
src/tools/clippy/tests/ui/mut_mut.stderr
src/tools/clippy/tests/ui/no_effect.rs
src/tools/clippy/tests/ui/no_effect.stderr
src/tools/clippy/tests/ui/option_if_let_else.fixed
src/tools/clippy/tests/ui/option_if_let_else.rs
src/tools/clippy/tests/ui/option_if_let_else.stderr
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/question_mark.stderr
src/tools/clippy/tests/ui/semicolon_if_nothing_returned.rs
src/tools/clippy/tests/ui/shadow.rs
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/to_string_in_display.rs
src/tools/clippy/tests/ui/trailing_empty_array.rs
src/tools/clippy/tests/ui/trailing_empty_array.stderr
src/tools/clippy/tests/ui/transmute.rs
src/tools/clippy/tests/ui/transmute.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/uninit_vec.rs
src/tools/clippy/tests/ui/uninit_vec.stderr
src/tools/clippy/tests/ui/unnecessary_sort_by.fixed
src/tools/clippy/tests/ui/unnecessary_sort_by.rs
src/tools/clippy/tests/ui/unnecessary_sort_by.stderr
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/tests/ui/wildcard_imports.stderr
src/tools/clippy/tests/ui_test/eq_op.rs

index e700e5f0d736e7ca056dc0fdf0a355c429c07d38,0000000000000000000000000000000000000000..3b4c687209e11dfa8ebb9454ad8c4af571a9677f
mode 100644,000000..100644
--- /dev/null
@@@ -1,3107 -1,0 +1,3116 @@@
 +# 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
 +
 +[7bfc26e...master](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...master)
 +
 +## Rust 1.56
 +
 +Current beta, release 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_type`]: 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)
 +
 +### New Lints
 +
 +* Renamed Lint: `if_let_some_result` is now called [`match_result_ok`]. Now also handles `while let` case.
 +
 +## Rust 1.55
 +
 +Current stable, 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_type`]
 +  [#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_method`], [`disallowed_type`]: 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_method`]: 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_method`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* [`to_string_in_display`] [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* [`to_string_in_display`]: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`blacklisted_name`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`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_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_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
 +[`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
 +[`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_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`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_panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_panic
 +[`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
 +[`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
 +[`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_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_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`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_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_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
 +[`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_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
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_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
 +[`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
 +[`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_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_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_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
++[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
++[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
++[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`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_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_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 ba3ed3053ac743466be07b2fdf04c97911dcc0f1,0000000000000000000000000000000000000000..ed7fb1440139f7d199f5632cb54525633c0d48a1
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,62 @@@
- version = "0.1.57"
 +[package]
 +name = "clippy"
++version = "0.1.58"
 +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 = "0.11"
 +rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
 +tempfile = { version = "3.2", optional = true }
 +
 +[dev-dependencies]
 +cargo_metadata = "0.12"
 +compiletest_rs = { version = "0.7", 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"] }
 +
 +[build-dependencies]
 +rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
 +
 +[features]
 +deny-warnings = ["clippy_lints/deny-warnings"]
 +integration = ["tempfile"]
 +internal-lints = ["clippy_lints/internal-lints"]
 +metadata-collector-lint = ["internal-lints", "clippy_lints/metadata-collector-lint"]
 +
 +[package.metadata.rust-analyzer]
 +# This package uses #[feature(rustc_private)]
 +rustc_private = true
index 4a13a4524097601ffda40ce63349ca71fef9563e,0000000000000000000000000000000000000000..affb283017c8ce50252ae6d9750e553591e21497
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
 +[package]
 +name = "clippy_dev"
 +version = "0.0.1"
 +edition = "2021"
 +
 +[dependencies]
 +bytecount = "0.6"
 +clap = "2.33"
++indoc = "1.0"
 +itertools = "0.10"
 +opener = "0.5"
 +regex = "1.5"
 +shell-escape = "0.1"
 +walkdir = "2.3"
 +
 +[features]
 +deny-warnings = []
index 8fdeba9842af3e6f80070aa055e2a28dd70ff000,0000000000000000000000000000000000000000..b5c04efce3bc95bca885102df44ed2c612c6e8f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,217 -1,0 +1,223 @@@
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
 +use clippy_dev::{bless, fmt, new_lint, serve, setup, update_lints};
 +fn main() {
 +    let matches = get_clap_config();
 +
 +    match matches.subcommand() {
 +        ("bless", Some(matches)) => {
 +            bless::bless(matches.is_present("ignore-timestamp"));
 +        },
 +        ("fmt", Some(matches)) => {
 +            fmt::run(matches.is_present("check"), matches.is_present("verbose"));
 +        },
 +        ("update_lints", Some(matches)) => {
 +            if matches.is_present("print-only") {
 +                update_lints::print_lints();
 +            } else if matches.is_present("check") {
 +                update_lints::run(update_lints::UpdateMode::Check);
 +            } else {
 +                update_lints::run(update_lints::UpdateMode::Change);
 +            }
 +        },
 +        ("new_lint", Some(matches)) => {
 +            match new_lint::create(
 +                matches.value_of("pass"),
 +                matches.value_of("name"),
 +                matches.value_of("category"),
++                matches.is_present("msrv"),
 +            ) {
 +                Ok(_) => update_lints::run(update_lints::UpdateMode::Change),
 +                Err(e) => eprintln!("Unable to create lint: {}", e),
 +            }
 +        },
 +        ("setup", Some(sub_command)) => match sub_command.subcommand() {
 +            ("intellij", Some(matches)) => setup::intellij::setup_rustc_src(
 +                matches
 +                    .value_of("rustc-repo-path")
 +                    .expect("this field is mandatory and therefore always valid"),
 +            ),
 +            ("git-hook", Some(matches)) => setup::git_hook::install_hook(matches.is_present("force-override")),
 +            ("vscode-tasks", Some(matches)) => setup::vscode::install_tasks(matches.is_present("force-override")),
 +            _ => {},
 +        },
 +        ("remove", Some(sub_command)) => match sub_command.subcommand() {
 +            ("git-hook", Some(_)) => setup::git_hook::remove_hook(),
 +            ("intellij", Some(_)) => setup::intellij::remove_rustc_src(),
 +            ("vscode-tasks", Some(_)) => setup::vscode::remove_tasks(),
 +            _ => {},
 +        },
 +        ("serve", Some(matches)) => {
 +            let port = matches.value_of("port").unwrap().parse().unwrap();
 +            let lint = matches.value_of("lint");
 +            serve::run(port, lint);
 +        },
 +        _ => {},
 +    }
 +}
 +
 +fn get_clap_config<'a>() -> ArgMatches<'a> {
 +    App::new("Clippy developer tooling")
 +        .setting(AppSettings::ArgRequiredElseHelp)
 +        .subcommand(
 +            SubCommand::with_name("bless")
 +                .about("bless the test output changes")
 +                .arg(
 +                    Arg::with_name("ignore-timestamp")
 +                        .long("ignore-timestamp")
 +                        .help("Include files updated before clippy was built"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("fmt")
 +                .about("Run rustfmt on all projects and tests")
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Use the rustfmt --check option"),
 +                )
 +                .arg(
 +                    Arg::with_name("verbose")
 +                        .short("v")
 +                        .long("verbose")
 +                        .help("Echo commands run"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("update_lints")
 +                .about("Updates lint registration and information from the source code")
 +                .long_about(
 +                    "Makes sure that:\n \
 +                 * the lint count in README.md is correct\n \
 +                 * the changelog contains markdown link references at the bottom\n \
 +                 * all lint groups include the correct lints\n \
 +                 * lint modules in `clippy_lints/*` are visible in `src/lifb.rs` via `pub mod`\n \
 +                 * all lints are registered in the lint store",
 +                )
 +                .arg(Arg::with_name("print-only").long("print-only").help(
 +                    "Print a table of lints to STDOUT. \
 +                 This does not include deprecated and internal lints. \
 +                 (Does not modify any files)",
 +                ))
 +                .arg(
 +                    Arg::with_name("check")
 +                        .long("check")
 +                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("new_lint")
 +                .about("Create new lint and run `cargo dev update_lints`")
 +                .arg(
 +                    Arg::with_name("pass")
 +                        .short("p")
 +                        .long("pass")
 +                        .help("Specify whether the lint runs during the early or late pass")
 +                        .takes_value(true)
 +                        .possible_values(&["early", "late"])
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("name")
 +                        .short("n")
 +                        .long("name")
 +                        .help("Name of the new lint in snake case, ex: fn_too_long")
 +                        .takes_value(true)
 +                        .required(true),
 +                )
 +                .arg(
 +                    Arg::with_name("category")
 +                        .short("c")
 +                        .long("category")
 +                        .help("What category the lint belongs to")
 +                        .default_value("nursery")
 +                        .possible_values(&[
 +                            "style",
 +                            "correctness",
 +                            "suspicious",
 +                            "complexity",
 +                            "perf",
 +                            "pedantic",
 +                            "restriction",
 +                            "cargo",
 +                            "nursery",
 +                            "internal",
 +                            "internal_warn",
 +                        ])
 +                        .takes_value(true),
++                )
++                .arg(
++                    Arg::with_name("msrv")
++                        .long("msrv")
++                        .help("Add MSRV config code to the lint"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("setup")
 +                .about("Support for setting up your personal development environment")
 +                .setting(AppSettings::ArgRequiredElseHelp)
 +                .subcommand(
 +                    SubCommand::with_name("intellij")
 +                        .about("Alter dependencies so Intellij Rust can find rustc internals")
 +                        .arg(
 +                            Arg::with_name("rustc-repo-path")
 +                                .long("repo-path")
 +                                .short("r")
 +                                .help("The path to a rustc repo that will be used for setting the dependencies")
 +                                .takes_value(true)
 +                                .value_name("path")
 +                                .required(true),
 +                        ),
 +                )
 +                .subcommand(
 +                    SubCommand::with_name("git-hook")
 +                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
 +                        .arg(
 +                            Arg::with_name("force-override")
 +                                .long("force-override")
 +                                .short("f")
 +                                .help("Forces the override of an existing git pre-commit hook")
 +                                .required(false),
 +                        ),
 +                )
 +                .subcommand(
 +                    SubCommand::with_name("vscode-tasks")
 +                        .about("Add several tasks to vscode for formatting, validation and testing")
 +                        .arg(
 +                            Arg::with_name("force-override")
 +                                .long("force-override")
 +                                .short("f")
 +                                .help("Forces the override of existing vscode tasks")
 +                                .required(false),
 +                        ),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("remove")
 +                .about("Support for undoing changes done by the setup command")
 +                .setting(AppSettings::ArgRequiredElseHelp)
 +                .subcommand(SubCommand::with_name("git-hook").about("Remove any existing pre-commit git hook"))
 +                .subcommand(SubCommand::with_name("vscode-tasks").about("Remove any existing vscode tasks"))
 +                .subcommand(
 +                    SubCommand::with_name("intellij")
 +                        .about("Removes rustc source paths added via `cargo dev setup intellij`"),
 +                ),
 +        )
 +        .subcommand(
 +            SubCommand::with_name("serve")
 +                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
 +                .arg(
 +                    Arg::with_name("port")
 +                        .long("port")
 +                        .short("p")
 +                        .help("Local port for the http server")
 +                        .default_value("8000")
 +                        .validator_os(serve::validate_port),
 +                )
 +                .arg(Arg::with_name("lint").help("Which lint's page to load initially (optional)")),
 +        )
 +        .get_matches()
 +}
index 3a81aaba6de04054148e741ee8c551102b869b2c,0000000000000000000000000000000000000000..25320907bb492767f36da58f48e97d0b2d366477
mode 100644,000000..100644
--- /dev/null
@@@ -1,216 -1,0 +1,280 @@@
- pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>) -> io::Result<()> {
 +use crate::clippy_project_root;
++use indoc::indoc;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::io::{self, ErrorKind};
 +use std::path::{Path, PathBuf};
 +
 +struct LintData<'a> {
 +    pass: &'a str,
 +    name: &'a str,
 +    category: &'a str,
 +    project_root: PathBuf,
 +}
 +
 +trait Context {
 +    fn context<C: AsRef<str>>(self, text: C) -> Self;
 +}
 +
 +impl<T> Context for io::Result<T> {
 +    fn context<C: AsRef<str>>(self, text: C) -> Self {
 +        match self {
 +            Ok(t) => Ok(t),
 +            Err(e) => {
 +                let message = format!("{}: {}", text.as_ref(), e);
 +                Err(io::Error::new(ErrorKind::Other, message))
 +            },
 +        }
 +    }
 +}
 +
 +/// Creates the files required to implement and test a new lint and runs `update_lints`.
 +///
 +/// # Errors
 +///
 +/// This function errors out if the files couldn't be created or written to.
-     create_lint(&lint).context("Unable to create lint implementation")?;
++pub fn create(pass: Option<&str>, lint_name: Option<&str>, category: Option<&str>, msrv: bool) -> io::Result<()> {
 +    let lint = LintData {
 +        pass: pass.expect("`pass` argument is validated by clap"),
 +        name: lint_name.expect("`name` argument is validated by clap"),
 +        category: category.expect("`category` argument is validated by clap"),
 +        project_root: clippy_project_root(),
 +    };
 +
- fn create_lint(lint: &LintData<'_>) -> io::Result<()> {
-     let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
-         "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
-         "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
-         _ => {
-             unreachable!("`pass_type` should only ever be `early` or `late`!");
-         },
-     };
-     let camel_case_name = to_camel_case(lint.name);
-     let lint_contents = get_lint_file_contents(
-         pass_type,
-         pass_lifetimes,
-         lint.name,
-         &camel_case_name,
-         lint.category,
-         pass_import,
-         context_import,
-     );
++    create_lint(&lint, msrv).context("Unable to create lint implementation")?;
 +    create_test(&lint).context("Unable to create a test for the new lint")
 +}
 +
-         "#![warn(clippy::{})]
++fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
++    let lint_contents = get_lint_file_contents(lint, enable_msrv);
 +
 +    let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
 +    write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())
 +}
 +
 +fn create_test(lint: &LintData<'_>) -> io::Result<()> {
 +    fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
 +        let mut path = location.into().join(case);
 +        fs::create_dir(&path)?;
 +        write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
 +
 +        path.push("src");
 +        fs::create_dir(&path)?;
 +        let header = format!("// compile-flags: --crate-name={}", lint_name);
 +        write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
 +
 +        Ok(())
 +    }
 +
 +    if lint.category == "cargo" {
 +        let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
 +        let test_dir = lint.project_root.join(relative_test_dir);
 +        fs::create_dir(&test_dir)?;
 +
 +        create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
 +        create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")
 +    } else {
 +        let test_path = format!("tests/ui/{}.rs", lint.name);
 +        let test_contents = get_test_file_contents(lint.name, None);
 +        write_file(lint.project_root.join(test_path), test_contents)
 +    }
 +}
 +
 +fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
 +    fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
 +        OpenOptions::new()
 +            .write(true)
 +            .create_new(true)
 +            .open(path)?
 +            .write_all(contents)
 +    }
 +
 +    inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display()))
 +}
 +
 +fn to_camel_case(name: &str) -> String {
 +    name.split('_')
 +        .map(|s| {
 +            if s.is_empty() {
 +                String::from("")
 +            } else {
 +                [&s[0..1].to_uppercase(), &s[1..]].concat()
 +            }
 +        })
 +        .collect()
 +}
 +
 +fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
 +    let mut contents = format!(
- fn main() {{
-     // test code goes here
- }}
- ",
++        indoc! {"
++            #![warn(clippy::{})]
 +
-         r#"
- # {}
++            fn main() {{
++                // test code goes here
++            }}
++        "},
 +        lint_name
 +    );
 +
 +    if let Some(header) = header_commands {
 +        contents = format!("{}\n{}", header, contents);
 +    }
 +
 +    contents
 +}
 +
 +fn get_manifest_contents(lint_name: &str, hint: &str) -> String {
 +    format!(
- [package]
- name = "{}"
- version = "0.1.0"
- publish = false
++        indoc! {r#"
++            # {}
 +
- [workspace]
- "#,
++            [package]
++            name = "{}"
++            version = "0.1.0"
++            publish = false
 +
- fn get_lint_file_contents(
-     pass_type: &str,
-     pass_lifetimes: &str,
-     lint_name: &str,
-     camel_case_name: &str,
-     category: &str,
-     pass_import: &str,
-     context_import: &str,
- ) -> String {
-     format!(
-         "use rustc_lint::{{{type}, {context_import}}};
- use rustc_session::{{declare_lint_pass, declare_tool_lint}};
- {pass_import}
- declare_clippy_lint! {{
-     /// ### What it does
-     ///
-     /// ### Why is this bad?
-     ///
-     /// ### Example
-     /// ```rust
-     /// // example code where clippy issues a warning
-     /// ```
-     /// Use instead:
-     /// ```rust
-     /// // example code which does not raise clippy warning
-     /// ```
-     pub {name_upper},
-     {category},
-     \"default lint description\"
- }}
- declare_lint_pass!({name_camel} => [{name_upper}]);
- impl {type}{lifetimes} for {name_camel} {{}}
- ",
-         type=pass_type,
-         lifetimes=pass_lifetimes,
-         name_upper=lint_name.to_uppercase(),
-         name_camel=camel_case_name,
-         category=category,
-         pass_import=pass_import,
-         context_import=context_import
-     )
++            [workspace]
++        "#},
 +        hint, lint_name
 +    )
 +}
 +
++fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
++    let mut result = String::new();
++
++    let (pass_type, pass_lifetimes, pass_import, context_import) = match lint.pass {
++        "early" => ("EarlyLintPass", "", "use rustc_ast::ast::*;", "EarlyContext"),
++        "late" => ("LateLintPass", "<'_>", "use rustc_hir::*;", "LateContext"),
++        _ => {
++            unreachable!("`pass_type` should only ever be `early` or `late`!");
++        },
++    };
++
++    let lint_name = lint.name;
++    let pass_name = lint.pass;
++    let category = lint.category;
++    let name_camel = to_camel_case(lint.name);
++    let name_upper = lint_name.to_uppercase();
++
++    result.push_str(&if enable_msrv {
++        format!(
++            indoc! {"
++                use clippy_utils::msrvs;
++                {pass_import}
++                use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
++                use rustc_semver::RustcVersion;
++                use rustc_session::{{declare_tool_lint, impl_lint_pass}};
++
++            "},
++            pass_type = pass_type,
++            pass_import = pass_import,
++            context_import = context_import,
++        )
++    } else {
++        format!(
++            indoc! {"
++                {pass_import}
++                use rustc_lint::{{{context_import}, {pass_type}}};
++                use rustc_session::{{declare_lint_pass, declare_tool_lint}};
++
++            "},
++            pass_import = pass_import,
++            pass_type = pass_type,
++            context_import = context_import
++        )
++    });
++
++    result.push_str(&format!(
++        indoc! {"
++            declare_clippy_lint! {{
++                /// ### What it does
++                ///
++                /// ### Why is this bad?
++                ///
++                /// ### Example
++                /// ```rust
++                /// // example code where clippy issues a warning
++                /// ```
++                /// Use instead:
++                /// ```rust
++                /// // example code which does not raise clippy warning
++                /// ```
++                pub {name_upper},
++                {category},
++                \"default lint description\"
++            }}
++        "},
++        name_upper = name_upper,
++        category = category,
++    ));
++
++    result.push_str(&if enable_msrv {
++        format!(
++            indoc! {"
++                pub struct {name_camel} {{
++                    msrv: Option<RustcVersion>,
++                }}
++
++                impl {name_camel} {{
++                    #[must_use]
++                    pub fn new(msrv: Option<RustcVersion>) -> Self {{
++                        Self {{ msrv }}
++                    }}
++                }}
++
++                impl_lint_pass!({name_camel} => [{name_upper}]);
++
++                impl {pass_type}{pass_lifetimes} for {name_camel} {{
++                    extract_msrv_attr!({context_import});
++                }}
++
++                // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
++                //       e.g. store.register_{pass_name}_pass(move || Box::new({module_name}::{name_camel}::new(msrv)));
++                // TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed.
++                // TODO: Add MSRV test to `tests/ui/min_rust_version_attr.rs`.
++                // TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs`
++            "},
++            pass_type = pass_type,
++            pass_lifetimes = pass_lifetimes,
++            pass_name = pass_name,
++            name_upper = name_upper,
++            name_camel = name_camel,
++            module_name = lint_name,
++            context_import = context_import,
++        )
++    } else {
++        format!(
++            indoc! {"
++                declare_lint_pass!({name_camel} => [{name_upper}]);
++
++                impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
++                //
++                // TODO: Register the lint pass in `clippy_lints/src/lib.rs`,
++                //       e.g. store.register_{pass_name}_pass(|| Box::new({module_name}::{name_camel}));
++            "},
++            pass_type = pass_type,
++            pass_lifetimes = pass_lifetimes,
++            pass_name = pass_name,
++            name_upper = name_upper,
++            name_camel = name_camel,
++            module_name = lint_name,
++        )
++    });
++
++    result
 +}
 +
 +#[test]
 +fn test_camel_case() {
 +    let s = "a_lint";
 +    let s2 = to_camel_case(s);
 +    assert_eq!(s2, "ALint");
 +
 +    let name = "a_really_long_new_lint";
 +    let name2 = to_camel_case(name);
 +    assert_eq!(name2, "AReallyLongNewLint");
 +
 +    let name3 = "lint__name";
 +    let name4 = to_camel_case(name3);
 +    assert_eq!(name4, "LintName");
 +}
index 10d859988f6f2d55d52ac3cf035397be337aba0d,0000000000000000000000000000000000000000..23f58bc4915f919eed14c71f50684c77ff2b52c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,705 -1,0 +1,705 @@@
-             format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK.to_string()),
-             format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK.to_string()),
 +use itertools::Itertools;
 +use regex::Regex;
 +use std::collections::HashMap;
 +use std::ffi::OsStr;
 +use std::fs;
 +use std::lazy::SyncLazy;
 +use std::path::Path;
 +use walkdir::WalkDir;
 +
 +use crate::clippy_project_root;
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
 +    Regex::new(
 +        r#"(?x)
 +    declare_clippy_lint!\s*[\{(]
 +    (?:\s+///.*)*
 +    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
 +    (?P<cat>[a-z_]+)\s*,\s*
 +    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 +"#,
 +    )
 +    .unwrap()
 +});
 +
 +static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
 +    Regex::new(
 +        r#"(?x)
 +    declare_deprecated_lint!\s*[{(]\s*
 +    (?:\s+///.*)*
 +    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
 +    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
 +"#,
 +    )
 +    .unwrap()
 +});
 +static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
 +
 +static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
 +#[allow(clippy::too_many_lines)]
 +pub fn run(update_mode: UpdateMode) {
 +    let lint_list: Vec<Lint> = gather_all().collect();
 +
 +    let internal_lints = Lint::internal_lints(&lint_list);
 +    let deprecated_lints = Lint::deprecated_lints(&lint_list);
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let mut sorted_usable_lints = usable_lints.clone();
 +    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    let usable_lint_count = round_to_fifty(usable_lints.len());
 +
 +    let mut file_change = false;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("README.md"),
 +        &format!(
 +            r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
 +            DOCS_LINK
 +        ),
 +        "",
 +        true,
 +        update_mode == UpdateMode::Change,
 +        || {
 +            vec![format!(
 +                "[There are over {} lints included in this crate!]({})",
 +                usable_lint_count, DOCS_LINK
 +            )]
 +        },
 +    )
 +    .changed;
 +
 +    file_change |= replace_region_in_file(
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->",
 +        "<!-- end autogenerated links to lint list -->",
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
 +    )
 +    .changed;
 +
 +    // This has to be in lib.rs, otherwise rustfmt doesn't work
 +    file_change |= replace_region_in_file(
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "begin lints modules",
 +        "end lints modules",
 +        false,
 +        update_mode == UpdateMode::Change,
 +        || gen_modules_list(usable_lints.iter()),
 +    )
 +    .changed;
 +
 +    if file_change && update_mode == UpdateMode::Check {
 +        exit_with_failure();
 +    }
 +
 +    process_file(
 +        "clippy_lints/src/lib.register_lints.rs",
 +        update_mode,
 +        &gen_register_lint_list(internal_lints.iter(), usable_lints.iter()),
 +    );
 +    process_file(
 +        "clippy_lints/src/lib.deprecated.rs",
 +        update_mode,
 +        &gen_deprecated(deprecated_lints.iter()),
 +    );
 +
 +    let all_group_lints = usable_lints.iter().filter(|l| {
 +        matches!(
 +            &*l.group,
 +            "correctness" | "suspicious" | "style" | "complexity" | "perf"
 +        )
 +    });
 +    let content = gen_lint_group_list("all", all_group_lints);
 +    process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
 +
 +    for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
 +        let content = gen_lint_group_list(&lint_group, lints.iter());
 +        process_file(
 +            &format!("clippy_lints/src/lib.register_{}.rs", lint_group),
 +            update_mode,
 +            &content,
 +        );
 +    }
 +}
 +
 +pub fn print_lints() {
 +    let lint_list: Vec<Lint> = gather_all().collect();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        if lint_group == "Deprecated" {
 +            continue;
 +        }
 +        println!("\n## {}", lint_group);
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {} lints", usable_lint_count);
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
 +
 +fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
 +    if update_mode == UpdateMode::Check {
 +        let old_content =
 +            fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.as_ref().display(), e));
 +        if content != old_content {
 +            exit_with_failure();
 +        }
 +    } else {
 +        fs::write(&path, content.as_bytes())
 +            .unwrap_or_else(|e| panic!("Cannot write to {}: {}", path.as_ref().display(), e));
 +    }
 +}
 +
 +fn exit_with_failure() {
 +    println!(
 +        "Not all lints defined properly. \
 +                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
 +    );
 +    std::process::exit(1);
 +}
 +
 +/// Lint data parsed from the Clippy source code.
 +#[derive(Clone, PartialEq, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    deprecation: Option<String>,
 +    module: String,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.to_string(),
 +            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
 +            deprecation: deprecation.map(ToString::to_string),
 +            module: module.to_string(),
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns all deprecated lints
 +    #[must_use]
 +    fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +/// Generates the code for registering a group
 +fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
 +    let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +
 +    output.push_str(&format!(
 +        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![\n",
 +        group_name
 +    ));
 +    for (module, name) in details {
 +        output.push_str(&format!("    LintId::of({}::{}),\n", module, name));
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +/// Generates the module declarations for `lints`
 +#[must_use]
 +fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .map(|l| &l.module)
 +        .unique()
 +        .map(|module| format!("mod {};", module))
 +        .sorted()
 +        .collect::<Vec<String>>()
 +}
 +
 +/// Generates the list of lint links at the bottom of the CHANGELOG
 +#[must_use]
 +fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
 +    lints
 +        .sorted_by_key(|l| &l.name)
 +        .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
 +        .collect()
 +}
 +
 +/// Generates the `register_removed` code
 +#[must_use]
 +fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("{\n");
 +    for Lint { name, deprecation, .. } in lints {
 +        output.push_str(&format!(
 +            concat!(
 +                "    store.register_removed(\n",
 +                "        \"clippy::{}\",\n",
 +                "        \"{}\",\n",
 +                "    );\n"
 +            ),
 +            name,
 +            deprecation.as_ref().expect("`lints` are deprecated")
 +        ));
 +    }
 +    output.push_str("}\n");
 +
 +    output
 +}
 +
 +/// Generates the code for registering lints
 +#[must_use]
 +fn gen_register_lint_list<'a>(
 +    internal_lints: impl Iterator<Item = &'a Lint>,
 +    usable_lints: impl Iterator<Item = &'a Lint>,
 +) -> String {
 +    let mut details: Vec<_> = internal_lints
 +        .map(|l| (false, &l.module, l.name.to_uppercase()))
 +        .chain(usable_lints.map(|l| (true, &l.module, l.name.to_uppercase())))
 +        .collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("store.register_lints(&[\n");
 +
 +    for (is_public, module_name, lint_name) in details {
 +        if !is_public {
 +            output.push_str("    #[cfg(feature = \"internal-lints\")]\n");
 +        }
 +        output.push_str(&format!("    {}::{},\n", module_name, lint_name));
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +/// Gathers all files in `src/clippy_lints` and gathers all lints inside
 +fn gather_all() -> impl Iterator<Item = Lint> {
 +    lint_files().flat_map(|f| gather_from_file(&f))
 +}
 +
 +fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
 +    let content = fs::read_to_string(dir_entry.path()).unwrap();
 +    let path = dir_entry.path();
 +    let filename = path.file_stem().unwrap();
 +    let path_buf = path.with_file_name(filename);
 +    let mut rel_path = path_buf
 +        .strip_prefix(clippy_project_root().join("clippy_lints/src"))
 +        .expect("only files in `clippy_lints/src` should be looked at");
 +    // If the lints are stored in mod.rs, we get the module name from
 +    // the containing directory:
 +    if filename == "mod" {
 +        rel_path = rel_path.parent().unwrap();
 +    }
 +
 +    let module = rel_path
 +        .components()
 +        .map(|c| c.as_os_str().to_str().unwrap())
 +        .collect::<Vec<_>>()
 +        .join("::");
 +
 +    parse_contents(&content, &module)
 +}
 +
 +fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
 +    let lints = DEC_CLIPPY_LINT_RE
 +        .captures_iter(content)
 +        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
 +    let deprecated = DEC_DEPRECATED_LINT_RE
 +        .captures_iter(content)
 +        .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
 +    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
 +    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
 +}
 +
 +/// Collects all .rs files in the `clippy_lints/src` directory
 +fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
 +    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
 +    // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
 +    let path = clippy_project_root().join("clippy_lints/src");
 +    WalkDir::new(path)
 +        .into_iter()
 +        .filter_map(Result::ok)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +}
 +
 +/// Whether a file has had its text changed or not
 +#[derive(PartialEq, Debug)]
 +struct FileChange {
 +    changed: bool,
 +    new_lines: String,
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +fn replace_region_in_file<F>(
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    replace_start: bool,
 +    write_back: bool,
 +    replacements: F,
 +) -> FileChange
 +where
 +    F: FnOnce() -> Vec<String>,
 +{
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
 +    let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
 +
 +    if write_back {
 +        if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
 +            panic!("Cannot write to {}: {}", path.display(), e);
 +        }
 +    }
 +    file_change
 +}
 +
 +/// Replaces a region in a text delimited by two lines matching regexes.
 +///
 +/// * `text` is the input text on which you want to perform the replacement
 +/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
 +///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
 +/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
 +///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
 +/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
 +///   delimiter line is never replaced.
 +/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
 +///
 +/// If you want to perform the replacement on files instead of already parsed text,
 +/// use `replace_region_in_file`.
 +///
 +/// # Example
 +///
 +/// ```ignore
 +/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
 +/// let result =
 +///     replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
 +///         vec!["a different".to_string(), "text".to_string()]
 +///     })
 +///     .new_lines;
 +/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
 +/// ```
 +///
 +/// # Panics
 +///
 +/// Panics if start or end is not valid regex
 +fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
 +where
 +    F: FnOnce() -> Vec<String>,
 +{
 +    let replace_it = replacements();
 +    let mut in_old_region = false;
 +    let mut found = false;
 +    let mut new_lines = vec![];
 +    let start = Regex::new(start).unwrap();
 +    let end = Regex::new(end).unwrap();
 +
 +    for line in text.lines() {
 +        if in_old_region {
 +            if end.is_match(line) {
 +                in_old_region = false;
 +                new_lines.extend(replace_it.clone());
 +                new_lines.push(line.to_string());
 +            }
 +        } else if start.is_match(line) {
 +            if !replace_start {
 +                new_lines.push(line.to_string());
 +            }
 +            in_old_region = true;
 +            found = true;
 +        } else {
 +            new_lines.push(line.to_string());
 +        }
 +    }
 +
 +    if !found {
 +        // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
 +        // given text or file. Most likely this is an error on the programmer's side and the Regex
 +        // is incorrect.
 +        eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
 +        std::process::exit(1);
 +    }
 +
 +    let mut new_lines = new_lines.join("\n");
 +    if text.ends_with('\n') {
 +        new_lines.push('\n');
 +    }
 +    let changed = new_lines != text;
 +    FileChange { changed, new_lines }
 +}
 +
 +#[test]
 +fn test_parse_contents() {
 +    let result: Vec<Lint> = parse_contents(
 +        r#"
 +declare_clippy_lint! {
 +    pub PTR_ARG,
 +    style,
 +    "really long \
 +     text"
 +}
 +
 +declare_clippy_lint!{
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "single line"
 +}
 +
 +/// some doc comment
 +declare_deprecated_lint! {
 +    pub SHOULD_ASSERT_EQ,
 +    "`assert!()` will be more flexible with RFC 2011"
 +}
 +    "#,
 +        "module_name",
 +    )
 +    .collect();
 +
 +    let expected = vec![
 +        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
 +        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
 +        Lint::new(
 +            "should_assert_eq",
 +            "Deprecated",
 +            "`assert!()` will be more flexible with RFC 2011",
 +            Some("`assert!()` will be more flexible with RFC 2011"),
 +            "module_name",
 +        ),
 +    ];
 +    assert_eq!(expected, result);
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    #[test]
 +    fn test_replace_region() {
 +        let text = "\nabc\n123\n789\ndef\nghi";
 +        let expected = FileChange {
 +            changed: true,
 +            new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
 +        };
 +        let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
 +            vec!["hello world".to_string()]
 +        });
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_replace_region_with_start() {
 +        let text = "\nabc\n123\n789\ndef\nghi";
 +        let expected = FileChange {
 +            changed: true,
 +            new_lines: "\nhello world\ndef\nghi".to_string(),
 +        };
 +        let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
 +            vec!["hello world".to_string()]
 +        });
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_replace_region_no_changes() {
 +        let text = "123\n456\n789";
 +        let expected = FileChange {
 +            changed: false,
 +            new_lines: "123\n456\n789".to_string(),
 +        };
 +        let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
 +            Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
 +            Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
 +            Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "abc",
 +            None,
 +            "module_name",
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +            Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
 +            Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
 +                Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +                Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
 +        );
 +        assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_changelog_lint_list() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +            Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
 +        ];
 +        let expected = vec![
++            format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK),
++            format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK),
 +        ];
 +        assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_deprecated() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq",
 +                "group1",
 +                "abc",
 +                Some("has been superseded by should_assert_eq2"),
 +                "module_name",
 +            ),
 +            Lint::new(
 +                "another_deprecated",
 +                "group2",
 +                "abc",
 +                Some("will be removed"),
 +                "module_name",
 +            ),
 +        ];
 +
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "{",
 +                "    store.register_removed(",
 +                "        \"clippy::should_assert_eq\",",
 +                "        \"has been superseded by should_assert_eq2\",",
 +                "    );",
 +                "    store.register_removed(",
 +                "        \"clippy::another_deprecated\",",
 +                "        \"will be removed\",",
 +                "    );",
 +                "}",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        assert_eq!(expected, gen_deprecated(lints.iter()));
 +    }
 +
 +    #[test]
 +    #[should_panic]
 +    fn test_gen_deprecated_fail() {
 +        let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
 +        let _deprecated_lints = gen_deprecated(lints.iter());
 +    }
 +
 +    #[test]
 +    fn test_gen_modules_list() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +            Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
 +        ];
 +        let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
 +        assert_eq!(expected, gen_modules_list(lints.iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_lint_group_list() {
 +        let lints = vec![
 +            Lint::new("abc", "group1", "abc", None, "module_name"),
 +            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
 +            Lint::new("internal", "internal_style", "abc", None, "module_name"),
 +        ];
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "store.register_group(true, \"clippy::group1\", Some(\"clippy_group1\"), vec![",
 +                "    LintId::of(module_name::ABC),",
 +                "    LintId::of(module_name::INTERNAL),",
 +                "    LintId::of(module_name::SHOULD_ASSERT_EQ),",
 +                "])",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        let result = gen_lint_group_list("group1", lints.iter());
 +
 +        assert_eq!(expected, result);
 +    }
 +}
index 7900dc6d0414136a0ef27a74f787c7d97cd6fc05,0000000000000000000000000000000000000000..aaf9ac83d49005dad0df8a05de2dc7818dcedd06
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,38 @@@
- version = "0.1.57"
 +[package]
 +name = "clippy_lints"
++version = "0.1.58"
 +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.12"
 +clippy_utils = { path = "../clippy_utils" }
 +if_chain = "1.0"
 +itertools = "0.10"
 +pulldown-cmark = { version = "0.8", 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 = "0.11"
 +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-lints = ["clippy_utils/internal-lints"]
 +metadata-collector-lint = ["serde_json", "clippy_utils/metadata-collector-lint"]
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..03621887a34a67e1771da65174ad9f0cd452f8f5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use rustc_errors::Applicability;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, Ty};
++
++use super::FN_TO_NUMERIC_CAST_ANY;
++
++pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
++    // We allow casts from any function type to any function type.
++    match cast_to.kind() {
++        ty::FnDef(..) | ty::FnPtr(..) => return,
++        _ => { /* continue to checks */ },
++    }
++
++    match cast_from.kind() {
++        ty::FnDef(..) | ty::FnPtr(_) => {
++            let mut applicability = Applicability::MaybeIncorrect;
++            let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
++
++            span_lint_and_sugg(
++                cx,
++                FN_TO_NUMERIC_CAST_ANY,
++                expr.span,
++                &format!("casting function pointer `{}` to `{}`", from_snippet, cast_to),
++                "did you mean to invoke the function?",
++                format!("{}() as {}", from_snippet, cast_to),
++                applicability,
++            );
++        },
++        _ => {},
++    }
++}
index 27e1bea799353da4bd3cbb3076ef4d788934264c,0000000000000000000000000000000000000000..f0800c6a6f18f788bbb2804b20aaba1b8b362fc6
mode 100644,000000..100644
--- /dev/null
@@@ -1,406 -1,0 +1,445 @@@
 +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;
 +    /// ```
 +    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
 +    /// ```
 +    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
 +    /// }
 +    /// ```
 +    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`
 +    /// ```
 +    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)
 +    /// }
 +    /// ```
 +    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;
 +    /// ```
 +    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>();
 +    /// ```
 +    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;
 +    /// ```
 +    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;
 +    /// ```
 +    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;
++    /// ```
++    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;
 +    ///     }
 +    /// }
 +    /// ```
 +    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'
 +    /// ```
 +    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>();
 +    /// ```
 +    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`"
 +}
 +
 +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,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Casts {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        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_from.is_numeric() && cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
 +                cast_possible_truncation::check(cx, expr, cast_from, cast_to);
 +                cast_possible_wrap::check(cx, expr, cast_from, cast_to);
 +                cast_precision_loss::check(cx, expr, cast_from, cast_to);
 +                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
 +                cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
 +            }
 +        }
 +
 +        cast_ref_to_mut::check(cx, expr);
 +        cast_ptr_alignment::check(cx, expr);
 +        char_lit_as_u8::check(cx, expr);
 +        ptr_as_ptr::check(cx, expr, &self.msrv);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index b7385dcfbca19a38a4165a1f676d1f8c52c577bf,0000000000000000000000000000000000000000..8abf10c0d1c2d174b1380b5a3c01d5ef5b17d3ee
mode 100644,000000..100644
--- /dev/null
@@@ -1,648 -1,0 +1,646 @@@
-         let moved_end = block
-             .expr
-             .map_or_else(
-                 || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span),
-                 |expr| expr.span.source_callsite(),
-             );
 +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 +use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
 +use clippy_utils::{
 +    both, count_eq, eq_expr_value, get_enclosing_block, get_parent_expr, if_sequence, in_macro, is_else_clause,
 +    is_lint_allowed, search_same, ContainsName, SpanlessEq, SpanlessHash,
 +};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::{Applicability, DiagnosticBuilder};
 +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 +use rustc_hir::{Block, Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::map::Map;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{source_map::Span, symbol::Symbol, BytePos};
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same condition.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if a == b {
 +    ///     …
 +    /// } else if a == b {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// Note that this lint ignores all conditions with a function call as it could
 +    /// have side effects:
 +    ///
 +    /// ```ignore
 +    /// if foo() {
 +    ///     …
 +    /// } else if foo() { // not linted
 +    ///     …
 +    /// }
 +    /// ```
 +    pub IFS_SAME_COND,
 +    correctness,
 +    "consecutive `if`s with the same condition"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for consecutive `if`s with the same function call.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    /// Despite the fact that function can have side effects and `if` works as
 +    /// intended, such an approach is implicit and can be considered a "code smell".
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == bar {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// This probably should be:
 +    /// ```ignore
 +    /// if foo() == bar {
 +    ///     …
 +    /// } else if foo() == baz {
 +    ///     …
 +    /// }
 +    /// ```
 +    ///
 +    /// or if the original code was not a typo and called function mutates a state,
 +    /// consider move the mutation out of the `if` condition to avoid similarity to
 +    /// a copy & paste error:
 +    ///
 +    /// ```ignore
 +    /// let first = foo();
 +    /// if first == bar {
 +    ///     …
 +    /// } else {
 +    ///     let second = foo();
 +    ///     if second == bar {
 +    ///     …
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SAME_FUNCTIONS_IN_IF_CONDITION,
 +    pedantic,
 +    "consecutive `if`s with the same function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `if/else` with the same body as the *then* part
 +    /// and the *else* part.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is probably a copy & paste error.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     42
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    pub IF_SAME_THEN_ELSE,
 +    correctness,
 +    "`if` with the same `then` and `else` blocks"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks if the `if` and `else` block contain shared code that can be
 +    /// moved out of the blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate code is less maintainable.
 +    ///
 +    /// ### Known problems
 +    /// * The lint doesn't check if the moved expressions modify values that are beeing used in
 +    ///   the if condition. The suggestion can in that case modify the behavior of the program.
 +    ///   See [rust-clippy#7452](https://github.com/rust-lang/rust-clippy/issues/7452)
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let foo = if … {
 +    ///     println!("Hello World");
 +    ///     13
 +    /// } else {
 +    ///     println!("Hello World");
 +    ///     42
 +    /// };
 +    /// ```
 +    ///
 +    /// Could be written as:
 +    /// ```ignore
 +    /// println!("Hello World");
 +    /// let foo = if … {
 +    ///     13
 +    /// } else {
 +    ///     42
 +    /// };
 +    /// ```
 +    pub BRANCHES_SHARING_CODE,
 +    nursery,
 +    "`if` statement with shared code in all blocks"
 +}
 +
 +declare_lint_pass!(CopyAndPaste => [
 +    IFS_SAME_COND,
 +    SAME_FUNCTIONS_IN_IF_CONDITION,
 +    IF_SAME_THEN_ELSE,
 +    BRANCHES_SHARING_CODE
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !expr.span.from_expansion() {
 +            if let ExprKind::If(_, _, _) = expr.kind {
 +                // skip ifs directly in else, it will be checked in the parent if
 +                if let Some(&Expr {
 +                    kind: ExprKind::If(_, _, Some(else_expr)),
 +                    ..
 +                }) = get_parent_expr(cx, expr)
 +                {
 +                    if else_expr.hir_id == expr.hir_id {
 +                        return;
 +                    }
 +                }
 +
 +                let (conds, blocks) = if_sequence(expr);
 +                // Conditions
 +                lint_same_cond(cx, &conds);
 +                lint_same_fns_in_if_cond(cx, &conds);
 +                // Block duplication
 +                lint_same_then_else(cx, &blocks, conds.len() == blocks.len(), expr);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of `BRANCHES_SHARING_CODE` and `IF_SAME_THEN_ELSE` if the blocks are equal.
 +fn lint_same_then_else<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    blocks: &[&Block<'tcx>],
 +    has_conditional_else: bool,
 +    expr: &'tcx Expr<'_>,
 +) {
 +    // We only lint ifs with multiple blocks
 +    if blocks.len() < 2 || is_else_clause(cx.tcx, expr) {
 +        return;
 +    }
 +
 +    // Check if each block has shared code
 +    let has_expr = blocks[0].expr.is_some();
 +
 +    let (start_eq, mut end_eq, expr_eq) = if let Some(block_eq) = scan_block_for_eq(cx, blocks) {
 +        (block_eq.start_eq, block_eq.end_eq, block_eq.expr_eq)
 +    } else {
 +        return;
 +    };
 +
 +    // BRANCHES_SHARING_CODE prerequisites
 +    if has_conditional_else || (start_eq == 0 && end_eq == 0 && (has_expr && !expr_eq)) {
 +        return;
 +    }
 +
 +    // Only the start is the same
 +    if start_eq != 0 && end_eq == 0 && (!has_expr || !expr_eq) {
 +        let block = blocks[0];
 +        let start_stmts = block.stmts.split_at(start_eq).0;
 +
 +        let mut start_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in start_stmts {
 +            intravisit::walk_stmt(&mut start_walker, stmt);
 +        }
 +
 +        emit_branches_sharing_code_lint(
 +            cx,
 +            start_eq,
 +            0,
 +            false,
 +            check_for_warn_of_moved_symbol(cx, &start_walker.def_symbols, expr),
 +            blocks,
 +            expr,
 +        );
 +    } else if end_eq != 0 || (has_expr && expr_eq) {
 +        let block = blocks[blocks.len() - 1];
 +        let (start_stmts, block_stmts) = block.stmts.split_at(start_eq);
 +        let (block_stmts, end_stmts) = block_stmts.split_at(block_stmts.len() - end_eq);
 +
 +        // Scan start
 +        let mut start_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in start_stmts {
 +            intravisit::walk_stmt(&mut start_walker, stmt);
 +        }
 +        let mut moved_syms = start_walker.def_symbols;
 +
 +        // Scan block
 +        let mut block_walker = UsedValueFinderVisitor::new(cx);
 +        for stmt in block_stmts {
 +            intravisit::walk_stmt(&mut block_walker, stmt);
 +        }
 +        let mut block_defs = block_walker.defs;
 +
 +        // Scan moved stmts
 +        let mut moved_start: Option<usize> = None;
 +        let mut end_walker = UsedValueFinderVisitor::new(cx);
 +        for (index, stmt) in end_stmts.iter().enumerate() {
 +            intravisit::walk_stmt(&mut end_walker, stmt);
 +
 +            for value in &end_walker.uses {
 +                // Well we can't move this and all prev statements. So reset
 +                if block_defs.contains(value) {
 +                    moved_start = Some(index + 1);
 +                    end_walker.defs.drain().for_each(|x| {
 +                        block_defs.insert(x);
 +                    });
 +
 +                    end_walker.def_symbols.clear();
 +                }
 +            }
 +
 +            end_walker.uses.clear();
 +        }
 +
 +        if let Some(moved_start) = moved_start {
 +            end_eq -= moved_start;
 +        }
 +
 +        let end_linable = block.expr.map_or_else(
 +            || end_eq != 0,
 +            |expr| {
 +                intravisit::walk_expr(&mut end_walker, expr);
 +                end_walker.uses.iter().any(|x| !block_defs.contains(x))
 +            },
 +        );
 +
 +        if end_linable {
 +            end_walker.def_symbols.drain().for_each(|x| {
 +                moved_syms.insert(x);
 +            });
 +        }
 +
 +        emit_branches_sharing_code_lint(
 +            cx,
 +            start_eq,
 +            end_eq,
 +            end_linable,
 +            check_for_warn_of_moved_symbol(cx, &moved_syms, expr),
 +            blocks,
 +            expr,
 +        );
 +    }
 +}
 +
 +struct BlockEqual {
 +    /// The amount statements that are equal from the start
 +    start_eq: usize,
 +    /// The amount statements that are equal from the end
 +    end_eq: usize,
 +    ///  An indication if the block expressions are the same. This will also be true if both are
 +    /// `None`
 +    expr_eq: bool,
 +}
 +
 +/// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to
 +/// abort any further processing and avoid duplicate lint triggers.
 +fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> {
 +    let mut start_eq = usize::MAX;
 +    let mut end_eq = usize::MAX;
 +    let mut expr_eq = true;
 +    let mut iter = blocks.windows(2);
 +    while let Some(&[win0, win1]) = iter.next() {
 +        let l_stmts = win0.stmts;
 +        let r_stmts = win1.stmts;
 +
 +        // `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
 +        // The comparison therefore needs to be done in a way that builds the correct context.
 +        let mut evaluator = SpanlessEq::new(cx);
 +        let mut evaluator = evaluator.inter_expr();
 +
 +        let current_start_eq = count_eq(&mut l_stmts.iter(), &mut r_stmts.iter(), |l, r| evaluator.eq_stmt(l, r));
 +
 +        let current_end_eq = {
 +            // We skip the middle statements which can't be equal
 +            let end_comparison_count = l_stmts.len().min(r_stmts.len()) - current_start_eq;
 +            let it1 = l_stmts.iter().skip(l_stmts.len() - end_comparison_count);
 +            let it2 = r_stmts.iter().skip(r_stmts.len() - end_comparison_count);
 +            it1.zip(it2)
 +                .fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
 +        };
 +        let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r));
 +
 +        // IF_SAME_THEN_ELSE
 +        if_chain! {
 +            if block_expr_eq;
 +            if l_stmts.len() == r_stmts.len();
 +            if l_stmts.len() == current_start_eq;
 +            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id);
 +            if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id);
 +            then {
 +                span_lint_and_note(
 +                    cx,
 +                    IF_SAME_THEN_ELSE,
 +                    win0.span,
 +                    "this `if` has identical blocks",
 +                    Some(win1.span),
 +                    "same as this",
 +                );
 +
 +                return None;
 +            }
 +        }
 +
 +        start_eq = start_eq.min(current_start_eq);
 +        end_eq = end_eq.min(current_end_eq);
 +        expr_eq &= block_expr_eq;
 +    }
 +
 +    if !expr_eq {
 +        end_eq = 0;
 +    }
 +
 +    // Check if the regions are overlapping. Set `end_eq` to prevent the overlap
 +    let min_block_size = blocks.iter().map(|x| x.stmts.len()).min().unwrap();
 +    if (start_eq + end_eq) > min_block_size {
 +        end_eq = min_block_size - start_eq;
 +    }
 +
 +    Some(BlockEqual {
 +        start_eq,
 +        end_eq,
 +        expr_eq,
 +    })
 +}
 +
 +fn check_for_warn_of_moved_symbol(
 +    cx: &LateContext<'tcx>,
 +    symbols: &FxHashSet<Symbol>,
 +    if_expr: &'tcx Expr<'_>,
 +) -> bool {
 +    get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
 +        let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
 +
 +        symbols
 +            .iter()
 +            .filter(|sym| !sym.as_str().starts_with('_'))
 +            .any(move |sym| {
 +                let mut walker = ContainsName {
 +                    name: *sym,
 +                    result: false,
 +                };
 +
 +                // Scan block
 +                block
 +                    .stmts
 +                    .iter()
 +                    .filter(|stmt| !ignore_span.overlaps(stmt.span))
 +                    .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
 +
 +                if let Some(expr) = block.expr {
 +                    intravisit::walk_expr(&mut walker, expr);
 +                }
 +
 +                walker.result
 +            })
 +    })
 +}
 +
 +fn emit_branches_sharing_code_lint(
 +    cx: &LateContext<'tcx>,
 +    start_stmts: usize,
 +    end_stmts: usize,
 +    lint_end: bool,
 +    warn_about_moved_symbol: bool,
 +    blocks: &[&Block<'tcx>],
 +    if_expr: &'tcx Expr<'_>,
 +) {
 +    if start_stmts == 0 && !lint_end {
 +        return;
 +    }
 +
 +    // (help, span, suggestion)
 +    let mut suggestions: Vec<(&str, Span, String)> = vec![];
 +    let mut add_expr_note = false;
 +
 +    // Construct suggestions
 +    let sm = cx.sess().source_map();
 +    if start_stmts > 0 {
 +        let block = blocks[0];
 +        let span_start = first_line_of_span(cx, if_expr.span).shrink_to_lo();
 +        let span_end = sm.stmt_span(block.stmts[start_stmts - 1].span, block.span);
 +
 +        let cond_span = first_line_of_span(cx, if_expr.span).until(block.span);
 +        let cond_snippet = reindent_multiline(snippet(cx, cond_span, "_"), false, None);
 +        let cond_indent = indent_of(cx, cond_span);
 +        let moved_span = block.stmts[0].span.source_callsite().to(span_end);
 +        let moved_snippet = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
 +        let suggestion = moved_snippet.to_string() + "\n" + &cond_snippet + "{";
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, cond_indent);
 +
 +        let span = span_start.to(span_end);
 +        suggestions.push(("start", span, suggestion.to_string()));
 +    }
 +
 +    if lint_end {
 +        let block = blocks[blocks.len() - 1];
 +        let span_end = block.span.shrink_to_hi();
 +
 +        let moved_start = if end_stmts == 0 && block.expr.is_some() {
 +            block.expr.unwrap().span.source_callsite()
 +        } else {
 +            sm.stmt_span(block.stmts[block.stmts.len() - end_stmts].span, block.span)
 +        };
++        let moved_end = block.expr.map_or_else(
++            || sm.stmt_span(block.stmts[block.stmts.len() - 1].span, block.span),
++            |expr| expr.span.source_callsite(),
++        );
 +
 +        let moved_span = moved_start.to(moved_end);
 +        let moved_snipped = reindent_multiline(snippet(cx, moved_span, "_"), true, None);
 +        let indent = indent_of(cx, if_expr.span.shrink_to_hi());
 +        let suggestion = "}\n".to_string() + &moved_snipped;
 +        let suggestion = reindent_multiline(Cow::Borrowed(&suggestion), true, indent);
 +
 +        let mut span = moved_start.to(span_end);
 +        // Improve formatting if the inner block has indention (i.e. normal Rust formatting)
 +        let test_span = Span::new(span.lo() - BytePos(4), span.lo(), span.ctxt(), span.parent());
 +        if snippet_opt(cx, test_span)
 +            .map(|snip| snip == "    ")
 +            .unwrap_or_default()
 +        {
 +            span = span.with_lo(test_span.lo());
 +        }
 +
 +        suggestions.push(("end", span, suggestion.to_string()));
 +        add_expr_note = !cx.typeck_results().expr_ty(if_expr).is_unit();
 +    }
 +
 +    let add_optional_msgs = |diag: &mut DiagnosticBuilder<'_>| {
 +        if add_expr_note {
 +            diag.note("The end suggestion probably needs some adjustments to use the expression result correctly");
 +        }
 +
 +        if warn_about_moved_symbol {
 +            diag.warn("Some moved values might need to be renamed to avoid wrong references");
 +        }
 +    };
 +
 +    // Emit lint
 +    if suggestions.len() == 1 {
 +        let (place_str, span, sugg) = suggestions.pop().unwrap();
 +        let msg = format!("all if blocks contain the same code at the {}", place_str);
 +        let help = format!("consider moving the {} statements out like this", place_str);
 +        span_lint_and_then(cx, BRANCHES_SHARING_CODE, span, msg.as_str(), |diag| {
 +            diag.span_suggestion(span, help.as_str(), sugg, Applicability::Unspecified);
 +
 +            add_optional_msgs(diag);
 +        });
 +    } else if suggestions.len() == 2 {
 +        let (_, end_span, end_sugg) = suggestions.pop().unwrap();
 +        let (_, start_span, start_sugg) = suggestions.pop().unwrap();
 +        span_lint_and_then(
 +            cx,
 +            BRANCHES_SHARING_CODE,
 +            start_span,
 +            "all if blocks contain the same code at the start and the end. Here at the start",
 +            move |diag| {
 +                diag.span_note(end_span, "and here at the end");
 +
 +                diag.span_suggestion(
 +                    start_span,
 +                    "consider moving the start statements out like this",
 +                    start_sugg,
 +                    Applicability::Unspecified,
 +                );
 +
 +                diag.span_suggestion(
 +                    end_span,
 +                    "and consider moving the end statements out like this",
 +                    end_sugg,
 +                    Applicability::Unspecified,
 +                );
 +
 +                add_optional_msgs(diag);
 +            },
 +        );
 +    }
 +}
 +
 +/// This visitor collects `HirId`s and Symbols of defined symbols and `HirId`s of used values.
 +struct UsedValueFinderVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +
 +    /// The `HirId`s of defined values in the scanned statements
 +    defs: FxHashSet<HirId>,
 +
 +    /// The Symbols of the defined symbols in the scanned statements
 +    def_symbols: FxHashSet<Symbol>,
 +
 +    /// The `HirId`s of the used values
 +    uses: FxHashSet<HirId>,
 +}
 +
 +impl<'a, 'tcx> UsedValueFinderVisitor<'a, 'tcx> {
 +    fn new(cx: &'a LateContext<'tcx>) -> Self {
 +        UsedValueFinderVisitor {
 +            cx,
 +            defs: FxHashSet::default(),
 +            def_symbols: FxHashSet::default(),
 +            uses: FxHashSet::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UsedValueFinderVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::All(self.cx.tcx.hir())
 +    }
 +
 +    fn visit_local(&mut self, l: &'tcx rustc_hir::Local<'tcx>) {
 +        let local_id = l.pat.hir_id;
 +        self.defs.insert(local_id);
 +
 +        if let Some(sym) = l.pat.simple_ident() {
 +            self.def_symbols.insert(sym.name);
 +        }
 +
 +        if let Some(expr) = l.init {
 +            intravisit::walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn visit_qpath(&mut self, qpath: &'tcx rustc_hir::QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
 +        if let rustc_hir::QPath::Resolved(_, path) = *qpath {
 +            if path.segments.len() == 1 {
 +                if let rustc_hir::def::Res::Local(var) = self.cx.qpath_res(qpath, id) {
 +                    self.uses.insert(var);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of `IFS_SAME_COND`.
 +fn lint_same_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(expr);
 +        h.finish()
 +    };
 +
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool { eq_expr_value(cx, lhs, rhs) };
 +
 +    for (i, j) in search_same(conds, hash, eq) {
 +        span_lint_and_note(
 +            cx,
 +            IFS_SAME_COND,
 +            j.span,
 +            "this `if` has the same condition as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
 +
 +/// Implementation of `SAME_FUNCTIONS_IN_IF_CONDITION`.
 +fn lint_same_fns_in_if_cond(cx: &LateContext<'_>, conds: &[&Expr<'_>]) {
 +    let hash: &dyn Fn(&&Expr<'_>) -> u64 = &|expr| -> u64 {
 +        let mut h = SpanlessHash::new(cx);
 +        h.hash_expr(expr);
 +        h.finish()
 +    };
 +
 +    let eq: &dyn Fn(&&Expr<'_>, &&Expr<'_>) -> bool = &|&lhs, &rhs| -> bool {
 +        // Do not lint if any expr originates from a macro
 +        if in_macro(lhs.span) || in_macro(rhs.span) {
 +            return false;
 +        }
 +        // Do not spawn warning if `IFS_SAME_COND` already produced it.
 +        if eq_expr_value(cx, lhs, rhs) {
 +            return false;
 +        }
 +        SpanlessEq::new(cx).eq_expr(lhs, rhs)
 +    };
 +
 +    for (i, j) in search_same(conds, hash, eq) {
 +        span_lint_and_note(
 +            cx,
 +            SAME_FUNCTIONS_IN_IF_CONDITION,
 +            j.span,
 +            "this `if` has the same function call as a previous `if`",
 +            Some(i.span),
 +            "same as this",
 +        );
 +    }
 +}
index db8f2171348f7046ffa96bae0a3a2519471a1e4d,0000000000000000000000000000000000000000..cde27d3ad2a0ce3def12b28ab0e5ebfddb640489
mode 100644,000000..100644
--- /dev/null
@@@ -1,282 -1,0 +1,290 @@@
 +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, in_macro, 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();
 +    /// ```
 +    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()
 +    /// };
 +    /// ```
 +    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 LateLintPass<'_> for Default {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if !in_macro(expr.span);
 +            // 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);
 +            // 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<'tcx>(&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 !in_macro(expr.span);
 +                // 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.ident.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
 +        }
 +    }
 +}
index 87124f093a86dbf7c327581296397e5d0e58a55d,0000000000000000000000000000000000000000..48f781516f4228c2aecdb5f738d76c47942772ff
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,136 @@@
- use clippy_utils::diagnostics::span_lint;
++use clippy_utils::diagnostics::span_lint_and_then;
 +
- use rustc_data_structures::fx::FxHashSet;
++use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    def::Res, def_id::DefId, Item, ItemKind, PolyTraitRef, PrimTy, TraitBoundModifier, Ty, TyKind, UseKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
- use rustc_span::{Span, Symbol};
++use rustc_span::Span;
++
++use crate::utils::conf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Denies the configured types in clippy.toml.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some types are undesirable in certain contexts.
 +    ///
 +    /// ### Example:
 +    /// An example clippy.toml configuration:
 +    /// ```toml
 +    /// # clippy.toml
-     /// disallowed-types = ["std::collections::BTreeMap"]
++    /// disallowed-types = [
++    ///     # Can use a string as the path of the disallowed type.
++    ///     "std::collections::BTreeMap",
++    ///     # Can also use an inline table with a `path` key.
++    ///     { path = "std::net::TcpListener" },
++    ///     # When using an inline table, can add a `reason` for why the type
++    ///     # is disallowed.
++    ///     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
++    /// ]
 +    /// ```
 +    ///
 +    /// ```rust,ignore
 +    /// use std::collections::BTreeMap;
 +    /// // or its use
 +    /// let x = std::collections::BTreeMap::new();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// // A similar type that is allowed by the config
 +    /// use std::collections::HashMap;
 +    /// ```
 +    pub DISALLOWED_TYPE,
 +    nursery,
 +    "use of a disallowed type"
 +}
 +#[derive(Clone, Debug)]
 +pub struct DisallowedType {
-     disallowed: FxHashSet<Vec<Symbol>>,
-     def_ids: FxHashSet<DefId>,
-     prim_tys: FxHashSet<PrimTy>,
++    conf_disallowed: Vec<conf::DisallowedType>,
++    def_ids: FxHashMap<DefId, Option<String>>,
++    prim_tys: FxHashMap<PrimTy, Option<String>>,
 +}
 +
 +impl DisallowedType {
-     pub fn new(disallowed: &FxHashSet<String>) -> Self {
++    pub fn new(conf_disallowed: Vec<conf::DisallowedType>) -> Self {
 +        Self {
-             disallowed: disallowed
-                 .iter()
-                 .map(|s| s.split("::").map(Symbol::intern).collect::<Vec<_>>())
-                 .collect(),
-             def_ids: FxHashSet::default(),
-             prim_tys: FxHashSet::default(),
++            conf_disallowed,
++            def_ids: FxHashMap::default(),
++            prim_tys: FxHashMap::default(),
 +        }
 +    }
 +
 +    fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) {
 +        match res {
 +            Res::Def(_, did) => {
-                 if self.def_ids.contains(did) {
-                     emit(cx, &cx.tcx.def_path_str(*did), span);
++                if let Some(reason) = self.def_ids.get(did) {
++                    emit(cx, &cx.tcx.def_path_str(*did), span, reason.as_deref());
 +                }
 +            },
 +            Res::PrimTy(prim) => {
-                 if self.prim_tys.contains(prim) {
-                     emit(cx, prim.name_str(), span);
++                if let Some(reason) = self.prim_tys.get(prim) {
++                    emit(cx, prim.name_str(), span, reason.as_deref());
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DisallowedType => [DISALLOWED_TYPE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for DisallowedType {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
-         for path in &self.disallowed {
-             let segs = path.iter().map(ToString::to_string).collect::<Vec<_>>();
-             match clippy_utils::path_to_res(cx, &segs.iter().map(String::as_str).collect::<Vec<_>>()) {
++        for conf in &self.conf_disallowed {
++            let (path, reason) = match conf {
++                conf::DisallowedType::Simple(path) => (path, None),
++                conf::DisallowedType::WithReason { path, reason } => (
++                    path,
++                    reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)),
++                ),
++            };
++            let segs: Vec<_> = path.split("::").collect();
++            match clippy_utils::path_to_res(cx, &segs) {
 +                Res::Def(_, id) => {
-                     self.def_ids.insert(id);
++                    self.def_ids.insert(id, reason);
 +                },
 +                Res::PrimTy(ty) => {
-                     self.prim_tys.insert(ty);
++                    self.prim_tys.insert(ty, reason);
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if let ItemKind::Use(path, UseKind::Single) = &item.kind {
 +            self.check_res_emit(cx, &path.res, item.span);
 +        }
 +    }
 +
 +    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
 +        if let TyKind::Path(path) = &ty.kind {
 +            self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span);
 +        }
 +    }
 +
 +    fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>, _: TraitBoundModifier) {
 +        self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span);
 +    }
 +}
 +
- fn emit(cx: &LateContext<'_>, name: &str, span: Span) {
-     span_lint(
++fn emit(cx: &LateContext<'_>, name: &str, span: Span, reason: Option<&str>) {
++    span_lint_and_then(
 +        cx,
 +        DISALLOWED_TYPE,
 +        span,
 +        &format!("`{}` is not allowed according to config", name),
++        |diag| {
++            if let Some(reason) = reason {
++                diag.note(reason);
++            }
++        },
 +    );
 +}
index 9840affbf6fd81b978a00e2e17a0d1a0a564690c,0000000000000000000000000000000000000000..5511c3ea9b6889701a2dbdfaf015fdec0fa5b987
mode 100644,000000..100644
--- /dev/null
@@@ -1,807 -1,0 +1,819 @@@
++use clippy_utils::attrs::is_doc_hidden;
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note};
 +use clippy_utils::source::first_line_of_span;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
 +use if_chain::if_chain;
 +use itertools::Itertools;
 +use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind};
 +use rustc_ast::token::CommentKind;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_data_structures::sync::Lrc;
 +use rustc_errors::emitter::EmitterWriter;
 +use rustc_errors::Handler;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 +use rustc_hir::{AnonConst, Expr, ExprKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_parse::maybe_new_parser_from_source_str;
 +use rustc_parse::parser::ForceCollect;
 +use rustc_session::parse::ParseSess;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::def_id::LocalDefId;
 +use rustc_span::edition::Edition;
 +use rustc_span::source_map::{BytePos, FilePathMapping, MultiSpan, SourceMap, Span};
 +use rustc_span::{sym, FileName, Pos};
 +use std::io;
 +use std::ops::Range;
 +use std::thread;
 +use url::Url;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the presence of `_`, `::` or camel-case words
 +    /// outside ticks in documentation.
 +    ///
 +    /// ### Why is this bad?
 +    /// *Rustdoc* supports markdown formatting, `_`, `::` and
 +    /// camel-case probably indicates some code which should be included between
 +    /// ticks. `_` can also be used for emphasis in markdown, this lint tries to
 +    /// consider that.
 +    ///
 +    /// ### Known problems
 +    /// Lots of bad docs won’t be fixed, what the lint checks
 +    /// for is limited, and there are still false positives. HTML elements and their
 +    /// content are not linted.
 +    ///
 +    /// In addition, when writing documentation comments, including `[]` brackets
 +    /// inside a link text would trip the parser. Therfore, documenting link with
 +    /// `[`SmallVec<[T; INLINE_CAPACITY]>`]` and then [`SmallVec<[T; INLINE_CAPACITY]>`]: SmallVec
 +    /// would fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// /// Do something with the foo_bar parameter. See also
 +    /// /// that::other::module::foo.
 +    /// // ^ `foo_bar` and `that::other::module::foo` should be ticked.
 +    /// fn doit(foo_bar: usize) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Link text with `[]` brackets should be written as following:
 +    /// /// Consume the array and return the inner
 +    /// /// [`SmallVec<[T; INLINE_CAPACITY]>`][SmallVec].
 +    /// /// [SmallVec]: SmallVec
 +    /// fn main() {}
 +    /// ```
 +    pub DOC_MARKDOWN,
 +    pedantic,
 +    "presence of `_`, `::` or camel-case outside backticks in documentation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the doc comments of publicly visible
 +    /// unsafe functions and warns if there is no `# Safety` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unsafe functions should document their safety
 +    /// preconditions, so that users can be sure they are using them safely.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// This function should really be documented
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    ///
 +    /// At least write a line about safety:
 +    ///
 +    /// ```rust
 +    ///# type Universe = ();
 +    /// /// # Safety
 +    /// ///
 +    /// /// This function should not be called before the horsemen are ready.
 +    /// pub unsafe fn start_apocalypse(u: &mut Universe) {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    pub MISSING_SAFETY_DOC,
 +    style,
 +    "`pub unsafe fn` without `# Safety` docs"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// return a `Result` type and warns if there is no `# Errors` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the type of errors that can be returned from a
 +    /// function can help callers write code to handle the errors appropriately.
 +    ///
 +    /// ### Examples
 +    /// Since the following function returns a `Result` it has an `# Errors` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    ///# use std::io;
 +    /// /// # Errors
 +    /// ///
 +    /// /// Will return `Err` if `filename` does not exist or the user does not have
 +    /// /// permission to read it.
 +    /// pub fn read(filename: String) -> io::Result<String> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    pub MISSING_ERRORS_DOC,
 +    pedantic,
 +    "`pub fn` returns `Result` without `# Errors` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks the doc comments of publicly visible functions that
 +    /// may panic and warns if there is no `# Panics` section.
 +    ///
 +    /// ### Why is this bad?
 +    /// Documenting the scenarios in which panicking occurs
 +    /// can help callers who do not want to panic to avoid those situations.
 +    ///
 +    /// ### Examples
 +    /// Since the following function may panic it has a `# Panics` section in
 +    /// its doc comment:
 +    ///
 +    /// ```rust
 +    /// /// # Panics
 +    /// ///
 +    /// /// Will panic if y is 0
 +    /// pub fn divide_by(x: i32, y: i32) -> i32 {
 +    ///     if y == 0 {
 +    ///         panic!("Cannot divide by 0")
 +    ///     } else {
 +    ///         x / y
 +    ///     }
 +    /// }
 +    /// ```
 +    pub MISSING_PANICS_DOC,
 +    pedantic,
 +    "`pub fn` may panic without `# Panics` in doc comment"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `fn main() { .. }` in doctests
 +    ///
 +    /// ### Why is this bad?
 +    /// The test can be shorter (and likely more readable)
 +    /// if the `fn main()` is left implicit.
 +    ///
 +    /// ### Examples
 +    /// ``````rust
 +    /// /// An example of a doctest with a `main()` function
 +    /// ///
 +    /// /// # Examples
 +    /// ///
 +    /// /// ```
 +    /// /// fn main() {
 +    /// ///     // this needs not be in an `fn`
 +    /// /// }
 +    /// /// ```
 +    /// fn needless_main() {
 +    ///     unimplemented!();
 +    /// }
 +    /// ``````
 +    pub NEEDLESS_DOCTEST_MAIN,
 +    style,
 +    "presence of `fn main() {` in code examples"
 +}
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Clone)]
 +pub struct DocMarkdown {
 +    valid_idents: FxHashSet<String>,
 +    in_trait_impl: bool,
 +}
 +
 +impl DocMarkdown {
 +    pub fn new(valid_idents: FxHashSet<String>) -> Self {
 +        Self {
 +            valid_idents,
 +            in_trait_impl: false,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(DocMarkdown =>
 +    [DOC_MARKDOWN, MISSING_SAFETY_DOC, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, NEEDLESS_DOCTEST_MAIN]
 +);
 +
 +impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
 +    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
 +        let attrs = cx.tcx.hir().attrs(hir::CRATE_HIR_ID);
 +        check_attrs(cx, &self.valid_idents, attrs);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        match item.kind {
 +            hir::ItemKind::Fn(ref sig, _, body_id) => {
 +                if !(is_entrypoint_fn(cx, item.def_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
 +                    let body = cx.tcx.hir().body(body_id);
 +                    let mut fpu = FindPanicUnwrap {
 +                        cx,
 +                        typeck_results: cx.tcx.typeck(item.def_id),
 +                        panic_span: None,
 +                    };
 +                    fpu.visit_expr(&body.value);
 +                    lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
 +                }
 +            },
 +            hir::ItemKind::Impl(ref impl_) => {
 +                self.in_trait_impl = impl_.of_trait.is_some();
 +            },
 +            hir::ItemKind::Trait(_, unsafety, ..) => {
 +                if !headers.safety && unsafety == hir::Unsafety::Unsafe {
 +                    span_lint(
 +                        cx,
 +                        MISSING_SAFETY_DOC,
 +                        item.span,
 +                        "docs for unsafe trait missing `# Safety` section",
 +                    );
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        if let hir::ItemKind::Impl { .. } = item.kind {
 +            self.in_trait_impl = false;
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if let hir::TraitItemKind::Fn(ref sig, ..) = item.kind {
 +            if !in_external_macro(cx.tcx.sess, item.span) {
 +                lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, None, None);
 +            }
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        let attrs = cx.tcx.hir().attrs(item.hir_id());
 +        let headers = check_attrs(cx, &self.valid_idents, attrs);
 +        if self.in_trait_impl || in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +        if let hir::ImplItemKind::Fn(ref sig, body_id) = item.kind {
 +            let body = cx.tcx.hir().body(body_id);
 +            let mut fpu = FindPanicUnwrap {
 +                cx,
 +                typeck_results: cx.tcx.typeck(item.def_id),
 +                panic_span: None,
 +            };
 +            fpu.visit_expr(&body.value);
 +            lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
 +        }
 +    }
 +}
 +
 +fn lint_for_missing_headers<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    def_id: LocalDefId,
 +    span: impl Into<MultiSpan> + Copy,
 +    sig: &hir::FnSig<'_>,
 +    headers: DocHeaders,
 +    body_id: Option<hir::BodyId>,
 +    panic_span: Option<Span>,
 +) {
 +    if !cx.access_levels.is_exported(def_id) {
 +        return; // Private functions do not require doc comments
 +    }
++
++    // do not lint if any parent has `#[doc(hidden)]` attribute (#7347)
++    if cx
++        .tcx
++        .hir()
++        .parent_iter(cx.tcx.hir().local_def_id_to_hir_id(def_id))
++        .any(|(id, _node)| is_doc_hidden(cx.tcx.hir().attrs(id)))
++    {
++        return;
++    }
++
 +    if !headers.safety && sig.header.unsafety == hir::Unsafety::Unsafe {
 +        span_lint(
 +            cx,
 +            MISSING_SAFETY_DOC,
 +            span,
 +            "unsafe function's docs miss `# Safety` section",
 +        );
 +    }
 +    if !headers.panics && panic_span.is_some() {
 +        span_lint_and_note(
 +            cx,
 +            MISSING_PANICS_DOC,
 +            span,
 +            "docs for function which may panic missing `# Panics` section",
 +            panic_span,
 +            "first possible panic found here",
 +        );
 +    }
 +    if !headers.errors {
 +        let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +        if is_type_diagnostic_item(cx, return_ty(cx, hir_id), sym::Result) {
 +            span_lint(
 +                cx,
 +                MISSING_ERRORS_DOC,
 +                span,
 +                "docs for function returning `Result` missing `# Errors` section",
 +            );
 +        } else {
 +            if_chain! {
 +                if let Some(body_id) = body_id;
 +                if let Some(future) = cx.tcx.lang_items().future_trait();
 +                let typeck = cx.tcx.typeck_body(body_id);
 +                let body = cx.tcx.hir().body(body_id);
 +                let ret_ty = typeck.expr_ty(&body.value);
 +                if implements_trait(cx, ret_ty, future, &[]);
 +                if let ty::Opaque(_, subs) = ret_ty.kind();
 +                if let Some(gen) = subs.types().next();
 +                if let ty::Generator(_, subs, _) = gen.kind();
 +                if is_type_diagnostic_item(cx, subs.as_generator().return_ty(), sym::Result);
 +                then {
 +                    span_lint(
 +                        cx,
 +                        MISSING_ERRORS_DOC,
 +                        span,
 +                        "docs for function returning `Result` missing `# Errors` section",
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Cleanup documentation decoration.
 +///
 +/// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 +/// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
 +/// need to keep track of
 +/// the spans but this function is inspired from the later.
 +#[allow(clippy::cast_possible_truncation)]
 +#[must_use]
 +pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
 +    // one-line comments lose their prefix
 +    if comment_kind == CommentKind::Line {
 +        let mut doc = doc.to_owned();
 +        doc.push('\n');
 +        let len = doc.len();
 +        // +3 skips the opening delimiter
 +        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
 +    }
 +
 +    let mut sizes = vec![];
 +    let mut contains_initial_stars = false;
 +    for line in doc.lines() {
 +        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
 +        debug_assert_eq!(offset as u32 as usize, offset);
 +        contains_initial_stars |= line.trim_start().starts_with('*');
 +        // +1 adds the newline, +3 skips the opening delimiter
 +        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
 +    }
 +    if !contains_initial_stars {
 +        return (doc.to_string(), sizes);
 +    }
 +    // remove the initial '*'s if any
 +    let mut no_stars = String::with_capacity(doc.len());
 +    for line in doc.lines() {
 +        let mut chars = line.chars();
 +        for c in &mut chars {
 +            if c.is_whitespace() {
 +                no_stars.push(c);
 +            } else {
 +                no_stars.push(if c == '*' { ' ' } else { c });
 +                break;
 +            }
 +        }
 +        no_stars.push_str(chars.as_str());
 +        no_stars.push('\n');
 +    }
 +
 +    (no_stars, sizes)
 +}
 +
 +#[derive(Copy, Clone)]
 +struct DocHeaders {
 +    safety: bool,
 +    errors: bool,
 +    panics: bool,
 +}
 +
 +fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &'a [Attribute]) -> DocHeaders {
 +    use pulldown_cmark::{BrokenLink, CowStr, Options};
 +    /// We don't want the parser to choke on intra doc links. Since we don't
 +    /// actually care about rendering them, just pretend that all broken links are
 +    /// point to a fake address.
 +    #[allow(clippy::unnecessary_wraps)] // we're following a type signature
 +    fn fake_broken_link_callback<'a>(_: BrokenLink<'_>) -> Option<(CowStr<'a>, CowStr<'a>)> {
 +        Some(("fake".into(), "fake".into()))
 +    }
 +
 +    let mut doc = String::new();
 +    let mut spans = vec![];
 +
 +    for attr in attrs {
 +        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
 +            let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
 +            spans.extend_from_slice(&current_spans);
 +            doc.push_str(&comment);
 +        } else if attr.has_name(sym::doc) {
 +            // ignore mix of sugared and non-sugared doc
 +            // don't trigger the safety or errors check
 +            return DocHeaders {
 +                safety: true,
 +                errors: true,
 +                panics: true,
 +            };
 +        }
 +    }
 +
 +    let mut current = 0;
 +    for &mut (ref mut offset, _) in &mut spans {
 +        let offset_copy = *offset;
 +        *offset = current;
 +        current += offset_copy;
 +    }
 +
 +    if doc.is_empty() {
 +        return DocHeaders {
 +            safety: false,
 +            errors: false,
 +            panics: false,
 +        };
 +    }
 +
 +    let mut cb = fake_broken_link_callback;
 +
 +    let parser =
 +        pulldown_cmark::Parser::new_with_broken_link_callback(&doc, Options::empty(), Some(&mut cb)).into_offset_iter();
 +    // Iterate over all `Events` and combine consecutive events into one
 +    let events = parser.coalesce(|previous, current| {
 +        use pulldown_cmark::Event::Text;
 +
 +        let previous_range = previous.1;
 +        let current_range = current.1;
 +
 +        match (previous.0, current.0) {
 +            (Text(previous), Text(current)) => {
 +                let mut previous = previous.to_string();
 +                previous.push_str(&current);
 +                Ok((Text(previous.into()), previous_range))
 +            },
 +            (previous, current) => Err(((previous, previous_range), (current, current_range))),
 +        }
 +    });
 +    check_doc(cx, valid_idents, events, &spans)
 +}
 +
 +const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 +
 +fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize>)>>(
 +    cx: &LateContext<'_>,
 +    valid_idents: &FxHashSet<String>,
 +    events: Events,
 +    spans: &[(usize, Span)],
 +) -> DocHeaders {
 +    // true if a safety header was found
 +    use pulldown_cmark::Event::{
 +        Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 +    };
 +    use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
 +    use pulldown_cmark::{CodeBlockKind, CowStr};
 +
 +    let mut headers = DocHeaders {
 +        safety: false,
 +        errors: false,
 +        panics: false,
 +    };
 +    let mut in_code = false;
 +    let mut in_link = None;
 +    let mut in_heading = false;
 +    let mut is_rust = false;
 +    let mut edition = None;
 +    let mut ticks_unbalanced = false;
 +    let mut text_to_check: Vec<(CowStr<'_>, Span)> = Vec::new();
 +    let mut paragraph_span = spans.get(0).expect("function isn't called if doc comment is empty").1;
 +    for (event, range) in events {
 +        match event {
 +            Start(CodeBlock(ref kind)) => {
 +                in_code = true;
 +                if let CodeBlockKind::Fenced(lang) = kind {
 +                    for item in lang.split(',') {
 +                        if item == "ignore" {
 +                            is_rust = false;
 +                            break;
 +                        }
 +                        if let Some(stripped) = item.strip_prefix("edition") {
 +                            is_rust = true;
 +                            edition = stripped.parse::<Edition>().ok();
 +                        } else if item.is_empty() || RUST_CODE.contains(&item) {
 +                            is_rust = true;
 +                        }
 +                    }
 +                }
 +            },
 +            End(CodeBlock(_)) => {
 +                in_code = false;
 +                is_rust = false;
 +            },
 +            Start(Link(_, url, _)) => in_link = Some(url),
 +            End(Link(..)) => in_link = None,
 +            Start(Heading(_) | Paragraph | Item) => {
 +                if let Start(Heading(_)) = event {
 +                    in_heading = true;
 +                }
 +                ticks_unbalanced = false;
 +                let (_, span) = get_current_span(spans, range.start);
 +                paragraph_span = first_line_of_span(cx, span);
 +            },
 +            End(Heading(_) | Paragraph | Item) => {
 +                if let End(Heading(_)) = event {
 +                    in_heading = false;
 +                }
 +                if ticks_unbalanced {
 +                    span_lint_and_help(
 +                        cx,
 +                        DOC_MARKDOWN,
 +                        paragraph_span,
 +                        "backticks are unbalanced",
 +                        None,
 +                        "a backtick may be missing a pair",
 +                    );
 +                } else {
 +                    for (text, span) in text_to_check {
 +                        check_text(cx, valid_idents, &text, span);
 +                    }
 +                }
 +                text_to_check = Vec::new();
 +            },
 +            Start(_tag) | End(_tag) => (), // We don't care about other tags
 +            Html(_html) => (),             // HTML is weird, just ignore it
 +            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
 +            FootnoteReference(text) | Text(text) => {
 +                let (begin, span) = get_current_span(spans, range.start);
 +                paragraph_span = paragraph_span.with_hi(span.hi());
 +                ticks_unbalanced |= text.contains('`') && !in_code;
 +                if Some(&text) == in_link.as_ref() || ticks_unbalanced {
 +                    // Probably a link of the form `<http://example.com>`
 +                    // Which are represented as a link to "http://example.com" with
 +                    // text "http://example.com" by pulldown-cmark
 +                    continue;
 +                }
 +                headers.safety |= in_heading && text.trim() == "Safety";
 +                headers.errors |= in_heading && text.trim() == "Errors";
 +                headers.panics |= in_heading && text.trim() == "Panics";
 +                if in_code {
 +                    if is_rust {
 +                        let edition = edition.unwrap_or_else(|| cx.tcx.sess.edition());
 +                        check_code(cx, &text, edition, span);
 +                    }
 +                } else {
 +                    // Adjust for the beginning of the current `Event`
 +                    let span = span.with_lo(span.lo() + BytePos::from_usize(range.start - begin));
 +                    text_to_check.push((text, span));
 +                }
 +            },
 +        }
 +    }
 +    headers
 +}
 +
 +fn get_current_span(spans: &[(usize, Span)], idx: usize) -> (usize, Span) {
 +    let index = match spans.binary_search_by(|c| c.0.cmp(&idx)) {
 +        Ok(o) => o,
 +        Err(e) => e - 1,
 +    };
 +    spans[index]
 +}
 +
 +fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) {
 +    fn has_needless_main(code: String, edition: Edition) -> bool {
 +        rustc_driver::catch_fatal_errors(|| {
 +            rustc_span::create_session_globals_then(edition, || {
 +                let filename = FileName::anon_source_code(&code);
 +
 +                let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
 +                let emitter = EmitterWriter::new(Box::new(io::sink()), None, false, false, false, None, false);
 +                let handler = Handler::with_emitter(false, None, Box::new(emitter));
 +                let sess = ParseSess::with_span_handler(handler, sm);
 +
 +                let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {
 +                    Ok(p) => p,
 +                    Err(errs) => {
 +                        for mut err in errs {
 +                            err.cancel();
 +                        }
 +                        return false;
 +                    },
 +                };
 +
 +                let mut relevant_main_found = false;
 +                loop {
 +                    match parser.parse_item(ForceCollect::No) {
 +                        Ok(Some(item)) => match &item.kind {
 +                            // Tests with one of these items are ignored
 +                            ItemKind::Static(..)
 +                            | ItemKind::Const(..)
 +                            | ItemKind::ExternCrate(..)
 +                            | ItemKind::ForeignMod(..) => return false,
 +                            // We found a main function ...
 +                            ItemKind::Fn(box FnKind(_, sig, _, Some(block))) if item.ident.name == sym::main => {
 +                                let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
 +                                let returns_nothing = match &sig.decl.output {
 +                                    FnRetTy::Default(..) => true,
 +                                    FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
 +                                    FnRetTy::Ty(_) => false,
 +                                };
 +
 +                                if returns_nothing && !is_async && !block.stmts.is_empty() {
 +                                    // This main function should be linted, but only if there are no other functions
 +                                    relevant_main_found = true;
 +                                } else {
 +                                    // This main function should not be linted, we're done
 +                                    return false;
 +                                }
 +                            },
 +                            // Another function was found; this case is ignored too
 +                            ItemKind::Fn(..) => return false,
 +                            _ => {},
 +                        },
 +                        Ok(None) => break,
 +                        Err(mut e) => {
 +                            e.cancel();
 +                            return false;
 +                        },
 +                    }
 +                }
 +
 +                relevant_main_found
 +            })
 +        })
 +        .ok()
 +        .unwrap_or_default()
 +    }
 +
 +    // Because of the global session, we need to create a new session in a different thread with
 +    // the edition we need.
 +    let text = text.to_owned();
 +    if thread::spawn(move || has_needless_main(text, edition))
 +        .join()
 +        .expect("thread::spawn failed")
 +    {
 +        span_lint(cx, NEEDLESS_DOCTEST_MAIN, span, "needless `fn main` in doctest");
 +    }
 +}
 +
 +fn check_text(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, text: &str, span: Span) {
 +    for word in text.split(|c: char| c.is_whitespace() || c == '\'') {
 +        // Trim punctuation as in `some comment (see foo::bar).`
 +        //                                                   ^^
 +        // Or even as in `_foo bar_` which is emphasized.
 +        let word = word.trim_matches(|c: char| !c.is_alphanumeric());
 +
 +        if valid_idents.contains(word) {
 +            continue;
 +        }
 +
 +        // Adjust for the current word
 +        let offset = word.as_ptr() as usize - text.as_ptr() as usize;
 +        let span = Span::new(
 +            span.lo() + BytePos::from_usize(offset),
 +            span.lo() + BytePos::from_usize(offset + word.len()),
 +            span.ctxt(),
 +            span.parent(),
 +        );
 +
 +        check_word(cx, word, span);
 +    }
 +}
 +
 +fn check_word(cx: &LateContext<'_>, word: &str, span: Span) {
 +    /// Checks if a string is camel-case, i.e., contains at least two uppercase
 +    /// letters (`Clippy` is ok) and one lower-case letter (`NASA` is ok).
 +    /// Plurals are also excluded (`IDs` is ok).
 +    fn is_camel_case(s: &str) -> bool {
 +        if s.starts_with(|c: char| c.is_digit(10)) {
 +            return false;
 +        }
 +
 +        let s = s.strip_suffix('s').unwrap_or(s);
 +
 +        s.chars().all(char::is_alphanumeric)
 +            && s.chars().filter(|&c| c.is_uppercase()).take(2).count() > 1
 +            && s.chars().filter(|&c| c.is_lowercase()).take(1).count() > 0
 +    }
 +
 +    fn has_underscore(s: &str) -> bool {
 +        s != "_" && !s.contains("\\_") && s.contains('_')
 +    }
 +
 +    fn has_hyphen(s: &str) -> bool {
 +        s != "-" && s.contains('-')
 +    }
 +
 +    if let Ok(url) = Url::parse(word) {
 +        // try to get around the fact that `foo::bar` parses as a valid URL
 +        if !url.cannot_be_a_base() {
 +            span_lint(
 +                cx,
 +                DOC_MARKDOWN,
 +                span,
 +                "you should put bare URLs between `<`/`>` or make a proper Markdown link",
 +            );
 +
 +            return;
 +        }
 +    }
 +
 +    // We assume that mixed-case words are not meant to be put inside bacticks. (Issue #2343)
 +    if has_underscore(word) && has_hyphen(word) {
 +        return;
 +    }
 +
 +    if has_underscore(word) || word.contains("::") || is_camel_case(word) {
 +        span_lint(
 +            cx,
 +            DOC_MARKDOWN,
 +            span,
 +            &format!("you should put `{}` between ticks in the documentation", word),
 +        );
 +    }
 +}
 +
 +struct FindPanicUnwrap<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    panic_span: Option<Span>,
 +    typeck_results: &'tcx ty::TypeckResults<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.panic_span.is_some() {
 +            return;
 +        }
 +
 +        // check for `begin_panic`
 +        if_chain! {
 +            if let ExprKind::Call(func_expr, _) = expr.kind;
 +            if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind;
 +            if let Some(path_def_id) = path.res.opt_def_id();
 +            if match_panic_def_id(self.cx, path_def_id);
 +            if is_expn_of(expr.span, "unreachable").is_none();
 +            if !is_expn_of_debug_assertions(expr.span);
 +            then {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // check for `assert_eq` or `assert_ne`
 +        if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() {
 +            self.panic_span = Some(expr.span);
 +        }
 +
 +        // check for `unwrap`
 +        if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
 +            let reciever_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs();
 +            if is_type_diagnostic_item(self.cx, reciever_ty, sym::Option)
 +                || is_type_diagnostic_item(self.cx, reciever_ty, sym::Result)
 +            {
 +                self.panic_span = Some(expr.span);
 +            }
 +        }
 +
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    // Panics in const blocks will cause compilation to fail.
 +    fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
 +    }
 +}
 +
 +fn is_expn_of_debug_assertions(span: Span) -> bool {
 +    const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
 +    MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some())
 +}
index 51d5094e8c9987491d39bb3b29c3b91cc050b789,0000000000000000000000000000000000000000..655560afd4250045d8675529fc41bb5abf6f8677
mode 100644,000000..100644
--- /dev/null
@@@ -1,253 -1,0 +1,258 @@@
- use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of};
 +use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
 +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, higher, in_macro, is_expn_of, is_in_test_function,
++};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +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);
 +    /// ```
 +    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
 +    /// ```
 +    pub OP_REF,
 +    style,
 +    "taking a reference to satisfy the type constraints on `==`"
 +}
 +
 +declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
 +
 +const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"];
 +
 +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 let ExprKind::Block(block, _) = e.kind {
 +            for stmt in block.stmts {
 +                for amn in &ASSERT_MACRO_NAMES {
 +                    if_chain! {
 +                        if is_expn_of(stmt.span, amn).is_some();
 +                        if let StmtKind::Semi(matchexpr) = stmt.kind;
 +                        if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr);
 +                        if macro_args.len() == 2;
 +                        let (lhs, rhs) = (macro_args[0], macro_args[1]);
 +                        if eq_expr_value(cx, lhs, rhs);
-             if is_useless_with_eq_exprs(op.node.into()) && eq_expr_value(cx, left, right) {
++                        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", amn),
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +        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 {
 +                    in_macro(expr.span)
 +                } 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);
 +                        // 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);
 +                        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);
 +                        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
 +                                );
 +                            });
 +                        }
 +                    },
 +                    _ => {},
 +                }
 +            }
 +        }
 +    }
 +}
index 0c6ba91c9430b9db1024c62276d96dab17b4a3d9,0000000000000000000000000000000000000000..e8b1d6f6edaaaeb27b1a41daf89a9cd1e0580248
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,100 @@@
- use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
-                         snippet_with_applicability(cx, pat.span, "..", &mut applicability),
++use clippy_utils::source::snippet_with_context;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Pat, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for pattern matchings that can be expressed using equality.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// * It reads better and has less cognitive load because equality won't cause binding.
 +    /// * It is a [Yoda condition](https://en.wikipedia.org/wiki/Yoda_conditions). Yoda conditions are widely
 +    /// criticized for increasing the cognitive load of reading the code.
 +    /// * Equality is a simple bool expression and can be merged with `&&` and `||` and
 +    /// reuse if blocks
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if let Some(2) = x {
 +    ///     do_thing();
 +    /// }
 +    /// ```
 +    /// Should be written
 +    /// ```rust,ignore
 +    /// if x == Some(2) {
 +    ///     do_thing();
 +    /// }
 +    /// ```
 +    pub EQUATABLE_IF_LET,
 +    nursery,
 +    "using pattern matching instead of equality"
 +}
 +
 +declare_lint_pass!(PatternEquality => [EQUATABLE_IF_LET]);
 +
 +/// detects if pattern matches just one thing
 +fn unary_pattern(pat: &Pat<'_>) -> bool {
 +    fn array_rec(pats: &[Pat<'_>]) -> bool {
 +        pats.iter().all(unary_pattern)
 +    }
 +    match &pat.kind {
 +        PatKind::Slice(_, _, _) | PatKind::Range(_, _, _) | PatKind::Binding(..) | PatKind::Wild | PatKind::Or(_) => {
 +            false
 +        },
 +        PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
 +        PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => !etc.is_some() && array_rec(a),
 +        PatKind::Ref(x, _) | PatKind::Box(x) => unary_pattern(x),
 +        PatKind::Path(_) | PatKind::Lit(_) => true,
 +    }
 +}
 +
 +fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool {
 +    if let Some(def_id) = cx.tcx.lang_items().eq_trait() {
 +        implements_trait(cx, ty, def_id, &[other.into()])
 +    } else {
 +        false
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for PatternEquality {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if_chain! {
 +            if let ExprKind::Let(pat, exp, _) = expr.kind;
 +            if unary_pattern(pat);
 +            let exp_ty = cx.typeck_results().expr_ty(exp);
 +            let pat_ty = cx.typeck_results().pat_ty(pat);
 +            if is_structural_partial_eq(cx, exp_ty, pat_ty);
 +            then {
 +
 +                let mut applicability = Applicability::MachineApplicable;
 +                let pat_str = match pat.kind {
 +                    PatKind::Struct(..) => format!(
 +                        "({})",
-                     _ => snippet_with_applicability(cx, pat.span, "..", &mut applicability).to_string(),
++                        snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                    ),
-                         snippet_with_applicability(cx, exp.span, "..", &mut applicability),
++                    _ => snippet_with_context(cx, pat.span, expr.span.ctxt(), "..", &mut applicability).0.to_string(),
 +                };
 +                span_lint_and_sugg(
 +                    cx,
 +                    EQUATABLE_IF_LET,
 +                    expr.span,
 +                    "this pattern matching can be expressed using equality",
 +                    "try",
 +                    format!(
 +                        "{} == {}",
++                        snippet_with_context(cx, exp.span, expr.span.ctxt(), "..", &mut applicability).0,
 +                        pat_str,
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index 37d9ea3bdc117cae4b4e917425751ec27e155213,0000000000000000000000000000000000000000..c22f9d0e170326c79fbdd0df8cc2228b27822aaf
mode 100644,000000..100644
--- /dev/null
@@@ -1,147 -1,0 +1,102 @@@
- use clippy_utils::last_path_segment;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher::FormatExpn;
- use rustc_hir::{BorrowKind, Expr, ExprKind, QPath};
 +use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
-                 if format_args.args.iter().all(is_display_arg);
-                 if format_args.fmt_expr.map_or(true, check_unformatted);
++use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::kw;
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `format!("string literal with no
 +    /// argument")` and `format!("{}", foo)` where `foo` is a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// There is no point of doing that. `format!("foo")` can
 +    /// be replaced by `"foo".to_owned()` if you really need a `String`. The even
 +    /// worse `&format!("foo")` is often encountered in the wild. `format!("{}",
 +    /// foo)` can be replaced by `foo.clone()` if `foo: String` or `foo.to_owned()`
 +    /// if `foo: &str`.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    ///
 +    /// // Bad
 +    /// let foo = "foo";
 +    /// format!("{}", foo);
 +    ///
 +    /// // Good
 +    /// foo.to_owned();
 +    /// ```
 +    pub USELESS_FORMAT,
 +    complexity,
 +    "useless use of `format!`"
 +}
 +
 +declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UselessFormat {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) {
 +            Some(e) if !e.call_site.from_expansion() => e,
 +            _ => return,
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +        if format_args.value_args.is_empty() {
 +            if_chain! {
 +                if let [e] = &*format_args.format_string_parts;
 +                if let ExprKind::Lit(lit) = &e.kind;
 +                if let Some(s_src) = snippet_opt(cx, lit.span);
 +                then {
 +                    // Simulate macro expansion, converting {{ and }} to { and }.
 +                    let s_expand = s_src.replace("{{", "{").replace("}}", "}");
 +                    let sugg = format!("{}.to_string()", s_expand);
 +                    span_useless_format(cx, call_site, sugg, applicability);
 +                }
 +            }
 +        } else if let [value] = *format_args.value_args {
 +            if_chain! {
 +                if format_args.format_string_symbols == [kw::Empty];
 +                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
 +                    ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did),
 +                    ty::Str => true,
 +                    _ => false,
 +                };
- fn is_display_arg(expr: &Expr<'_>) -> bool {
-     if_chain! {
-         if let ExprKind::Call(_, [_, fmt]) = expr.kind;
-         if let ExprKind::Path(QPath::Resolved(_, path)) = fmt.kind;
-         if let [.., t, _] = path.segments;
-         if t.ident.name == sym::Display;
-         then { true } else { false }
-     }
- }
- /// Checks if the expression matches
- /// ```rust,ignore
- /// &[_ {
- ///    format: _ {
- ///         width: _::Implied,
- ///         precision: _::Implied,
- ///         ...
- ///    },
- ///    ...,
- /// }]
- /// ```
- fn check_unformatted(expr: &Expr<'_>) -> bool {
-     if_chain! {
-         if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
-         if let ExprKind::Array([expr]) = expr.kind;
-         // struct `core::fmt::rt::v1::Argument`
-         if let ExprKind::Struct(_, fields, _) = expr.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(_, fields, _) = format_field.expr.kind;
-         if let Some(precision_field) = fields.iter().find(|f| f.ident.name == sym::precision);
-         if let ExprKind::Path(ref precision_path) = precision_field.expr.kind;
-         if last_path_segment(precision_path).ident.name == sym::Implied;
-         if let Some(width_field) = fields.iter().find(|f| f.ident.name == sym::width);
-         if let ExprKind::Path(ref width_qpath) = width_field.expr.kind;
-         if last_path_segment(width_qpath).ident.name == sym::Implied;
-         then {
-             return true;
-         }
-     }
-     false
- }
++                if let Some(args) = format_args.args();
++                if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting());
 +                then {
 +                    let is_new_string = match value.kind {
 +                        ExprKind::Binary(..) => true,
 +                        ExprKind::MethodCall(path, ..) => path.ident.name.as_str() == "to_string",
 +                        _ => false,
 +                    };
 +                    let sugg = if is_new_string {
 +                        snippet_with_applicability(cx, value.span, "..", &mut applicability).into_owned()
 +                    } else {
 +                        let sugg = Sugg::hir_with_applicability(cx, value, "<arg>", &mut applicability);
 +                        format!("{}.to_string()", sugg.maybe_par())
 +                    };
 +                    span_useless_format(cx, call_site, sugg, applicability);
 +                }
 +            }
 +        };
 +    }
 +}
 +
 +fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        USELESS_FORMAT,
 +        span,
 +        "useless use of `format!`",
 +        "consider using `.to_string()`",
 +        sugg,
 +        applicability,
 +    );
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b27442aa94656453060d0f80f558ae972b7a69e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,223 @@@
++use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
++use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn};
++use clippy_utils::source::snippet_opt;
++use clippy_utils::ty::implements_trait;
++use clippy_utils::{is_diag_trait_item, match_def_path, paths};
++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, BytePos, 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());
++    /// ```
++    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());
++    /// ```
++    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]);
++
++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];
++
++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(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 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));
++            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.is_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);
++                    check_to_string_in_format_args(cx, name, arg);
++                }
++            }
++        }
++    }
++}
++
++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: &FormatArgsArg<'_>) {
++    if_chain! {
++        if FormatExpn::parse(arg.value).is_some();
++        if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion();
++        then {
++            span_lint_and_then(
++                cx,
++                FORMAT_IN_FORMAT_ARGS,
++                trim_semicolon(cx, 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<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) {
++    let value = arg.value;
++    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 trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span {
++    snippet_opt(cx, span).map_or(span, |snippet| {
++        let snippet = snippet.trim_end_matches(';');
++        span.with_hi(span.lo() + BytePos(u32::try_from(snippet.len()).unwrap()))
++    })
++}
++
++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 04fc5887e8e8b21123d92d9bde53ed6179151560,0000000000000000000000000000000000000000..d7c5ec9ba355bdf4bcc4118bc518554718afd4cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,274 -1,0 +1,268 @@@
-     /// Checks for a [`#[must_use]`] attribute on
 +mod must_use;
 +mod not_unsafe_ptr_arg_deref;
 +mod result_unit_err;
 +mod too_many_arguments;
 +mod too_many_lines;
 +
 +use rustc_hir as hir;
 +use rustc_hir::intravisit;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions with too many parameters.
 +    ///
 +    /// ### Why is this bad?
 +    /// Functions with lots of parameters are considered bad
 +    /// style and reduce readability (“what does the 5th parameter mean?”). Consider
 +    /// grouping some parameters into a new type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct Color;
 +    /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    pub TOO_MANY_ARGUMENTS,
 +    complexity,
 +    "functions with too many arguments"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions with a large amount of lines.
 +    ///
 +    /// ### Why is this bad?
 +    /// Functions with a lot of lines are harder to understand
 +    /// due to having to look at a larger amount of code to understand what the
 +    /// function is doing. Consider splitting the body of the function into
 +    /// multiple functions.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn im_too_long() {
 +    ///     println!("");
 +    ///     // ... 100 more LoC
 +    ///     println!("");
 +    /// }
 +    /// ```
 +    pub TOO_MANY_LINES,
 +    pedantic,
 +    "functions with too many lines"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that dereference raw pointer
 +    /// arguments but are not marked `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function should probably be marked `unsafe`, since
 +    /// for an arbitrary raw pointer, there is no way of telling for sure if it is
 +    /// valid.
 +    ///
 +    /// ### Known problems
 +    /// * It does not check functions recursively so if the pointer is passed to a
 +    /// private non-`unsafe` function which does the dereferencing, the lint won't
 +    /// trigger.
 +    /// * It only checks for arguments whose type are raw pointers, not raw pointers
 +    /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
 +    /// `some_argument.get_raw_ptr()`).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// pub fn foo(x: *const u8) {
 +    ///     println!("{}", unsafe { *x });
 +    /// }
 +    ///
 +    /// // Good
 +    /// pub unsafe fn foo(x: *const u8) {
 +    ///     println!("{}", unsafe { *x });
 +    /// }
 +    /// ```
 +    pub NOT_UNSAFE_PTR_ARG_DEREF,
 +    correctness,
 +    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
-     ///
++    /// Checks for a `#[must_use]` attribute on
 +    /// unit-returning functions and methods.
 +    ///
-     /// Checks for a [`#[must_use]`] attribute without
 +    /// ### Why is this bad?
 +    /// Unit values are useless. The attribute is likely
 +    /// a remnant of a refactoring that removed the return type.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// #[must_use]
 +    /// fn useless() { }
 +    /// ```
 +    pub MUST_USE_UNIT,
 +    style,
 +    "`#[must_use]` attribute on a unit-returning function / method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
-     ///
++    /// Checks for a `#[must_use]` attribute without
 +    /// further information on functions and methods that return a type already
 +    /// marked as `#[must_use]`.
 +    ///
-     /// [`#[must_use]`] attribute, but return something not already marked
 +    /// ### Why is this bad?
 +    /// The attribute isn't needed. Not using the result
 +    /// will already be reported. Alternatively, one can add some text to the
 +    /// attribute to improve the lint message.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// #[must_use]
 +    /// fn double_must_use() -> Result<(), ()> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    pub DOUBLE_MUST_USE,
 +    style,
 +    "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that have no
-     /// [`#[must_use]`]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute
-     ///
++    /// `#[must_use]` attribute, but return something not already marked
 +    /// must-use, have no mutable arg and mutate no statics.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not bad at all, this lint just shows places where
 +    /// you could add the attribute.
 +    ///
 +    /// ### Known problems
 +    /// The lint only checks the arguments for mutable
 +    /// types without looking if they are actually changed. On the other hand,
 +    /// it also ignores a broad range of potentially interesting side effects,
 +    /// because we cannot decide whether the programmer intends the function to
 +    /// be called for the side effect or the result. Expect many false
 +    /// positives. At least we don't lint if the result type is unit or already
 +    /// `#[must_use]`.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// // this could be annotated with `#[must_use]`.
 +    /// fn id<T>(t: T) -> T { t }
 +    /// ```
 +    pub MUST_USE_CANDIDATE,
 +    pedantic,
 +    "function or method that could take a `#[must_use]` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that return a `Result`
 +    /// with an `Err` type of `()`. It suggests using a custom type that
 +    /// implements `std::error::Error`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit does not implement `Error` and carries no
 +    /// further information about what went wrong.
 +    ///
 +    /// ### Known problems
 +    /// Of course, this lint assumes that `Result` is used
 +    /// for a fallible operation (which is after all the intended use). However
 +    /// code may opt to (mis)use it as a basic two-variant-enum. In that case,
 +    /// the suggestion is misguided, and the code should use a custom enum
 +    /// instead.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// pub fn read_u8() -> Result<u8, ()> { Err(()) }
 +    /// ```
 +    /// should become
 +    /// ```rust,should_panic
 +    /// use std::fmt;
 +    ///
 +    /// #[derive(Debug)]
 +    /// pub struct EndOfStream;
 +    ///
 +    /// impl fmt::Display for EndOfStream {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         write!(f, "End of Stream")
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for EndOfStream { }
 +    ///
 +    /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
 +    ///# fn main() {
 +    ///#     read_u8().unwrap();
 +    ///# }
 +    /// ```
 +    ///
 +    /// Note that there are crates that simplify creating the error type, e.g.
 +    /// [`thiserror`](https://docs.rs/thiserror).
 +    pub RESULT_UNIT_ERR,
 +    style,
 +    "public function returning `Result` with an `Err` type of `()`"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct Functions {
 +    too_many_arguments_threshold: u64,
 +    too_many_lines_threshold: u64,
 +}
 +
 +impl Functions {
 +    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
 +        Self {
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Functions => [
 +    TOO_MANY_ARGUMENTS,
 +    TOO_MANY_LINES,
 +    NOT_UNSAFE_PTR_ARG_DEREF,
 +    MUST_USE_UNIT,
 +    DOUBLE_MUST_USE,
 +    MUST_USE_CANDIDATE,
 +    RESULT_UNIT_ERR,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Functions {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: intravisit::FnKind<'tcx>,
 +        decl: &'tcx hir::FnDecl<'_>,
 +        body: &'tcx hir::Body<'_>,
 +        span: Span,
 +        hir_id: hir::HirId,
 +    ) {
 +        too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
 +        too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
 +        not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        must_use::check_item(cx, item);
 +        result_unit_err::check_item(cx, item);
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        must_use::check_impl_item(cx, item);
 +        result_unit_err::check_impl_item(cx, item);
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
 +        not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
 +        must_use::check_trait_item(cx, item);
 +        result_unit_err::check_trait_item(cx, item);
 +    }
 +}
index 73bdd67ff5d25a864b88c23055d7e8cec1b63c18,0000000000000000000000000000000000000000..414f465c49414088113b36ee2eb09f45c72b9964
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,93 @@@
- #[allow(clippy::cast_possible_wrap)]
 +use clippy_utils::source::snippet;
 +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::{clip, unsext};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for identity operations, e.g., `x + 0`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This code can be removed without changing the
 +    /// meaning. So it just obscures what's going on. Delete it mercilessly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// x / 1 + 0 * 1 - 0 | 0;
 +    /// ```
 +    pub IDENTITY_OP,
 +    complexity,
 +    "using identity operations, e.g., `x + 0` or `y / 1`"
 +}
 +
 +declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IdentityOp {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if e.span.from_expansion() {
 +            return;
 +        }
 +        if let ExprKind::Binary(cmp, left, right) = e.kind {
 +            if is_allowed(cx, cmp, left, right) {
 +                return;
 +            }
 +            match cmp.node {
 +                BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
 +                    check(cx, left, 0, e.span, right.span);
 +                    check(cx, right, 0, e.span, left.span);
 +                },
 +                BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => check(cx, right, 0, e.span, left.span),
 +                BinOpKind::Mul => {
 +                    check(cx, left, 1, e.span, right.span);
 +                    check(cx, right, 1, e.span, left.span);
 +                },
 +                BinOpKind::Div => check(cx, right, 1, e.span, left.span),
 +                BinOpKind::BitAnd => {
 +                    check(cx, left, -1, e.span, right.span);
 +                    check(cx, right, -1, e.span, left.span);
 +                },
 +                _ => (),
 +            }
 +        }
 +    }
 +}
 +
 +fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
 +    // `1 << 0` is a common pattern in bit manipulation code
 +    cmp.node == BinOpKind::Shl
 +        && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
 +        && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1))
 +}
 +
 +fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
 +    if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e) {
 +        let check = match *cx.typeck_results().expr_ty(e).kind() {
 +            ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
 +            ty::Uint(uty) => clip(cx.tcx, !0, uty),
 +            _ => return,
 +        };
 +        if match m {
 +            0 => v == 0,
 +            -1 => v == check,
 +            1 => v == 1,
 +            _ => unreachable!(),
 +        } {
 +            span_lint(
 +                cx,
 +                IDENTITY_OP,
 +                span,
 +                &format!(
 +                    "the operation is ineffective. Consider reducing it to `{}`",
 +                    snippet(cx, arg, "..")
 +                ),
 +            );
 +        }
 +    }
 +}
index 10bca59e6d06ab5da76ebe19bbba10020f50f9a8,0000000000000000000000000000000000000000..e8cea5529e889100e88514becadc89e37aedec5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,99 -1,0 +1,99 @@@
-                        format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par().to_string())
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher::PanicExpn;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::{is_expn_of, sugg};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `if`-then-`panic!` that can be replaced with `assert!`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `assert!` is simpler than `if`-then-`panic!`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let sad_people: Vec<&str> = vec![];
 +    /// if !sad_people.is_empty() {
 +    ///     panic!("there are sad people: {:?}", sad_people);
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let sad_people: Vec<&str> = vec![];
 +    /// assert!(sad_people.is_empty(), "there are sad people: {:?}", sad_people);
 +    /// ```
 +    pub IF_THEN_PANIC,
 +    style,
 +    "`panic!` and only a `panic!` in `if`-then statement"
 +}
 +
 +declare_lint_pass!(IfThenPanic => [IF_THEN_PANIC]);
 +
 +impl LateLintPass<'_> for IfThenPanic {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let Expr {
 +                kind: ExprKind:: If(cond, Expr {
 +                    kind: ExprKind::Block(
 +                        Block {
 +                            stmts: [stmt],
 +                            ..
 +                        },
 +                        _),
 +                    ..
 +                }, None),
 +                ..
 +            } = &expr;
 +            if is_expn_of(stmt.span, "panic").is_some();
 +            if !matches!(cond.kind, ExprKind::Let(_, _, _));
 +            if let StmtKind::Semi(semi) = stmt.kind;
 +            if !cx.tcx.sess.source_map().is_multiline(cond.span);
 +
 +            then {
 +                let span = if let Some(panic_expn) = PanicExpn::parse(semi) {
 +                    match *panic_expn.format_args.value_args {
 +                        [] => panic_expn.format_args.format_string_span,
 +                        [.., last] => panic_expn.format_args.format_string_span.to(last.span),
 +                    }
 +                } else {
 +                    if_chain! {
 +                        if let ExprKind::Block(block, _) = semi.kind;
 +                        if let Some(init) = block.expr;
 +                        if let ExprKind::Call(_, [format_args]) = init.kind;
 +
 +                        then {
 +                            format_args.span
 +                        } else {
 +                            return
 +                        }
 +                    }
 +                };
 +                let mut applicability = Applicability::MachineApplicable;
 +                let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
 +                let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind {
 +                    if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e {
 +                         sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string()
 +                    } else {
-                    format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par().to_string())
++                       format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par())
 +                    }
 +                } else {
++                   format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par())
 +                };
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    IF_THEN_PANIC,
 +                    expr.span,
 +                    "only a `panic!` in `if`-then statement",
 +                    "try",
 +                    format!("assert!({}, {});", cond_sugg, sugg),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index 79d4d7ddcbcedfe8e0ad85116a98a76068dc6c46,0000000000000000000000000000000000000000..a4f60ded3a6e0ebcaadab8e2b92ab712211d5159
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,185 @@@
-             if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::{in_macro, SpanlessEq};
 +use if_chain::if_chain;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{lang_items::LangItem, BinOpKind, Expr, ExprKind, QPath, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for implicit saturating subtraction.
 +    ///
 +    /// ### Why is this bad?
 +    /// Simplicity and readability. Instead we can easily use an builtin function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let end: u32 = 10;
 +    /// let start: u32 = 5;
 +    ///
 +    /// let mut i: u32 = end - start;
 +    ///
 +    /// // Bad
 +    /// if i != 0 {
 +    ///     i -= 1;
 +    /// }
 +    ///
 +    /// // Good
 +    /// i = i.saturating_sub(1);
 +    /// ```
 +    pub IMPLICIT_SATURATING_SUB,
 +    pedantic,
 +    "Perform saturating subtraction instead of implicitly checking lower bound of data type"
 +}
 +
 +declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if in_macro(expr.span) {
 +            return;
 +        }
 +        if_chain! {
++            if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr);
 +
 +            // Check if the conditional expression is a binary operation
 +            if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind;
 +
 +            // Ensure that the binary operator is >, != and <
 +            if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node;
 +
 +            // Check if the true condition block has only one statement
 +            if let ExprKind::Block(block, _) = then.kind;
 +            if block.stmts.len() == 1 && block.expr.is_none();
 +
 +            // Check if assign operation is done
 +            if let StmtKind::Semi(e) = block.stmts[0].kind;
 +            if let Some(target) = subtracts_one(cx, e);
 +
 +            // Extracting out the variable name
 +            if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind;
 +
 +            then {
 +                // Handle symmetric conditions in the if statement
 +                let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
 +                    if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
 +                        (cond_left, cond_right)
 +                    } else {
 +                        return;
 +                    }
 +                } else if SpanlessEq::new(cx).eq_expr(cond_right, target) {
 +                    if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node {
 +                        (cond_right, cond_left)
 +                    } else {
 +                        return;
 +                    }
 +                } else {
 +                    return;
 +                };
 +
 +                // Check if the variable in the condition statement is an integer
 +                if !cx.typeck_results().expr_ty(cond_var).is_integral() {
 +                    return;
 +                }
 +
 +                // Get the variable name
 +                let var_name = ares_path.segments[0].ident.name.as_str();
 +                const INT_TYPES: [LangItem; 5] = [
 +                    LangItem::I8,
 +                    LangItem::I16,
 +                    LangItem::I32,
 +                    LangItem::I64,
 +                    LangItem::Isize
 +                ];
 +
 +                match cond_num_val.kind {
 +                    ExprKind::Lit(ref cond_lit) => {
 +                        // Check if the constant is zero
 +                        if let LitKind::Int(0, _) = cond_lit.node {
 +                            if cx.typeck_results().expr_ty(cond_left).is_signed() {
 +                            } else {
 +                                print_lint_and_sugg(cx, &var_name, expr);
 +                            };
 +                        }
 +                    },
 +                    ExprKind::Path(QPath::TypeRelative(_, name)) => {
 +                        if_chain! {
 +                            if name.ident.as_str() == "MIN";
 +                            if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id);
 +                            if let Some(impl_id) = cx.tcx.impl_of_method(const_id);
 +                            let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
 +                            if int_ids.any(|int_id| int_id == impl_id);
 +                            then {
 +                                print_lint_and_sugg(cx, &var_name, expr)
 +                            }
 +                        }
 +                    },
 +                    ExprKind::Call(func, []) => {
 +                        if_chain! {
 +                            if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind;
 +                            if name.ident.as_str() == "min_value";
 +                            if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id);
 +                            if let Some(impl_id) = cx.tcx.impl_of_method(func_id);
 +                            let mut int_ids = INT_TYPES.iter().filter_map(|&ty| cx.tcx.lang_items().require(ty).ok());
 +                            if int_ids.any(|int_id| int_id == impl_id);
 +                            then {
 +                                print_lint_and_sugg(cx, &var_name, expr)
 +                            }
 +                        }
 +                    },
 +                    _ => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &Expr<'a>) -> Option<&'a Expr<'a>> {
 +    match expr.kind {
 +        ExprKind::AssignOp(ref op1, target, value) => {
 +            if_chain! {
 +                if BinOpKind::Sub == op1.node;
 +                // Check if literal being subtracted is one
 +                if let ExprKind::Lit(ref lit1) = value.kind;
 +                if let LitKind::Int(1, _) = lit1.node;
 +                then {
 +                    Some(target)
 +                } else {
 +                    None
 +                }
 +            }
 +        },
 +        ExprKind::Assign(target, value, _) => {
 +            if_chain! {
 +                if let ExprKind::Binary(ref op1, left1, right1) = value.kind;
 +                if BinOpKind::Sub == op1.node;
 +
 +                if SpanlessEq::new(cx).eq_expr(left1, target);
 +
 +                if let ExprKind::Lit(ref lit1) = right1.kind;
 +                if let LitKind::Int(1, _) = lit1.node;
 +                then {
 +                    Some(target)
 +                } else {
 +                    None
 +                }
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn print_lint_and_sugg(cx: &LateContext<'_>, var_name: &str, expr: &Expr<'_>) {
 +    span_lint_and_sugg(
 +        cx,
 +        IMPLICIT_SATURATING_SUB,
 +        expr.span,
 +        "implicitly performing saturating subtraction",
 +        "try",
 +        format!("{} = {}.saturating_sub({});", var_name, var_name, '1'),
 +        Applicability::MachineApplicable,
 +    );
 +}
index 3c40ca50a0981aa04c42d6357362fc4aef139311,0000000000000000000000000000000000000000..61dd0eb4af7ed37d7239f2b334bbed4712a7edf8
mode 100644,000000..100644
--- /dev/null
@@@ -1,159 -1,0 +1,159 @@@
-                 self_type.to_string()
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method};
 +use if_chain::if_chain;
 +use rustc_hir::{ImplItem, ImplItemKind};
 +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 the definition of inherent methods with a signature of `to_string(&self) -> String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This method is also implicitly defined if a type implements the `Display` trait. As the functionality of `Display` is much more versatile, it should be preferred.
 +    ///
 +    /// ### Known problems
 +    /// None
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// pub struct A;
 +    ///
 +    /// impl A {
 +    ///     pub fn to_string(&self) -> String {
 +    ///         "I am A".to_string()
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Good
 +    /// use std::fmt;
 +    ///
 +    /// pub struct A;
 +    ///
 +    /// impl fmt::Display for A {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "I am A")
 +    ///     }
 +    /// }
 +    /// ```
 +    pub INHERENT_TO_STRING,
 +    style,
 +    "type implements inherent method `to_string()`, but should instead implement the `Display` trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the definition of inherent methods with a signature of `to_string(&self) -> String` and if the type implementing this method also implements the `Display` trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// This method is also implicitly defined if a type implements the `Display` trait. The less versatile inherent method will then shadow the implementation introduced by `Display`.
 +    ///
 +    /// ### Known problems
 +    /// None
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// use std::fmt;
 +    ///
 +    /// pub struct A;
 +    ///
 +    /// impl A {
 +    ///     pub fn to_string(&self) -> String {
 +    ///         "I am A".to_string()
 +    ///     }
 +    /// }
 +    ///
 +    /// impl fmt::Display for A {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "I am A, too")
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// // Good
 +    /// use std::fmt;
 +    ///
 +    /// pub struct A;
 +    ///
 +    /// impl fmt::Display for A {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "I am A")
 +    ///     }
 +    /// }
 +    /// ```
 +    pub INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    correctness,
 +    "type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
 +}
 +
 +declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InherentToString {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
 +        if impl_item.span.from_expansion() {
 +            return;
 +        }
 +
 +        if_chain! {
 +            // Check if item is a method, called to_string and has a parameter 'self'
 +            if let ImplItemKind::Fn(ref signature, _) = impl_item.kind;
 +            if impl_item.ident.name.as_str() == "to_string";
 +            let decl = &signature.decl;
 +            if decl.implicit_self.has_implicit_self();
 +            if decl.inputs.len() == 1;
 +            if impl_item.generics.params.is_empty();
 +
 +            // Check if return type is String
 +            if is_type_diagnostic_item(cx, return_ty(cx, impl_item.hir_id()), sym::String);
 +
 +            // Filters instances of to_string which are required by a trait
 +            if trait_ref_of_method(cx, impl_item.hir_id()).is_none();
 +
 +            then {
 +                show_lint(cx, impl_item);
 +            }
 +        }
 +    }
 +}
 +
 +fn show_lint(cx: &LateContext<'_>, item: &ImplItem<'_>) {
 +    let display_trait_id = get_trait_def_id(cx, &paths::DISPLAY_TRAIT).expect("Failed to get trait ID of `Display`!");
 +
 +    // Get the real type of 'self'
 +    let self_type = cx.tcx.fn_sig(item.def_id).input(0);
 +    let self_type = self_type.skip_binder().peel_refs();
 +
 +    // Emit either a warning or an error
 +    if implements_trait(cx, self_type, display_trait_id, &[]) {
 +        span_lint_and_help(
 +            cx,
 +            INHERENT_TO_STRING_SHADOW_DISPLAY,
 +            item.span,
 +            &format!(
 +                "type `{}` implements inherent method `to_string(&self) -> String` which shadows the implementation of `Display`",
-             &format!("remove the inherent method from type `{}`", self_type.to_string()),
++                self_type
 +            ),
 +            None,
-                 self_type.to_string()
++            &format!("remove the inherent method from type `{}`", self_type),
 +        );
 +    } else {
 +        span_lint_and_help(
 +            cx,
 +            INHERENT_TO_STRING,
 +            item.span,
 +            &format!(
 +                "implementation of inherent method `to_string(&self) -> String` for type `{}`",
-             &format!("implement trait `Display` for type `{}` instead", self_type.to_string()),
++                self_type
 +            ),
 +            None,
++            &format!("implement trait `Display` for type `{}` instead", self_type),
 +        );
 +    }
 +}
index 89146b4dd2c9bd5f58b61a89a2cab4b61f7b6d76,0000000000000000000000000000000000000000..9efd7aba7e83bccf09af6aea4f86da86285eeeda
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,170 @@@
-     /// Checks for `let _ = <expr>`
-     /// where expr is #[must_use]
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::{is_must_use_ty, match_type};
 +use clippy_utils::{is_must_use_func_call, paths};
 +use if_chain::if_chain;
 +use rustc_hir::{Local, PatKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// It's better to explicitly
-     /// handle the value of a #[must_use] expr
++    /// Checks for `let _ = <expr>` where expr is `#[must_use]`
 +    ///
 +    /// ### Why is this bad?
++    /// It's better to explicitly handle the value of a `#[must_use]`
++    /// expr
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn f() -> Result<u32, u32> {
 +    ///     Ok(0)
 +    /// }
 +    ///
 +    /// let _ = f();
 +    /// // is_ok() is marked #[must_use]
 +    /// let _ = f().is_ok();
 +    /// ```
 +    pub LET_UNDERSCORE_MUST_USE,
 +    restriction,
 +    "non-binding let on a `#[must_use]` expression"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let _ = sync_lock`
 +    ///
 +    /// ### Why is this bad?
 +    /// This statement immediately drops the lock instead of
 +    /// extending its lifetime to the end of the scope, which is often not intended.
 +    /// To extend lock lifetime to the end of the scope, use an underscore-prefixed
 +    /// name instead (i.e. _lock). If you want to explicitly drop the lock,
 +    /// `std::mem::drop` conveys your intention better and is less error-prone.
 +    ///
 +    /// ### Example
 +    ///
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// let _ = mutex.lock();
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// let _lock = mutex.lock();
 +    /// ```
 +    pub LET_UNDERSCORE_LOCK,
 +    correctness,
 +    "non-binding let on a synchronization lock"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let _ = <expr>`
 +    /// where expr has a type that implements `Drop`
 +    ///
 +    /// ### Why is this bad?
 +    /// This statement immediately drops the initializer
 +    /// expression instead of extending its lifetime to the end of the scope, which
 +    /// is often not intended. To extend the expression's lifetime to the end of the
 +    /// scope, use an underscore-prefixed name instead (i.e. _var). If you want to
 +    /// explicitly drop the expression, `std::mem::drop` conveys your intention
 +    /// better and is less error-prone.
 +    ///
 +    /// ### Example
 +    ///
 +    /// Bad:
 +    /// ```rust,ignore
 +    /// struct Droppable;
 +    /// impl Drop for Droppable {
 +    ///     fn drop(&mut self) {}
 +    /// }
 +    /// {
 +    ///     let _ = Droppable;
 +    ///     //               ^ dropped here
 +    ///     /* more code */
 +    /// }
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust,ignore
 +    /// {
 +    ///     let _droppable = Droppable;
 +    ///     /* more code */
 +    ///     // dropped at end of scope
 +    /// }
 +    /// ```
 +    pub LET_UNDERSCORE_DROP,
 +    pedantic,
 +    "non-binding let on a type that implements `Drop`"
 +}
 +
 +declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 +
 +const SYNC_GUARD_PATHS: [&[&str]; 3] = [
 +    &paths::MUTEX_GUARD,
 +    &paths::RWLOCK_READ_GUARD,
 +    &paths::RWLOCK_WRITE_GUARD,
 +];
 +
 +impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
 +    fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
 +        if in_external_macro(cx.tcx.sess, local.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let PatKind::Wild = local.pat.kind;
 +            if let Some(init) = local.init;
 +            then {
 +                let init_ty = cx.typeck_results().expr_ty(init);
 +                let contains_sync_guard = init_ty.walk(cx.tcx).any(|inner| match inner.unpack() {
 +                    GenericArgKind::Type(inner_ty) => {
 +                        SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path))
 +                    },
 +
 +                    GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +                });
 +                if contains_sync_guard {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_LOCK,
 +                        local.span,
 +                        "non-binding let on a synchronization lock",
 +                        None,
 +                        "consider using an underscore-prefixed named \
 +                            binding or dropping explicitly with `std::mem::drop`"
 +                    );
 +                } else if init_ty.needs_drop(cx.tcx, cx.param_env) {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_DROP,
 +                        local.span,
 +                        "non-binding `let` on a type that implements `Drop`",
 +                        None,
 +                        "consider using an underscore-prefixed named \
 +                            binding or dropping explicitly with `std::mem::drop`"
 +                    );
 +                } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_MUST_USE,
 +                        local.span,
 +                        "non-binding let on an expression with `#[must_use]` type",
 +                        None,
 +                        "consider explicitly using expression value"
 +                    );
 +                } else if is_must_use_func_call(cx, init) {
 +                    span_lint_and_help(
 +                        cx,
 +                        LET_UNDERSCORE_MUST_USE,
 +                        local.span,
 +                        "non-binding let on a result of a `#[must_use]` function",
 +                        None,
 +                        "consider explicitly using function result"
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
index 6a3ee35b41a4ba361018295205059784a15e9a64,0000000000000000000000000000000000000000..c949ee23ecc7a94858ce40214e496a881c8a08fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,303 -1,0 +1,308 @@@
 +// 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(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_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(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    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(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(if_then_panic::IF_THEN_PANIC),
 +    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(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_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_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_IDENTITY),
 +    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::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(mutex_atomic::MUTEX_ATOMIC),
 +    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_borrow::NEEDLESS_BORROW),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    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(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(reference::REF_IN_DEREF),
 +    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(to_string_in_display::TO_STRING_IN_DISPLAY),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
++    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::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_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 64b82fc0faac8593504e6de0b0653504c5eb442d,0000000000000000000000000000000000000000..c51341bdf0c233bbcd11a18a7759994acc83d8bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,95 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(double_comparison::DOUBLE_COMPARISONS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(duration_subsec::DURATION_SUBSEC),
 +    LintId::of(eval_order_dependence::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    LintId::of(get_last_with_len::GET_LAST_WITH_LEN),
 +    LintId::of(identity_op::IDENTITY_OP),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
 +    LintId::of(lifetimes::NEEDLESS_LIFETIMES),
 +    LintId::of(loops::EXPLICIT_COUNTER_LOOP),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(manual_unwrap_or::MANUAL_UNWRAP_OR),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(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_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(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
 +    LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
 +    LintId::of(precedence::PRECEDENCE),
 +    LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(reference::REF_IN_DEREF),
 +    LintId::of(repeat_once::REPEAT_ONCE),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
++    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index bbe47a0e772f7b6e5318b6af7b99f926ff3d6d70,0000000000000000000000000000000000000000..ff56a6081fb57f8cbc44c08f785b2981900147f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,72 -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::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(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(to_string_in_display::TO_STRING_IN_DISPLAY),
 +    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_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 b0be3b653664cd8f51ad4ea09b2bc6d77d7188ae,0000000000000000000000000000000000000000..e8dd3708c8ed406dc3f92b6bff81332d49d9757e
mode 100644,000000..100644
--- /dev/null
@@@ -1,509 -1,0 +1,518 @@@
 +// 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-lints")]
 +    utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::DEFAULT_LINT,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::INVALID_PATHS,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal-lints")]
 +    utils::internal_lints::PRODUCE_ICE,
 +    #[cfg(feature = "internal-lints")]
 +    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,
 +    bytecount::NAIVE_BYTECOUNT,
 +    cargo_common_metadata::CARGO_COMMON_METADATA,
 +    case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    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,
 +    dereference::EXPLICIT_DEREF_METHODS,
 +    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_method::DISALLOWED_METHOD,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_type::DISALLOWED_TYPE,
 +    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,
 +    feature_name::NEGATIVE_FEATURE_NAMES,
 +    feature_name::REDUNDANT_FEATURE_NAMES,
 +    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,
 +    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_panic::IF_THEN_PANIC,
 +    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,
 +    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,
 +    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_async_fn::MANUAL_ASYNC_FN,
 +    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_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::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::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::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,
 +    multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
 +    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_borrow::NEEDLESS_BORROW,
 +    needless_borrow::REF_BINDING_TO_REFERENCE,
 +    needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +    needless_continue::NEEDLESS_CONTINUE,
 +    needless_for_each::NEEDLESS_FOR_EACH,
 +    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,
 +    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::REDUNDANT_SLICING,
 +    redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +    ref_option_ref::REF_OPTION_REF,
 +    reference::DEREF_ADDROF,
 +    reference::REF_IN_DEREF,
 +    regex::INVALID_REGEX,
 +    regex::TRIVIAL_REGEX,
 +    repeat_once::REPEAT_ONCE,
 +    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_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_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,
 +    to_string_in_display::TO_STRING_IN_DISPLAY,
++    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::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_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_dependencies::WILDCARD_DEPENDENCIES,
 +    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 96e0b421094d622f4495a3a9be49085127a5841a,0000000000000000000000000000000000000000..1e54482a8dafdc7b5f73d3798f5fd5a04864cc2b
mode 100644,000000..100644
--- /dev/null
@@@ -1,30 -1,0 +1,31 @@@
 +// 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(disallowed_method::DISALLOWED_METHOD),
 +    LintId::of(disallowed_type::DISALLOWED_TYPE),
 +    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(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_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 6533b94e82bd5ce10c539392ca6a3c627480dbd1,0000000000000000000000000000000000000000..268349d28481182fdbac33fe20ba0d0312430fbf
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,101 @@@
 +// 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(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(bit_mask::VERBOSE_BIT_MASK),
 +    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(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_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_FLATTEN),
 +    LintId::of(methods::MAP_UNWRAP_OR),
 +    LintId::of(misc::FLOAT_CMP),
 +    LintId::of(misc::USED_UNDERSCORE_BINDING),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(mut_mut::MUT_MUT),
 +    LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
 +    LintId::of(needless_borrow::REF_BINDING_TO_REFERENCE),
 +    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(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::NON_ASCII_LITERAL),
 +    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 5432345760bc3242126b9f0978e8d90d44d62e70,0000000000000000000000000000000000000000..a0d5cf9418e0b32f7244fbd48759acb925e9997f
mode 100644,000000..100644
--- /dev/null
@@@ -1,27 -1,0 +1,29 @@@
 +// 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::perf", Some("clippy_perf"), vec![
 +    LintId::of(entry::MAP_ENTRY),
 +    LintId::of(escape::BOXED_LOCAL),
++    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
++    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(misc::CMP_OWNED),
 +    LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(vec::USELESS_VEC),
 +    LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
 +])
index 4463dea5fcb8436a12f82721865750869fc16cf1,0000000000000000000000000000000000000000..3d68a6e900958fbcfcd886007abc15c468b5ceef
mode 100644,000000..100644
--- /dev/null
@@@ -1,65 -1,0 +1,67 @@@
 +// 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(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::UNNEEDED_FIELD_PATTERN),
 +    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(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(strings::STRING_ADD),
 +    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(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 5534f9c94f367fe6a2de3d1aab5fa91846534c17,0000000000000000000000000000000000000000..ed7e827702395dc72336e5f2adaf24709324e6ac
mode 100644,000000..100644
--- /dev/null
@@@ -1,859 -1,0 +1,870 @@@
-     let disallowed_types = conf.disallowed_types.iter().cloned().collect::<FxHashSet<_>>();
-     store.register_late_pass(move || Box::new(disallowed_type::DisallowedType::new(&disallowed_types)));
 +// error-pattern:cargo-clippy
 +
 +#![feature(box_patterns)]
 +#![feature(drain_filter)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![feature(control_flow_enum)]
 +#![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)]
 +
 +// 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_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 = "metadata-collector-lint")]
 +mod deprecated_lints;
 +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 bytecount;
 +mod cargo_common_metadata;
 +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 dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_method;
 +mod disallowed_script_idents;
 +mod disallowed_type;
 +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 feature_name;
 +mod float_equality_without_abs;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
++mod format_args;
 +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_panic;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +mod implicit_saturating_sub;
 +mod inconsistent_struct_constructor;
 +mod indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +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_async_fn;
 +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 multiple_crate_versions;
 +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_borrow;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +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 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 returns;
 +mod same_name_method;
 +mod self_assignment;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +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 to_string_in_display;
++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_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;
 +mod wildcard_dependencies;
 +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) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
 +    store.register_pre_expansion_pass(|| Box::new(attrs::EarlyAttributes));
 +    store.register_pre_expansion_pass(|| Box::new(dbg_macro::DbgMacro));
 +}
 +
 +#[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)]
 +#[rustfmt::skip]
 +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-lints")]
 +    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 = "metadata-collector-lint")]
 +    {
 +        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-lints")]
 +    {
 +        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_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(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
 +    store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
 +    store.register_late_pass(|| Box::new(map_clone::MapClone));
 +    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_borrow::NeedlessBorrow::default()));
 +    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(reference::RefInDeref));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|| Box::new(to_string_in_display::ToStringInDisplay::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(if_not_else::IfNotElse));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let 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));
 +    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(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_method::DisallowedMethod::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_type::DisallowedType::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(feature_name::FeatureName));
 +    store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
 +    store.register_late_pass(move || Box::new(if_then_panic::IfThenPanic));
 +    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));
++
 +}
 +
 +#[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) {
 +    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");
 +
 +    // 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 3db1f0421ea70efb35940ed2c9ab96fbcbc935ad,0000000000000000000000000000000000000000..ecf6ad316a46124a121e0be7de6264ace3ed1098
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,89 @@@
-     ///        vec.push_value)
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::method_chain_args;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, PatKind, QPath};
 +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 unnecessary `ok()` in `while let`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling `ok()` in `while let` is unnecessary, instead match
 +    /// on `Ok(pat)`
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// while let Some(value) = iter.next().ok() {
 +    ///     vec.push(value)
 +    /// }
 +    ///
 +    /// if let Some(valie) = iter.next().ok() {
 +    ///     vec.push(value)
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// while let Ok(value) = iter.next() {
 +    ///     vec.push(value)
 +    /// }
 +    ///
 +    /// if let Ok(value) = iter.next() {
++    ///        vec.push(value)
 +    /// }
 +    /// ```
 +    pub MATCH_RESULT_OK,
 +    style,
 +    "usage of `ok()` in `let Some(pat)` statements is unnecessary, match on `Ok(pat)` instead"
 +}
 +
 +declare_lint_pass!(MatchResultOk => [MATCH_RESULT_OK]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (let_pat, let_expr, ifwhile) =
 +            if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
 +                (let_pat, let_expr, "if")
 +            } else if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
 +                (let_pat, let_expr, "while")
 +            } else {
 +                return;
 +            };
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(_, ok_span, [ref result_types_0, ..], _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
 +            if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _)  = let_pat.kind; //get operation
 +            if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
 +            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(result_types_0), sym::Result);
 +            if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
 +
 +            then {
 +
 +                let mut applicability = Applicability::MachineApplicable;
 +                let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
 +                let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
 +                let sugg = format!(
 +                    "{} let Ok({}) = {}",
 +                    ifwhile,
 +                    some_expr_string,
 +                    trimmed_ok.trim().trim_end_matches('.'),
 +                );
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_RESULT_OK,
 +                    expr.span.with_hi(let_expr.span.hi()),
 +                    "matching on `Some` with `ok()` is redundant",
 +                    &format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
 +                    sugg,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a83f38e3d516e5c74c345cc2de119f57468710e2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,171 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::is_type_diagnostic_item;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{Arm, Expr, ExprKind, MatchSource, PatKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::symbol::SymbolStr;
++use rustc_span::{sym, Span};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `match` expressions modifying the case of a string with non-compliant arms
++    ///
++    /// ### Why is this bad?
++    /// The arm is unreachable, which is likely a mistake
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let text = "Foo";
++    ///
++    /// match &*text.to_ascii_lowercase() {
++    ///     "foo" => {},
++    ///     "Bar" => {},
++    ///     _ => {},
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # let text = "Foo";
++    ///
++    /// match &*text.to_ascii_lowercase() {
++    ///     "foo" => {},
++    ///     "bar" => {},
++    ///     _ => {},
++    /// }
++    /// ```
++    pub MATCH_STR_CASE_MISMATCH,
++    correctness,
++    "creation of a case altering match expression with non-compliant arms"
++}
++
++declare_lint_pass!(MatchStrCaseMismatch => [MATCH_STR_CASE_MISMATCH]);
++
++#[derive(Debug)]
++enum CaseMethod {
++    LowerCase,
++    AsciiLowerCase,
++    UpperCase,
++    AsciiUppercase,
++}
++
++impl LateLintPass<'_> for MatchStrCaseMismatch {
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        if_chain! {
++            if !in_external_macro(cx.tcx.sess, expr.span);
++            if let ExprKind::Match(match_expr, arms, MatchSource::Normal) = expr.kind;
++            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(match_expr).kind();
++            if let ty::Str = ty.kind();
++            then {
++                let mut visitor = MatchExprVisitor {
++                    cx,
++                    case_method: None,
++                };
++
++                visitor.visit_expr(match_expr);
++
++                if let Some(case_method) = visitor.case_method {
++                    if let Some((bad_case_span, bad_case_str)) = verify_case(&case_method, arms) {
++                        lint(cx, &case_method, bad_case_span, &bad_case_str);
++                    }
++                }
++            }
++        }
++    }
++}
++
++struct MatchExprVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'tcx>,
++    case_method: Option<CaseMethod>,
++}
++
++impl<'a, 'tcx> Visitor<'tcx> for MatchExprVisitor<'a, 'tcx> {
++    type Map = Map<'tcx>;
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++
++    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
++        match ex.kind {
++            ExprKind::MethodCall(segment, _, [receiver], _)
++                if self.case_altered(&*segment.ident.as_str(), receiver) => {},
++            _ => walk_expr(self, ex),
++        }
++    }
++}
++
++impl<'a, 'tcx> MatchExprVisitor<'a, 'tcx> {
++    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
++        if let Some(case_method) = get_case_method(segment_ident) {
++            let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
++
++            if is_type_diagnostic_item(self.cx, ty, sym::String) || ty.kind() == &ty::Str {
++                self.case_method = Some(case_method);
++                return true;
++            }
++        }
++
++        false
++    }
++}
++
++fn get_case_method(segment_ident_str: &str) -> Option<CaseMethod> {
++    match segment_ident_str {
++        "to_lowercase" => Some(CaseMethod::LowerCase),
++        "to_ascii_lowercase" => Some(CaseMethod::AsciiLowerCase),
++        "to_uppercase" => Some(CaseMethod::UpperCase),
++        "to_ascii_uppercase" => Some(CaseMethod::AsciiUppercase),
++        _ => None,
++    }
++}
++
++fn verify_case<'a>(case_method: &'a CaseMethod, arms: &'a [Arm<'_>]) -> Option<(Span, SymbolStr)> {
++    let case_check = match case_method {
++        CaseMethod::LowerCase => |input: &str| -> bool { input.chars().all(char::is_lowercase) },
++        CaseMethod::AsciiLowerCase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'a'..='z')) },
++        CaseMethod::UpperCase => |input: &str| -> bool { input.chars().all(char::is_uppercase) },
++        CaseMethod::AsciiUppercase => |input: &str| -> bool { input.chars().all(|c| matches!(c, 'A'..='Z')) },
++    };
++
++    for arm in arms {
++        if_chain! {
++            if let PatKind::Lit(Expr {
++                                kind: ExprKind::Lit(lit),
++                                ..
++                            }) = arm.pat.kind;
++            if let LitKind::Str(symbol, _) = lit.node;
++            let input = symbol.as_str();
++            if !case_check(&input);
++            then {
++                return Some((lit.span, input));
++            }
++        }
++    }
++
++    None
++}
++
++fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad_case_str: &str) {
++    let (method_str, suggestion) = match case_method {
++        CaseMethod::LowerCase => ("to_lower_case", bad_case_str.to_lowercase()),
++        CaseMethod::AsciiLowerCase => ("to_ascii_lowercase", bad_case_str.to_ascii_lowercase()),
++        CaseMethod::UpperCase => ("to_uppercase", bad_case_str.to_uppercase()),
++        CaseMethod::AsciiUppercase => ("to_ascii_uppercase", bad_case_str.to_ascii_uppercase()),
++    };
++
++    span_lint_and_sugg(
++        cx,
++        MATCH_STR_CASE_MISMATCH,
++        bad_case_span,
++        "this `match` arm has a differing case than its expression",
++        &*format!("consider changing the case of this arm to respect `{}`", method_str),
++        format!("\"{}\"", suggestion),
++        Applicability::MachineApplicable,
++    );
++}
index 56d4163a6b3435cf9d6d88052c6eb1bbecd3e5ad,0000000000000000000000000000000000000000..b643fba5d328865baded5f1192cca02fa6ae979a
mode 100644,000000..100644
--- /dev/null
@@@ -1,2373 -1,0 +1,2373 @@@
-     if !has_only_ref_pats(pats.clone()) {
 +use clippy_utils::consts::{constant, miri_to_const, Constant};
 +use clippy_utils::diagnostics::{
 +    multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
 +};
 +use clippy_utils::higher;
 +use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
 +use clippy_utils::visitors::is_local_used;
 +use clippy_utils::{
 +    get_parent_expr, in_macro, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild,
 +    meets_msrv, msrvs, path_to_local, path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns,
 +    remove_blocks, strip_pat_refs,
 +};
 +use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 +use core::array;
 +use core::iter::{once, ExactSizeIterator};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{Attribute, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{CtorKind, DefKind, Res};
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_hir::{
 +    self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
 +    Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
 +};
 +use rustc_hir::{HirIdMap, HirIdSet};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, Ty, TyS, VariantDef};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::{Span, Spanned};
 +use rustc_span::sym;
 +use std::cmp::Ordering;
 +use std::collections::hash_map::Entry;
 +use std::iter;
 +use std::ops::Bound;
 +
 +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);
 +    /// }
 +    /// ```
 +    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);
 +    /// }
 +    /// ```
 +    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),
 +    /// }
 +    /// ```
 +    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();
 +    /// }
 +    /// ```
 +    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"),
 +    ///     _ => (),
 +    /// }
 +    /// ```
 +    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"),
 +    /// }
 +    /// ```
 +    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();
 +    /// ```
 +    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(_) => {},
 +    /// }
 +    /// ```
 +    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 => {},
 +    /// }
 +    /// ```
 +    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" => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    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;
 +    /// ```
 +    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);
 +    /// ```
 +    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 } => {},
 +    ///     _ => {},
 +    /// }
 +    /// ```
 +    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();
 +    /// ```
 +    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));
 +    /// ```
 +    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(),
 +    /// }
 +    /// ```
 +    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 in_external_macro(cx.sess(), expr.span) || in_macro(expr.span) {
 +            return;
 +        }
 +
 +        redundant_pattern_match::check(cx, expr);
 +
 +        if meets_msrv(self.msrv.as_ref(), &msrvs::MATCHES_MACRO) {
 +            if !check_match_like_matches(cx, expr) {
 +                lint_match_arms(cx, expr);
 +            }
 +        } else {
 +            lint_match_arms(cx, expr);
 +        }
 +
 +        if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
 +            check_single_match(cx, ex, arms, expr);
 +            check_match_bool(cx, ex, arms, expr);
 +            check_overlapping_arms(cx, ex, arms);
 +            check_wild_err_arm(cx, ex, arms);
 +            check_wild_enum_match(cx, ex, arms);
 +            check_match_as_ref(cx, ex, arms, expr);
 +            check_wild_in_or_pats(cx, arms);
 +
 +            if self.infallible_destructuring_match_linted {
 +                self.infallible_destructuring_match_linted = false;
 +            } else {
 +                check_match_single_binding(cx, ex, arms, expr);
 +            }
 +        }
 +        if let ExprKind::Match(ex, arms, _) = expr.kind {
 +            check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
 +        }
 +        if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
 +            check_match_ref_pats(cx, let_expr, once(let_pat), expr);
 +        }
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.sess(), local.span);
 +            if !in_macro(local.span);
 +            if let Some(expr) = local.init;
 +            if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind;
 +            if arms.len() == 1 && arms[0].guard.is_none();
 +            if let PatKind::TupleStruct(
 +                QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind;
 +            if args.len() == 1;
 +            if let PatKind::Binding(_, arg, ..) = strip_pat_refs(&args[0]).kind;
 +            let body = remove_blocks(arms[0].body);
 +            if path_to_local_id(body, arg);
 +
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                self.infallible_destructuring_match_linted = true;
 +                span_lint_and_sugg(
 +                    cx,
 +                    INFALLIBLE_DESTRUCTURING_MATCH,
 +                    local.span,
 +                    "you seem to be trying to use `match` to destructure a single infallible pattern. \
 +                    Consider using `let`",
 +                    "try this",
 +                    format!(
 +                        "let {}({}) = {};",
 +                        snippet_with_applicability(cx, variant_name.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, local.pat.span, "..", &mut applicability),
 +                        snippet_with_applicability(cx, target.span, "..", &mut applicability),
 +                    ),
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        if_chain! {
 +            if !in_external_macro(cx.sess(), pat.span);
 +            if !in_macro(pat.span);
 +            if let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind;
 +            if let Some(def_id) = path.res.opt_def_id();
 +            let ty = cx.tcx.type_of(def_id);
 +            if let ty::Adt(def, _) = ty.kind();
 +            if def.is_struct() || def.is_union();
 +            if fields.len() == def.non_enum_variant().fields.len();
 +
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +                    pat.span,
 +                    "unnecessary use of `..` pattern in struct binding. All fields were already bound",
 +                    None,
 +                    "consider removing `..` from this binding",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[rustfmt::skip]
 +fn check_single_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        if in_macro(expr.span) {
 +            // Don't lint match expressions present in
 +            // macro_rules! block
 +            return;
 +        }
 +        if let PatKind::Or(..) = arms[0].pat.kind {
 +            // don't lint for or patterns for now, this makes
 +            // the lint noisy in unnecessary situations
 +            return;
 +        }
 +        let els = arms[1].body;
 +        let els = if is_unit_expr(remove_blocks(els)) {
 +            None
 +        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
 +            if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
 +                // single statement/expr "else" block, don't lint
 +                return;
 +            }
 +            // block with 2+ statements or 1 expr and 1+ statement
 +            Some(els)
 +        } else {
 +            // not a block, don't lint
 +            return;
 +        };
 +
 +        let ty = cx.typeck_results().expr_ty(ex);
 +        if *ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
 +            check_single_match_single_pattern(cx, ex, arms, expr, els);
 +            check_single_match_opt_like(cx, ex, arms, expr, ty, els);
 +        }
 +    }
 +}
 +
 +fn check_single_match_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    if is_wild(arms[1].pat) {
 +        report_single_match_single_pattern(cx, ex, arms, expr, els);
 +    }
 +}
 +
 +fn report_single_match_single_pattern(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
 +    let els_str = els.map_or(String::new(), |els| {
 +        format!(" else {}", expr_block(cx, els, None, "..", Some(expr.span)))
 +    });
 +
 +    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
 +    let (msg, sugg) = if_chain! {
 +        if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind;
 +        let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex));
 +        if let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait();
 +        if let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait();
 +        if ty.is_integral() || ty.is_char() || ty.is_str()
 +            || (implements_trait(cx, ty, spe_trait_id, &[])
 +                && implements_trait(cx, ty, pe_trait_id, &[ty.into()]));
 +        then {
 +            // scrutinee derives PartialEq and the pattern is a constant.
 +            let pat_ref_count = match pat.kind {
 +                // string literals are already a reference.
 +                PatKind::Lit(Expr { kind: ExprKind::Lit(lit), .. }) if lit.node.is_str() => pat_ref_count + 1,
 +                _ => pat_ref_count,
 +            };
 +            // References are only implicitly added to the pattern, so no overflow here.
 +            // e.g. will work: match &Some(_) { Some(_) => () }
 +            // will not: match Some(_) { &Some(_) => () }
 +            let ref_count_diff = ty_ref_count - pat_ref_count;
 +
 +            // Try to remove address of expressions first.
 +            let (ex, removed) = peel_n_hir_expr_refs(ex, ref_count_diff);
 +            let ref_count_diff = ref_count_diff - removed;
 +
 +            let msg = "you seem to be trying to use `match` for an equality check. Consider using `if`";
 +            let sugg = format!(
 +                "if {} == {}{} {}{}",
 +                snippet(cx, ex.span, ".."),
 +                // PartialEq for different reference counts may not exist.
 +                "&".repeat(ref_count_diff),
 +                snippet(cx, arms[0].pat.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +                els_str,
 +            );
 +            (msg, sugg)
 +        } else {
 +            let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
 +            let sugg = format!(
 +                "if let {} = {} {}{}",
 +                snippet(cx, arms[0].pat.span, ".."),
 +                snippet(cx, ex.span, ".."),
 +                expr_block(cx, arms[0].body, None, "..", Some(expr.span)),
 +                els_str,
 +            );
 +            (msg, sugg)
 +        }
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        lint,
 +        expr.span,
 +        msg,
 +        "try this",
 +        sugg,
 +        Applicability::HasPlaceholders,
 +    );
 +}
 +
 +fn check_single_match_opt_like(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    arms: &[Arm<'_>],
 +    expr: &Expr<'_>,
 +    ty: Ty<'_>,
 +    els: Option<&Expr<'_>>,
 +) {
 +    // list of candidate `Enum`s we know will never get any more members
 +    let candidates = &[
 +        (&paths::COW, "Borrowed"),
 +        (&paths::COW, "Cow::Borrowed"),
 +        (&paths::COW, "Cow::Owned"),
 +        (&paths::COW, "Owned"),
 +        (&paths::OPTION, "None"),
 +        (&paths::RESULT, "Err"),
 +        (&paths::RESULT, "Ok"),
 +    ];
 +
 +    let path = match arms[1].pat.kind {
 +        PatKind::TupleStruct(ref path, inner, _) => {
 +            // Contains any non wildcard patterns (e.g., `Err(err)`)?
 +            if !inner.iter().all(is_wild) {
 +                return;
 +            }
 +            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
 +        },
 +        PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => ident.to_string(),
 +        PatKind::Path(ref path) => {
 +            rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false))
 +        },
 +        _ => return,
 +    };
 +
 +    for &(ty_path, pat_path) in candidates {
 +        if path == *pat_path && match_type(cx, ty, ty_path) {
 +            report_single_match_single_pattern(cx, ex, arms, expr, els);
 +        }
 +    }
 +}
 +
 +fn check_match_bool(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    // Type of expression is `bool`.
 +    if *cx.typeck_results().expr_ty(ex).kind() == ty::Bool {
 +        span_lint_and_then(
 +            cx,
 +            MATCH_BOOL,
 +            expr.span,
 +            "you seem to be trying to match on a boolean expression",
 +            move |diag| {
 +                if arms.len() == 2 {
 +                    // no guards
 +                    let exprs = if let PatKind::Lit(arm_bool) = arms[0].pat.kind {
 +                        if let ExprKind::Lit(ref lit) = arm_bool.kind {
 +                            match lit.node {
 +                                LitKind::Bool(true) => Some((&*arms[0].body, &*arms[1].body)),
 +                                LitKind::Bool(false) => Some((&*arms[1].body, &*arms[0].body)),
 +                                _ => None,
 +                            }
 +                        } else {
 +                            None
 +                        }
 +                    } else {
 +                        None
 +                    };
 +
 +                    if let Some((true_expr, false_expr)) = exprs {
 +                        let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) {
 +                            (false, false) => Some(format!(
 +                                "if {} {} else {}",
 +                                snippet(cx, ex.span, "b"),
 +                                expr_block(cx, true_expr, None, "..", Some(expr.span)),
 +                                expr_block(cx, false_expr, None, "..", Some(expr.span))
 +                            )),
 +                            (false, true) => Some(format!(
 +                                "if {} {}",
 +                                snippet(cx, ex.span, "b"),
 +                                expr_block(cx, true_expr, None, "..", Some(expr.span))
 +                            )),
 +                            (true, false) => {
 +                                let test = Sugg::hir(cx, ex, "..");
 +                                Some(format!(
 +                                    "if {} {}",
 +                                    !test,
 +                                    expr_block(cx, false_expr, None, "..", Some(expr.span))
 +                                ))
 +                            },
 +                            (true, true) => None,
 +                        };
 +
 +                        if let Some(sugg) = sugg {
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using an `if`/`else` expression",
 +                                sugg,
 +                                Applicability::HasPlaceholders,
 +                            );
 +                        }
 +                    }
 +                }
 +            },
 +        );
 +    }
 +}
 +
 +fn check_overlapping_arms<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>]) {
 +    if arms.len() >= 2 && cx.typeck_results().expr_ty(ex).is_integral() {
 +        let ranges = all_ranges(cx, arms, cx.typeck_results().expr_ty(ex));
 +        let type_ranges = type_ranges(&ranges);
 +        if !type_ranges.is_empty() {
 +            if let Some((start, end)) = overlapping(&type_ranges) {
 +                span_lint_and_note(
 +                    cx,
 +                    MATCH_OVERLAPPING_ARM,
 +                    start.span,
 +                    "some ranges overlap",
 +                    Some(end.span),
 +                    "overlaps with this",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
 +    let ex_ty = cx.typeck_results().expr_ty(ex).peel_refs();
 +    if is_type_diagnostic_item(cx, ex_ty, sym::Result) {
 +        for arm in arms {
 +            if let PatKind::TupleStruct(ref path, inner, _) = arm.pat.kind {
 +                let path_str = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_qpath(path, false));
 +                if path_str == "Err" {
 +                    let mut matching_wild = inner.iter().any(is_wild);
 +                    let mut ident_bind_name = String::from("_");
 +                    if !matching_wild {
 +                        // Looking for unused bindings (i.e.: `_e`)
 +                        for pat in inner.iter() {
 +                            if let PatKind::Binding(_, id, ident, None) = pat.kind {
 +                                if ident.as_str().starts_with('_') && !is_local_used(cx, arm.body, id) {
 +                                    ident_bind_name = (&ident.name.as_str()).to_string();
 +                                    matching_wild = true;
 +                                }
 +                            }
 +                        }
 +                    }
 +                    if_chain! {
 +                        if matching_wild;
 +                        if let ExprKind::Block(block, _) = arm.body.kind;
 +                        if is_panic_block(block);
 +                        then {
 +                            // `Err(_)` or `Err(_e)` arm with `panic!` found
 +                            span_lint_and_note(cx,
 +                                MATCH_WILD_ERR_ARM,
 +                                arm.pat.span,
 +                                &format!("`Err({})` matches all errors", &ident_bind_name),
 +                                None,
 +                                "match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +enum CommonPrefixSearcher<'a> {
 +    None,
 +    Path(&'a [PathSegment<'a>]),
 +    Mixed,
 +}
 +impl CommonPrefixSearcher<'a> {
 +    fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
 +        match path {
 +            [path @ .., _] => self.with_prefix(path),
 +            [] => (),
 +        }
 +    }
 +
 +    fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
 +        match self {
 +            Self::None => *self = Self::Path(path),
 +            Self::Path(self_path)
 +                if path
 +                    .iter()
 +                    .map(|p| p.ident.name)
 +                    .eq(self_path.iter().map(|p| p.ident.name)) => {},
 +            Self::Path(_) => *self = Self::Mixed,
 +            Self::Mixed => (),
 +        }
 +    }
 +}
 +
 +fn is_hidden(cx: &LateContext<'_>, variant_def: &VariantDef) -> bool {
 +    let attrs = cx.tcx.get_attrs(variant_def.def_id);
 +    clippy_utils::attrs::is_doc_hidden(attrs) || clippy_utils::attrs::is_unstable(attrs)
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
 +    let ty = cx.typeck_results().expr_ty(ex).peel_refs();
 +    let adt_def = match ty.kind() {
 +        ty::Adt(adt_def, _)
 +            if adt_def.is_enum()
 +                && !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) =>
 +        {
 +            adt_def
 +        },
 +        _ => return,
 +    };
 +
 +    // First pass - check for violation, but don't do much book-keeping because this is hopefully
 +    // the uncommon case, and the book-keeping is slightly expensive.
 +    let mut wildcard_span = None;
 +    let mut wildcard_ident = None;
 +    let mut has_non_wild = false;
 +    for arm in arms {
 +        match peel_hir_pat_refs(arm.pat).0.kind {
 +            PatKind::Wild => wildcard_span = Some(arm.pat.span),
 +            PatKind::Binding(_, _, ident, None) => {
 +                wildcard_span = Some(arm.pat.span);
 +                wildcard_ident = Some(ident);
 +            },
 +            _ => has_non_wild = true,
 +        }
 +    }
 +    let wildcard_span = match wildcard_span {
 +        Some(x) if has_non_wild => x,
 +        _ => return,
 +    };
 +
 +    // Accumulate the variants which should be put in place of the wildcard because they're not
 +    // already covered.
 +    let has_hidden = adt_def.variants.iter().any(|x| is_hidden(cx, x));
 +    let mut missing_variants: Vec<_> = adt_def.variants.iter().filter(|x| !is_hidden(cx, x)).collect();
 +
 +    let mut path_prefix = CommonPrefixSearcher::None;
 +    for arm in arms {
 +        // Guards mean that this case probably isn't exhaustively covered. Technically
 +        // this is incorrect, as we should really check whether each variant is exhaustively
 +        // covered by the set of guards that cover it, but that's really hard to do.
 +        recurse_or_patterns(arm.pat, |pat| {
 +            let path = match &peel_hir_pat_refs(pat).0.kind {
 +                PatKind::Path(path) => {
 +                    #[allow(clippy::match_same_arms)]
 +                    let id = match cx.qpath_res(path, pat.hir_id) {
 +                        Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
 +                        Res::Def(_, id) => id,
 +                        _ => return,
 +                    };
 +                    if arm.guard.is_none() {
 +                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
 +                    }
 +                    path
 +                },
 +                PatKind::TupleStruct(path, patterns, ..) => {
 +                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
 +                            missing_variants.retain(|e| e.ctor_def_id != Some(id));
 +                        }
 +                    }
 +                    path
 +                },
 +                PatKind::Struct(path, patterns, ..) => {
 +                    if let Some(id) = cx.qpath_res(path, pat.hir_id).opt_def_id() {
 +                        if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
 +                            missing_variants.retain(|e| e.def_id != id);
 +                        }
 +                    }
 +                    path
 +                },
 +                _ => return,
 +            };
 +            match path {
 +                QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
 +                QPath::TypeRelative(
 +                    hir::Ty {
 +                        kind: TyKind::Path(QPath::Resolved(_, path)),
 +                        ..
 +                    },
 +                    _,
 +                ) => path_prefix.with_prefix(path.segments),
 +                _ => (),
 +            }
 +        });
 +    }
 +
 +    let format_suggestion = |variant: &VariantDef| {
 +        format!(
 +            "{}{}{}{}",
 +            if let Some(ident) = wildcard_ident {
 +                format!("{} @ ", ident.name)
 +            } else {
 +                String::new()
 +            },
 +            if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
 +                let mut s = String::new();
 +                for seg in path_prefix {
 +                    s.push_str(&seg.ident.as_str());
 +                    s.push_str("::");
 +                }
 +                s
 +            } else {
 +                let mut s = cx.tcx.def_path_str(adt_def.did);
 +                s.push_str("::");
 +                s
 +            },
 +            variant.ident.name,
 +            match variant.ctor_kind {
 +                CtorKind::Fn if variant.fields.len() == 1 => "(_)",
 +                CtorKind::Fn => "(..)",
 +                CtorKind::Const => "",
 +                CtorKind::Fictive => "{ .. }",
 +            }
 +        )
 +    };
 +
 +    match missing_variants.as_slice() {
 +        [] => (),
 +        [x] if !adt_def.is_variant_list_non_exhaustive() && !has_hidden => span_lint_and_sugg(
 +            cx,
 +            MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +            wildcard_span,
 +            "wildcard matches only a single variant and will also match any future added variants",
 +            "try this",
 +            format_suggestion(x),
 +            Applicability::MaybeIncorrect,
 +        ),
 +        variants => {
 +            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
 +            let message = if adt_def.is_variant_list_non_exhaustive() || has_hidden {
 +                suggestions.push("_".into());
 +                "wildcard matches known variants and will also match future added variants"
 +            } else {
 +                "wildcard match will also match any future added variants"
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                WILDCARD_ENUM_MATCH_ARM,
 +                wildcard_span,
 +                message,
 +                "try this",
 +                suggestions.join(" | "),
 +                Applicability::MaybeIncorrect,
 +            );
 +        },
 +    };
 +}
 +
 +// If the block contains only a `panic!` macro (as expression or statement)
 +fn is_panic_block(block: &Block<'_>) -> bool {
 +    match (&block.expr, block.stmts.len(), block.stmts.first()) {
 +        (&Some(exp), 0, _) => is_expn_of(exp.span, "panic").is_some() && is_expn_of(exp.span, "unreachable").is_none(),
 +        (&None, 1, Some(stmt)) => {
 +            is_expn_of(stmt.span, "panic").is_some() && is_expn_of(stmt.span, "unreachable").is_none()
 +        },
 +        _ => false,
 +    }
 +}
 +
 +fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
 +where
 +    'b: 'a,
 +    I: Clone + Iterator<Item = &'a Pat<'b>>,
 +{
- fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool
++    if !has_multiple_ref_pats(pats.clone()) {
 +        return;
 +    }
 +
 +    let (first_sugg, msg, title);
 +    let span = ex.span.source_callsite();
 +    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
 +        first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
 +        msg = "try";
 +        title = "you don't need to add `&` to both the expression and the patterns";
 +    } else {
 +        first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
 +        msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
 +        title = "you don't need to add `&` to all patterns";
 +    }
 +
 +    let remaining_suggs = pats.filter_map(|pat| {
 +        if let PatKind::Ref(refp, _) = pat.kind {
 +            Some((pat.span, snippet(cx, refp.span, "..").to_string()))
 +        } else {
 +            None
 +        }
 +    });
 +
 +    span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
 +        if !expr.span.from_expansion() {
 +            multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
 +        }
 +    });
 +}
 +
 +fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
 +        let arm_ref: Option<BindingAnnotation> = if is_none_arm(cx, &arms[0]) {
 +            is_ref_some_arm(cx, &arms[1])
 +        } else if is_none_arm(cx, &arms[1]) {
 +            is_ref_some_arm(cx, &arms[0])
 +        } else {
 +            None
 +        };
 +        if let Some(rb) = arm_ref {
 +            let suggestion = if rb == BindingAnnotation::Ref {
 +                "as_ref"
 +            } else {
 +                "as_mut"
 +            };
 +
 +            let output_ty = cx.typeck_results().expr_ty(expr);
 +            let input_ty = cx.typeck_results().expr_ty(ex);
 +
 +            let cast = if_chain! {
 +                if let ty::Adt(_, substs) = input_ty.kind();
 +                let input_ty = substs.type_at(0);
 +                if let ty::Adt(_, substs) = output_ty.kind();
 +                let output_ty = substs.type_at(0);
 +                if let ty::Ref(_, output_ty, _) = *output_ty.kind();
 +                if input_ty != output_ty;
 +                then {
 +                    ".map(|x| x as _)"
 +                } else {
 +                    ""
 +                }
 +            };
 +
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_and_sugg(
 +                cx,
 +                MATCH_AS_REF,
 +                expr.span,
 +                &format!("use `{}()` instead", suggestion),
 +                "try this",
 +                format!(
 +                    "{}.{}(){}",
 +                    snippet_with_applicability(cx, ex.span, "_", &mut applicability),
 +                    suggestion,
 +                    cast,
 +                ),
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
 +    for arm in arms {
 +        if let PatKind::Or(fields) = arm.pat.kind {
 +            // look for multiple fields in this arm that contains at least one Wild pattern
 +            if fields.len() > 1 && fields.iter().any(is_wild) {
 +                span_lint_and_help(
 +                    cx,
 +                    WILDCARD_IN_OR_PATTERNS,
 +                    arm.pat.span,
 +                    "wildcard pattern covers any other pattern as it will match anyway",
 +                    None,
 +                    "consider handling `_` separately",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
 +fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
 +    if let Some(higher::IfLet {
 +        let_pat,
 +        let_expr,
 +        if_then,
 +        if_else: Some(if_else),
 +    }) = higher::IfLet::hir(cx, expr)
 +    {
 +        return find_matches_sugg(
 +            cx,
 +            let_expr,
 +            array::IntoIter::new([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
 +            expr,
 +            true,
 +        );
 +    }
 +
 +    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
 +}
 +
 +/// 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,
 +    }
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
 +        return;
 +    }
 +
 +    // 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;
 +        }
 +    }
 +
 +    let matched_vars = ex.span;
 +    let bind_names = arms[0].pat.span;
 +    let match_body = remove_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
 +}
 +
 +/// Gets all arms that are unbounded `PatRange`s.
 +fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>) -> Vec<SpannedRange<Constant>> {
 +    arms.iter()
 +        .filter_map(|arm| {
 +            if let Arm { pat, guard: None, .. } = *arm {
 +                if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
 +                    let lhs = match lhs {
 +                        Some(lhs) => constant(cx, cx.typeck_results(), lhs)?.0,
 +                        None => miri_to_const(ty.numeric_min_val(cx.tcx)?)?,
 +                    };
 +                    let rhs = match rhs {
 +                        Some(rhs) => constant(cx, cx.typeck_results(), rhs)?.0,
 +                        None => miri_to_const(ty.numeric_max_val(cx.tcx)?)?,
 +                    };
 +                    let rhs = match range_end {
 +                        RangeEnd::Included => Bound::Included(rhs),
 +                        RangeEnd::Excluded => Bound::Excluded(rhs),
 +                    };
 +                    return Some(SpannedRange {
 +                        span: pat.span,
 +                        node: (lhs, rhs),
 +                    });
 +                }
 +
 +                if let PatKind::Lit(value) = pat.kind {
 +                    let value = constant(cx, cx.typeck_results(), value)?.0;
 +                    return Some(SpannedRange {
 +                        span: pat.span,
 +                        node: (value.clone(), Bound::Included(value)),
 +                    });
 +                }
 +            }
 +            None
 +        })
 +        .collect()
 +}
 +
 +#[derive(Debug, Eq, PartialEq)]
 +pub struct SpannedRange<T> {
 +    pub span: Span,
 +    pub node: (T, Bound<T>),
 +}
 +
 +type TypedRanges = Vec<SpannedRange<u128>>;
 +
 +/// Gets all `Int` ranges or all `Uint` ranges. Mixed types are an error anyway
 +/// and other types than
 +/// `Uint` and `Int` probably don't make sense.
 +fn type_ranges(ranges: &[SpannedRange<Constant>]) -> TypedRanges {
 +    ranges
 +        .iter()
 +        .filter_map(|range| match range.node {
 +            (Constant::Int(start), Bound::Included(Constant::Int(end))) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Included(end)),
 +            }),
 +            (Constant::Int(start), Bound::Excluded(Constant::Int(end))) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Excluded(end)),
 +            }),
 +            (Constant::Int(start), Bound::Unbounded) => Some(SpannedRange {
 +                span: range.span,
 +                node: (start, Bound::Unbounded),
 +            }),
 +            _ => None,
 +        })
 +        .collect()
 +}
 +
 +// Checks if arm has the form `None => None`
 +fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +    matches!(arm.pat.kind, PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone))
 +}
 +
 +// Checks if arm has the form `Some(ref v) => Some(v)` (checks for `ref` and `ref mut`)
 +fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotation> {
 +    if_chain! {
 +        if let PatKind::TupleStruct(ref qpath, [first_pat, ..], _) = arm.pat.kind;
 +        if is_lang_ctor(cx, qpath, OptionSome);
 +        if let PatKind::Binding(rb, .., ident, _) = first_pat.kind;
 +        if rb == BindingAnnotation::Ref || rb == BindingAnnotation::RefMut;
 +        if let ExprKind::Call(e, args) = remove_blocks(arm.body).kind;
 +        if let ExprKind::Path(ref some_path) = e.kind;
 +        if is_lang_ctor(cx, some_path, OptionSome) && args.len() == 1;
 +        if let ExprKind::Path(QPath::Resolved(_, path2)) = args[0].kind;
 +        if path2.segments.len() == 1 && ident.name == path2.segments[0].ident.name;
 +        then {
 +            return Some(rb)
 +        }
 +    }
 +    None
 +}
 +
-     let mut at_least_one_is_true = false;
++fn has_multiple_ref_pats<'a, 'b, I>(pats: I) -> bool
 +where
 +    'b: 'a,
 +    I: Iterator<Item = &'a Pat<'b>>,
 +{
-                 at_least_one_is_true = true;
++    let mut ref_count = 0;
 +    for opt in pats.map(|pat| match pat.kind {
 +        PatKind::Ref(..) => Some(true), // &-patterns
 +        PatKind::Wild => Some(false),   // an "anything" wildcard is also fine
 +        _ => None,                      // any other pattern is not fine
 +    }) {
 +        if let Some(inner) = opt {
 +            if inner {
-     at_least_one_is_true
++                ref_count += 1;
 +            }
 +        } else {
 +            return false;
 +        }
 +    }
++    ref_count > 1
 +}
 +
 +pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
 +where
 +    T: Copy + Ord,
 +{
 +    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
 +    enum Kind<'a, T> {
 +        Start(T, &'a SpannedRange<T>),
 +        End(Bound<T>, &'a SpannedRange<T>),
 +    }
 +
 +    impl<'a, T: Copy> Kind<'a, T> {
 +        fn range(&self) -> &'a SpannedRange<T> {
 +            match *self {
 +                Kind::Start(_, r) | Kind::End(_, r) => r,
 +            }
 +        }
 +
 +        fn value(self) -> Bound<T> {
 +            match self {
 +                Kind::Start(t, _) => Bound::Included(t),
 +                Kind::End(t, _) => t,
 +            }
 +        }
 +    }
 +
 +    impl<'a, T: Copy + Ord> PartialOrd for Kind<'a, T> {
 +        fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
 +            Some(self.cmp(other))
 +        }
 +    }
 +
 +    impl<'a, T: Copy + Ord> Ord for Kind<'a, T> {
 +        fn cmp(&self, other: &Self) -> Ordering {
 +            match (self.value(), other.value()) {
 +                (Bound::Included(a), Bound::Included(b)) | (Bound::Excluded(a), Bound::Excluded(b)) => a.cmp(&b),
 +                // Range patterns cannot be unbounded (yet)
 +                (Bound::Unbounded, _) | (_, Bound::Unbounded) => unimplemented!(),
 +                (Bound::Included(a), Bound::Excluded(b)) => match a.cmp(&b) {
 +                    Ordering::Equal => Ordering::Greater,
 +                    other => other,
 +                },
 +                (Bound::Excluded(a), Bound::Included(b)) => match a.cmp(&b) {
 +                    Ordering::Equal => Ordering::Less,
 +                    other => other,
 +                },
 +            }
 +        }
 +    }
 +
 +    let mut values = Vec::with_capacity(2 * ranges.len());
 +
 +    for r in ranges {
 +        values.push(Kind::Start(r.node.0, r));
 +        values.push(Kind::End(r.node.1, r));
 +    }
 +
 +    values.sort();
 +
 +    for (a, b) in iter::zip(&values, values.iter().skip(1)) {
 +        match (a, b) {
 +            (&Kind::Start(_, ra), &Kind::End(_, rb)) => {
 +                if ra.node != rb.node {
 +                    return Some((ra, rb));
 +                }
 +            },
 +            (&Kind::End(a, _), &Kind::Start(b, _)) if a != Bound::Included(b) => (),
 +            _ => {
 +                // skip if the range `a` is completely included into the range `b`
 +                if let Ordering::Equal | Ordering::Less = a.cmp(b) {
 +                    let kind_a = Kind::End(a.range().node.1, a.range());
 +                    let kind_b = Kind::End(b.range().node.1, b.range());
 +                    if let Ordering::Equal | Ordering::Greater = kind_a.cmp(&kind_b) {
 +                        return None;
 +                    }
 +                }
 +                return Some((a.range(), b.range()));
 +            },
 +        }
 +    }
 +
 +    None
 +}
 +
 +mod redundant_pattern_match {
 +    use super::REDUNDANT_PATTERN_MATCHING;
 +    use clippy_utils::diagnostics::span_lint_and_then;
 +    use clippy_utils::higher;
 +    use clippy_utils::source::{snippet, snippet_with_applicability};
 +    use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
 +    use clippy_utils::{is_lang_ctor, is_qpath_def_path, 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, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
 +    use rustc_hir::{
 +        intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
 +        Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
 +    };
 +    use rustc_lint::LateContext;
 +    use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
 +    use rustc_span::sym;
 +
 +    pub 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());
 +        }
 +        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) {
 +            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(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +        type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 +    }
 +
 +    fn type_needs_ordered_drop_inner(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(_) => ty.tuple_fields().any(|ty| type_needs_ordered_drop_inner(cx, ty, seen)),
 +                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(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> {
 +            type Map = ErasedMap<'tcx>;
 +            fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +                NestedVisitorMap::None
 +            }
 +
 +            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
 +        let mut kind = &let_pat.kind;
 +        // if we have &None for example, peel it so we can detect "if let None = x"
 +        if let PatKind::Ref(inner, _mutability) = kind {
 +            kind = &inner.kind;
 +        }
 +        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 kind {
 +            PatKind::TupleStruct(ref path, [sub_pat], _) => {
 +                if let PatKind::Wild = sub_pat.kind {
 +                    if is_lang_ctor(cx, path, ResultOk) {
 +                        ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
 +                    } else if is_lang_ctor(cx, path, ResultErr) {
 +                        ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
 +                    } else if is_lang_ctor(cx, path, OptionSome) {
 +                        ("is_some()", op_ty)
 +                    } else if is_lang_ctor(cx, path, PollReady) {
 +                        ("is_ready()", op_ty)
 +                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
 +                        ("is_ipv4()", op_ty)
 +                    } else if is_qpath_def_path(cx, path, sub_pat.hir_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,
 +            _ => 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 mut app = if needs_drop {
 +                    Applicability::MaybeIncorrect
 +                } else {
 +                    Applicability::MachineApplicable
 +                };
 +                let sugg = snippet_with_applicability(cx, op_span, "_", &mut app);
 +
 +                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");
 +                }
 +            },
 +        );
 +    }
 +
 +    fn find_sugg_for_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 body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
 +            && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
 +        {
 +            (&(*arms[0].body).kind, &(*arms[1].body).kind)
 +        } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
 +            && is_qpath_def_path(cx, path_left, arms[0].pat.hir_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,
 +        }
 +    }
 +}
 +
 +#[test]
 +fn test_overlapping() {
 +    use rustc_span::source_map::DUMMY_SP;
 +
 +    let sp = |s, e| SpannedRange {
 +        span: DUMMY_SP,
 +        node: (s, e),
 +    };
 +
 +    assert_eq!(None, overlapping::<u8>(&[]));
 +    assert_eq!(None, overlapping(&[sp(1, Bound::Included(4))]));
 +    assert_eq!(
 +        None,
 +        overlapping(&[sp(1, Bound::Included(4)), sp(5, Bound::Included(6))])
 +    );
 +    assert_eq!(
 +        None,
 +        overlapping(&[
 +            sp(1, Bound::Included(4)),
 +            sp(5, Bound::Included(6)),
 +            sp(10, Bound::Included(11))
 +        ],)
 +    );
 +    assert_eq!(
 +        Some((&sp(1, Bound::Included(4)), &sp(3, Bound::Included(6)))),
 +        overlapping(&[sp(1, Bound::Included(4)), sp(3, Bound::Included(6))])
 +    );
 +    assert_eq!(
 +        Some((&sp(5, Bound::Included(6)), &sp(6, Bound::Included(11)))),
 +        overlapping(&[
 +            sp(1, Bound::Included(4)),
 +            sp(5, Bound::Included(6)),
 +            sp(6, Bound::Included(11))
 +        ],)
 +    );
 +}
 +
 +/// Implementation of `MATCH_SAME_ARMS`.
 +fn lint_match_arms<'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()
 +        };
 +
 +        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);
 +
 +            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 TyS::same_type(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
 +                    }
 +                }
 +            };
 +            // 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 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");
 +
 +                    // 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 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 b26d11c0d6b0d8620a77e985e0dc023e16c2bffc,0000000000000000000000000000000000000000..26c29fbb289cb7f8d11e1aa41ce5d22d46a07cb0
mode 100644,000000..100644
--- /dev/null
@@@ -1,2469 -1,0 +1,2468 @@@
-     /// **What it does:** Checks for usages of `str::splitn(2, _)`
-     ///
-     /// **Why is this bad?** `split_once` is both clearer in intent and slightly more efficient.
-     ///
-     /// **Known problems:** None.
 +mod bind_instead_of_map;
 +mod bytes_nth;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
 +mod iter_skip_next;
 +mod iterator_step_by_zero;
 +mod manual_saturating_arithmetic;
 +mod manual_split_once;
 +mod manual_str_repeat;
 +mod map_collect_result_unit;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
 +mod ok_expect;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
 +mod uninit_assumed_init;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_lazy_eval;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 +use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
 +use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::SymbolStr;
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    pub FLAT_MAP_OPTION,
 +    pedantic,
 +    "used `flat_map` where `filter_map` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.unwrap();
 +    ///
 +    /// // Good
 +    /// opt.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<usize, ()> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// res.unwrap();
 +    ///
 +    /// // Good
 +    /// res.expect("more helpful message");
 +    /// ```
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.expect()` calls on `Option`s and `Result`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.expect("one");
 +    ///
 +    /// // Good
 +    /// let opt = Some(1);
 +    /// opt?;
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let res: Result<usize, ()> = Ok(1);
 +    ///
 +    /// // Bad
 +    /// res.expect("one");
 +    ///
 +    /// // Good
 +    /// res?;
 +    /// # Ok::<(), ()>(())
 +    /// ```
 +    pub EXPECT_USED,
 +    restriction,
 +    "using `.expect()` on `Result` or `Option`, which might be better handled"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods that should live in a trait
 +    /// implementation of a `std` trait (see [llogiq's blog
 +    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
 +    /// information) instead of an inherent implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// Implementing the traits improve ergonomics for users of
 +    /// the code, often with very little cost. Also people seeing a `mul(...)`
 +    /// method
 +    /// may expect `*` to work equally, so you should have good reason to disappoint
 +    /// them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn add(&self, other: &X) -> X {
 +    ///         // ..
 +    /// # X
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SHOULD_IMPLEMENT_TRAIT,
 +    style,
 +    "defining a method that should be implementing a std trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with certain name prefixes and which
 +    /// doesn't match how self is taken. The actual rules are:
 +    ///
 +    /// |Prefix |Postfix     |`self` taken           | `self` type  |
 +    /// |-------|------------|-----------------------|--------------|
 +    /// |`as_`  | none       |`&self` or `&mut self` | any          |
 +    /// |`from_`| none       | none                  | any          |
 +    /// |`into_`| none       |`self`                 | any          |
 +    /// |`is_`  | none       |`&self` or none        | any          |
 +    /// |`to_`  | `_mut`     |`&mut self`            | any          |
 +    /// |`to_`  | not `_mut` |`self`                 | `Copy`       |
 +    /// |`to_`  | not `_mut` |`&self`                | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    pub WRONG_SELF_CONVENTION,
 +    style,
 +    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    ///
 +    /// // Bad
 +    /// x.ok().expect("why did I do this again?");
 +    ///
 +    /// // Good
 +    /// x.expect("why did I do this again?");
 +    /// ```
 +    pub OK_EXPECT,
 +    style,
 +    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    ///
 +    /// // Bad
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    ///
 +    /// // Good
 +    /// x.unwrap_or_default();
 +    /// ```
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    ///
 +    /// // Bad
 +    /// x.map(|a| a + 1).unwrap_or(0);
 +    ///
 +    /// // Good
 +    /// x.map_or(0, |a| a + 1);
 +    /// ```
 +    ///
 +    /// // or
 +    ///
 +    /// ```rust
 +    /// # let x: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    ///
 +    /// // Bad
 +    /// x.map(|a| a + 1).unwrap_or_else(some_function);
 +    ///
 +    /// // Good
 +    /// x.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    ///
 +    /// // Bad
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    ///
 +    /// // Good
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    ///
 +    /// // Bad
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    ///
 +    /// // Good
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// ```
 +    pub MAP_FLATTEN,
 +    pedantic,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// Bad:
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Good:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// let _ = "hello world".find("world").is_none();
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// let _ = !"hello world".contains("world");
 +    /// ```
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
 +    /// `unwrap_or_default` instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called and potentially
 +    /// allocate an object acting as the default.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantic of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or(String::new());
 +    /// ```
 +    /// this can instead be written:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    pub OR_FUN_CALL,
 +    perf,
 +    "using any `*or` method with a function call, which suggests `*or_else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    /// or
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    /// this can instead be written:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// // Bad
 +    /// x.clone();
 +    ///
 +    /// // Good
 +    /// Rc::clone(&x);
 +    /// ```
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    /// _.split("x");
 +    ///
 +    /// // Good
 +    /// _.split('x');
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// // Bad
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    ///
 +    /// // Good
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// // Bad
 +    /// a.extend(b.drain(..));
 +    ///
 +    /// // Good
 +    /// a.append(&mut b);
 +    /// ```
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
 +    "using vec.append(&mut vec) to move the full range of a vecor to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    ///
 +    /// // Bad
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    ///
 +    /// // Good
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    /// This could be written as:
 +    /// ```rust
 +    /// let _ = (0..3).any(|x| x > 2);
 +    /// ```
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls which could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _ = (&vec![3, 4, 5]).into_iter();
 +    ///
 +    /// // Good
 +    /// let _ = (&vec![3, 4, 5]).iter();
 +    /// ```
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    /// Could be written as
 +    /// ```rust
 +    /// let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::iter::FromIterator;
 +    ///
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let _ = "Hello".bytes().nth(3);
 +    ///
 +    /// // Good
 +    /// let _ = "Hello".as_bytes().get(3);
 +    /// ```
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.iter().count();
 +    /// let _ = &some_vec[..].iter().count();
 +    ///
 +    /// // Good
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let _ = some_vec.len();
 +    /// let _ = &some_vec[..].len();
 +    /// ```
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // use x
 +    /// }
 +    ///
 +    /// // Good
 +    /// let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // use x
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Bad
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    ///
 +    /// // Good
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +declare_clippy_lint! {
-     /// **Example:**
++    /// ### What it does
++    /// Checks for usages of `str::splitn(2, _)`
 +    ///
++    /// ### Why is this bad?
++    /// `split_once` is both clearer in intent and slightly more efficient.
 +    ///
++    /// ### Example
 +    /// ```rust,ignore
 +    /// // Bad
 +    ///  let (key, value) = _.splitn(2, '=').next_tuple()?;
 +    ///  let value = _.splitn(2, '=').nth(1)?;
 +    ///
 +    /// // Good
 +    /// let (key, value) = _.split_once('=')?;
 +    /// let value = _.split_once('=')?.1;
 +    /// ```
 +    pub MANUAL_SPLIT_ONCE,
 +    complexity,
 +    "replace `.splitn(2, pat)` with `.split_once(pat)`"
 +}
 +
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
 +    EXTEND_WITH_DRAIN,
 +    MANUAL_SPLIT_ONCE
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(SymbolStr, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, span, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) {
 +            return Some((path.ident.name.as_str(), args, span));
 +        }
 +    }
 +    None
 +}
 +
 +/// Same as `method_call` but the `SymbolStr` is dereferenced into a temporary `&str`
 +macro_rules! method_call {
 +    ($expr:expr) => {
 +        method_call($expr)
 +            .as_ref()
 +            .map(|&(ref name, args, span)| (&**name, args, span))
 +    };
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if in_macro(expr.span) {
 +            return;
 +        }
 +
 +        check_methods(cx, expr, self.msrv.as_ref());
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, ref method_span, args, _) => {
 +                or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
 +                expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
 +                single_char_add_str::check(cx, expr, args);
 +                into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, args);
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = &method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
 +                    // check missing trait implementations
 +                    for method_config in &TRAIT_METHODS {
 +                        if name == method_config.method_name &&
 +                            sig.decl.inputs.len() == method_config.param_count &&
 +                            method_config.output_type.matches(&sig.decl.output) &&
 +                            method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
 +                            fn_header_equals(method_config.fn_header, sig.header) &&
 +                            method_config.lifetime_param_cond(impl_item)
 +                        {
 +                            span_lint_and_help(
 +                                cx,
 +                                SHOULD_IMPLEMENT_TRAIT,
 +                                impl_item.span,
 +                                &format!(
 +                                    "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                    method_config.method_name,
 +                                    method_config.trait_name,
 +                                    method_config.method_name
 +                                ),
 +                                None,
 +                                &format!(
 +                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                    method_config.trait_name
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        &name,
 +                        self_ty,
 +                        first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(cx.tcx, ret_ty, self_adt) {
 +                    return;
 +                }
 +            } else if contains_ty(cx.tcx, ret_ty, self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(cx.tcx, projection_predicate.ty, self_adt) {
 +                                return;
 +                            }
 +                        } else if contains_ty(cx.tcx, projection_predicate.ty, self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && !TyS::same_type(ret_ty, self_ty) {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +                wrong_self_convention::check(
 +                    cx,
 +                    &item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +            if !contains_ty(cx.tcx, ret_ty, self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) {
 +    if let Some((name, [recv, args @ ..], span)) = method_call!(expr) {
 +        match (name, args) {
 +            ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                zst_offset::check(cx, expr, recv);
 +            },
 +            ("and_then", [arg]) => {
 +                let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                if !biom_option_linted && !biom_result_linted {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                }
 +            },
 +            ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
 +            ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
 +            ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
 +            ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv),
 +            ("collect", []) => match method_call!(recv) {
 +                Some(("cloned", [recv2], _)) => iter_cloned_collect::check(cx, expr, recv2),
 +                Some(("map", [m_recv, m_arg], _)) => {
 +                    map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
 +                },
 +                Some(("take", [take_self_arg, take_arg], _)) => {
 +                    if meets_msrv(msrv, &msrvs::STR_REPEAT) {
 +                        manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                    }
 +                },
 +                _ => {},
 +            },
 +            ("count", []) => match method_call!(recv) {
 +                Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
 +                    iter_count::check(cx, expr, recv2, name);
 +                },
 +                Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
 +                _ => {},
 +            },
 +            ("expect", [_]) => match method_call!(recv) {
 +                Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                _ => expect_used::check(cx, expr, recv),
 +            },
 +            ("extend", [arg]) => {
 +                string_extend_chars::check(cx, expr, recv, arg);
 +                extend_with_drain::check(cx, expr, recv, arg);
 +            },
 +            ("filter_map", [arg]) => {
 +                unnecessary_filter_map::check(cx, expr, arg);
 +                filter_map_identity::check(cx, expr, arg, span);
 +            },
 +            ("flat_map", [arg]) => {
 +                flat_map_identity::check(cx, expr, arg, span);
 +                flat_map_option::check(cx, expr, arg, span);
 +            },
 +            ("flatten", []) => {
 +                if let Some(("map", [recv, map_arg], _)) = method_call!(recv) {
 +                    map_flatten::check(cx, expr, recv, map_arg);
 +                }
 +            },
 +            ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +            ("for_each", [_]) => {
 +                if let Some(("inspect", [_, _], span2)) = method_call!(recv) {
 +                    inspect_for_each::check(cx, expr, span2);
 +                }
 +            },
 +            ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
 +            ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +            ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +            ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
 +            ("map", [m_arg]) => {
 +                if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) {
 +                    match (name, args) {
 +                        ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
 +                        ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv),
 +                        ("filter", [f_arg]) => {
 +                            filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                        },
 +                        ("find", [f_arg]) => filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true),
 +                        _ => {},
 +                    }
 +                }
 +                map_identity::check(cx, expr, recv, m_arg, span);
 +            },
 +            ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
 +            ("next", []) => {
 +                if let Some((name, [recv, args @ ..], _)) = method_call!(recv) {
 +                    match (name, args) {
 +                        ("filter", [arg]) => filter_next::check(cx, expr, recv, arg),
 +                        ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv),
 +                        ("iter", []) => iter_next_slice::check(cx, expr, recv),
 +                        ("skip", [arg]) => iter_skip_next::check(cx, expr, recv, arg),
 +                        ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                        _ => {},
 +                    }
 +                }
 +            },
 +            ("nth", [n_arg]) => match method_call!(recv) {
 +                Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +            },
 +            ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
 +            ("or_else", [arg]) => {
 +                if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                }
 +            },
 +            ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
 +                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                    suspicious_splitn::check(cx, name, expr, recv, count);
 +                    if count == 2 && meets_msrv(msrv, &msrvs::STR_SPLIT_ONCE) {
 +                        manual_split_once::check(cx, name, expr, recv, pat_arg);
 +                    }
 +                }
 +            },
 +            ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
 +                if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                    suspicious_splitn::check(cx, name, expr, recv, count);
 +                }
 +            },
 +            ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +            ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
 +                implicit_clone::check(cx, name, expr, recv, span);
 +            },
 +            ("unwrap", []) => match method_call!(recv) {
 +                Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false),
 +                Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true),
 +                _ => unwrap_used::check(cx, expr, recv),
 +            },
 +            ("unwrap_or", [u_arg]) => match method_call!(recv) {
 +                Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
 +                    manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                },
 +                Some(("map", [m_recv, m_arg], span)) => {
 +                    option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                },
 +                _ => {},
 +            },
 +            ("unwrap_or_else", [u_arg]) => match method_call!(recv) {
 +                Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {},
 +                _ => {
 +                    unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                    unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                },
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No,
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'_>, ty: Ty<'_>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => return false,
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        match self {
 +            Self::Value => matches_value(cx, parent_ty, ty),
 +            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
 +            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
 +            Self::No => ty != parent_ty,
 +        }
 +    }
 +
 +    #[must_use]
 +    fn description(self) -> &'static str {
 +        match self {
 +            Self::Value => "`self` by value",
 +            Self::Ref => "`self` by reference",
 +            Self::RefMut => "`self` by mutable reference",
 +            Self::No => "no `self`",
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OutType {
 +    Unit,
 +    Bool,
 +    Any,
 +    Ref,
 +}
 +
 +impl OutType {
 +    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
 +        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
 +        match (self, ty) {
 +            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
 +            (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
 +            (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
 +            (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
 +            (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn is_bool(ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        matches!(path.res, Res::PrimTy(PrimTy::Bool))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
 +    expected.constness == actual.constness
 +        && expected.unsafety == actual.unsafety
 +        && expected.asyncness == actual.asyncness
 +}
index 1a5894e48d14c5ac1c935dae2231e086f248c78c,0000000000000000000000000000000000000000..ce89189bce9779e12305ca5c269ef9c4e359e30d
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,25 @@@
- use clippy_utils::{is_expr_path_def_path, match_def_path, paths};
 +use clippy_utils::diagnostics::span_lint;
- use rustc_middle::ty::{self, Ty};
++use clippy_utils::{is_expr_path_def_path, paths, ty::is_uninit_value_valid_for_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
-         if !is_maybe_uninit_ty_valid(cx, cx.typeck_results().expr_ty_adjusted(expr));
 +
 +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_path_def_path(cx, callee, &paths::MEM_MAYBEUNINIT_UNINIT);
- fn is_maybe_uninit_ty_valid(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
-     match ty.kind() {
-         ty::Array(component, _) => is_maybe_uninit_ty_valid(cx, component),
-         ty::Tuple(types) => types.types().all(|ty| is_maybe_uninit_ty_valid(cx, ty)),
-         ty::Adt(adt, _) => match_def_path(cx, adt.did, &paths::MEM_MAYBEUNINIT),
-         _ => false,
-     }
- }
++        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 667cdd8302528caec005ef878d7a8f2087d50e70,0000000000000000000000000000000000000000..b593c747498e397b76ba9819b3d7388a87f41bd0
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,169 @@@
-     /// it lints if an exported function, method, trait method with default impl,
 +use clippy_utils::diagnostics::span_lint;
 +use rustc_ast::ast;
 +use rustc_hir as hir;
 +use rustc_lint::{self, LateContext, LateLintPass, LintContext};
 +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
++    /// It lints if an exported function, method, trait method with default impl,
 +    /// or trait method impl is not `#[inline]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, it is not. Functions can be inlined across
 +    /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
 +    /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
 +    /// might intend for most of the methods in their public API to be able to be inlined across
 +    /// crates even when LTO is disabled. For these types of crates, enabling this lint might make
 +    /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
 +    /// then opt out for specific methods where this might not make sense.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn foo() {} // missing #[inline]
 +    /// fn ok() {} // ok
 +    /// #[inline] pub fn bar() {} // ok
 +    /// #[inline(always)] pub fn baz() {} // ok
 +    ///
 +    /// pub trait Bar {
 +    ///   fn bar(); // ok
 +    ///   fn def_bar() {} // missing #[inline]
 +    /// }
 +    ///
 +    /// struct Baz;
 +    /// impl Baz {
 +    ///    fn private() {} // ok
 +    /// }
 +    ///
 +    /// impl Bar for Baz {
 +    ///   fn bar() {} // ok - Baz is not exported
 +    /// }
 +    ///
 +    /// pub struct PubBaz;
 +    /// impl PubBaz {
 +    ///    fn private() {} // ok
 +    ///    pub fn not_ptrivate() {} // missing #[inline]
 +    /// }
 +    ///
 +    /// impl Bar for PubBaz {
 +    ///    fn bar() {} // missing #[inline]
 +    ///    fn def_bar() {} // missing #[inline]
 +    /// }
 +    /// ```
 +    pub MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    restriction,
 +    "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
 +}
 +
 +fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
 +    let has_inline = attrs.iter().any(|a| a.has_name(sym::inline));
 +    if !has_inline {
 +        span_lint(
 +            cx,
 +            MISSING_INLINE_IN_PUBLIC_ITEMS,
 +            sp,
 +            &format!("missing `#[inline]` for {}", desc),
 +        );
 +    }
 +}
 +
 +fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool {
 +    use rustc_session::config::CrateType;
 +
 +    cx.tcx
 +        .sess
 +        .crate_types()
 +        .iter()
 +        .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro))
 +}
 +
 +declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MissingInline {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) {
 +        if rustc_middle::lint::in_external_macro(cx.sess(), it.span) || is_executable_or_proc_macro(cx) {
 +            return;
 +        }
 +
 +        if !cx.access_levels.is_exported(it.def_id) {
 +            return;
 +        }
 +        match it.kind {
 +            hir::ItemKind::Fn(..) => {
 +                let desc = "a function";
 +                let attrs = cx.tcx.hir().attrs(it.hir_id());
 +                check_missing_inline_attrs(cx, attrs, it.span, desc);
 +            },
 +            hir::ItemKind::Trait(ref _is_auto, ref _unsafe, ref _generics, _bounds, trait_items) => {
 +                // note: we need to check if the trait is exported so we can't use
 +                // `LateLintPass::check_trait_item` here.
 +                for tit in trait_items {
 +                    let tit_ = cx.tcx.hir().trait_item(tit.id);
 +                    match tit_.kind {
 +                        hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
 +                        hir::TraitItemKind::Fn(..) => {
 +                            if tit.defaultness.has_value() {
 +                                // trait method with default body needs inline in case
 +                                // an impl is not provided
 +                                let desc = "a default trait method";
 +                                let item = cx.tcx.hir().trait_item(tit.id);
 +                                let attrs = cx.tcx.hir().attrs(item.hir_id());
 +                                check_missing_inline_attrs(cx, attrs, item.span, desc);
 +                            }
 +                        },
 +                    }
 +                }
 +            },
 +            hir::ItemKind::Const(..)
 +            | hir::ItemKind::Enum(..)
 +            | hir::ItemKind::Macro(..)
 +            | hir::ItemKind::Mod(..)
 +            | hir::ItemKind::Static(..)
 +            | hir::ItemKind::Struct(..)
 +            | hir::ItemKind::TraitAlias(..)
 +            | hir::ItemKind::GlobalAsm(..)
 +            | hir::ItemKind::TyAlias(..)
 +            | hir::ItemKind::Union(..)
 +            | hir::ItemKind::OpaqueTy(..)
 +            | hir::ItemKind::ExternCrate(..)
 +            | hir::ItemKind::ForeignMod { .. }
 +            | hir::ItemKind::Impl { .. }
 +            | hir::ItemKind::Use(..) => {},
 +        };
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        use rustc_middle::ty::{ImplContainer, TraitContainer};
 +        if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable_or_proc_macro(cx) {
 +            return;
 +        }
 +
 +        // If the item being implemented is not exported, then we don't need #[inline]
 +        if !cx.access_levels.is_exported(impl_item.def_id) {
 +            return;
 +        }
 +
 +        let desc = match impl_item.kind {
 +            hir::ImplItemKind::Fn(..) => "a method",
 +            hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(_) => return,
 +        };
 +
 +        let trait_def_id = match cx.tcx.associated_item(impl_item.def_id).container {
 +            TraitContainer(cid) => Some(cid),
 +            ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id),
 +        };
 +
 +        if let Some(trait_def_id) = trait_def_id {
 +            if trait_def_id.is_local() && !cx.access_levels.is_exported(impl_item.def_id) {
 +                // If a trait is being implemented for an item, and the
 +                // trait is not exported, we don't need #[inline]
 +                return;
 +            }
 +        }
 +
 +        let attrs = cx.tcx.hir().attrs(impl_item.hir_id());
 +        check_missing_inline_attrs(cx, attrs, impl_item.span, desc);
 +    }
 +}
index 610152a217f1e3dac93028b9b41a945f37b92468,0000000000000000000000000000000000000000..7c4cac29ba8e8c699b7a5cfdeea61678f0a477ba
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,119 @@@
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::higher;
 +use rustc_hir as hir;
 +use rustc_hir::intravisit;
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::map::Map;
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `mut mut` references.
 +    ///
 +    /// ### Why is this bad?
 +    /// Multiple `mut`s don't add anything meaningful to the
 +    /// source. This is either a copy'n'paste error, or it shows a fundamental
 +    /// misunderstanding of references.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut y = 1;
 +    /// let x = &mut &mut y;
 +    /// ```
 +    pub MUT_MUT,
 +    pedantic,
 +    "usage of double-mut refs, e.g., `&mut &mut ...`"
 +}
 +
 +declare_lint_pass!(MutMut => [MUT_MUT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MutMut {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        intravisit::walk_block(&mut MutVisitor { cx }, block);
 +    }
 +
 +    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx hir::Ty<'_>) {
 +        use rustc_hir::intravisit::Visitor;
 +
 +        MutVisitor { cx }.visit_ty(ty);
 +    }
 +}
 +
 +pub struct MutVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +        if in_external_macro(self.cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        if let Some(higher::ForLoop { arg, body, .. }) = higher::ForLoop::hir(expr) {
 +            // A `for` loop lowers to:
 +            // ```rust
 +            // match ::std::iter::Iterator::next(&mut iter) {
 +            // //                                ^^^^
 +            // ```
 +            // Let's ignore the generated code.
 +            intravisit::walk_expr(self, arg);
 +            intravisit::walk_expr(self, body);
 +        } else if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, e) = expr.kind {
 +            if let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) = e.kind {
 +                span_lint(
 +                    self.cx,
 +                    MUT_MUT,
 +                    expr.span,
 +                    "generally you want to avoid `&mut &mut _` if possible",
 +                );
 +            } else if let ty::Ref(_, _, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
 +                span_lint(
 +                    self.cx,
 +                    MUT_MUT,
 +                    expr.span,
 +                    "this expression mutably borrows a mutable reference. Consider reborrowing",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
++        if in_external_macro(self.cx.sess(), ty.span) {
++            return;
++        }
++
 +        if let hir::TyKind::Rptr(
 +            _,
 +            hir::MutTy {
 +                ty: pty,
 +                mutbl: hir::Mutability::Mut,
 +            },
 +        ) = ty.kind
 +        {
 +            if let hir::TyKind::Rptr(
 +                _,
 +                hir::MutTy {
 +                    mutbl: hir::Mutability::Mut,
 +                    ..
 +                },
 +            ) = pty.kind
 +            {
 +                span_lint(
 +                    self.cx,
 +                    MUT_MUT,
 +                    ty.span,
 +                    "generally you want to avoid `&mut &mut _` if possible",
 +                );
 +            }
 +        }
 +
 +        intravisit::walk_ty(self, ty);
 +    }
 +    fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
 +        intravisit::NestedVisitorMap::None
 +    }
 +}
index c5a5cde4b110fccbb874801a53cd72dcc9b2e747,0000000000000000000000000000000000000000..6dae8f320436fc49609aa428cc808a218f84edd7
mode 100644,000000..100644
--- /dev/null
@@@ -1,216 -1,0 +1,267 @@@
- use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
 +use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
++use clippy_utils::is_lint_allowed;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::has_drop;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::{DefKind, Res};
-     /// Similar to dead code, these statements are actually
++use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use std::ops::Deref;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
- declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION]);
- impl<'tcx> LateLintPass<'tcx> for NoEffect {
-     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
-         if let StmtKind::Semi(expr) = stmt.kind {
-             if has_no_effect(cx, expr) {
-                 span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
-             } else if let Some(reduced) = reduce_expression(cx, expr) {
-                 for e in &reduced {
-                     if e.span.from_expansion() {
-                         return;
-                     }
-                 }
-                 if let ExprKind::Index(..) = &expr.kind {
-                     let snippet;
-                     if_chain! {
-                         if let Some(arr) = snippet_opt(cx, reduced[0].span);
-                         if let Some(func) = snippet_opt(cx, reduced[1].span);
-                         then {
-                             snippet = format!("assert!({}.len() > {});", &arr, &func);
-                         } else {
-                             return;
-                         }
-                     }
-                     span_lint_hir_and_then(
-                         cx,
-                         UNNECESSARY_OPERATION,
-                         expr.hir_id,
-                         stmt.span,
-                         "unnecessary operation",
-                         |diag| {
-                             diag.span_suggestion(
-                                 stmt.span,
-                                 "statement can be written as",
-                                 snippet,
-                                 Applicability::MaybeIncorrect,
-                             );
-                         },
-                     );
++    /// Unlike dead code, these statements are actually
 +    /// executed. However, as they have no effect, all they do is make the code less
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 0;
 +    /// ```
 +    pub NO_EFFECT,
 +    complexity,
 +    "statements with no effect"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for binding to underscore prefixed variable without side-effects.
++    ///
++    /// ### Why is this bad?
++    /// Unlike dead code, these bindings are actually
++    /// executed. However, as they have no effect and shouldn't be used further on, all they
++    /// do is make the code less readable.
++    ///
++    /// ### Known problems
++    /// Further usage of this variable is not checked, which can lead to false positives if it is
++    /// used later in the code.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// let _i_serve_no_purpose = 1;
++    /// ```
++    pub NO_EFFECT_UNDERSCORE_BINDING,
++    pedantic,
++    "binding to `_` prefixed variable with no side-effect"
++}
++
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expression statements that can be reduced to a
 +    /// sub-expression.
 +    ///
 +    /// ### Why is this bad?
 +    /// Expressions by themselves often have no side-effects.
 +    /// Having such expressions reduces readability.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// compute_array()[0];
 +    /// ```
 +    pub UNNECESSARY_OPERATION,
 +    complexity,
 +    "outer expressions with no effect"
 +}
 +
++declare_lint_pass!(NoEffect => [NO_EFFECT, UNNECESSARY_OPERATION, NO_EFFECT_UNDERSCORE_BINDING]);
++
++impl<'tcx> LateLintPass<'tcx> for NoEffect {
++    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
++        if check_no_effect(cx, stmt) {
++            return;
++        }
++        check_unnecessary_operation(cx, stmt);
++    }
++}
++
++fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool {
++    if let StmtKind::Semi(expr) = stmt.kind {
++        if has_no_effect(cx, expr) {
++            span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
++            return true;
++        }
++    } else if let StmtKind::Local(local) = stmt.kind {
++        if_chain! {
++            if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id);
++            if let Some(init) = local.init;
++            if !local.pat.span.from_expansion();
++            if has_no_effect(cx, init);
++            if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
++            if ident.name.to_ident_string().starts_with('_');
++            then {
++                span_lint_hir(
++                    cx,
++                    NO_EFFECT_UNDERSCORE_BINDING,
++                    init.hir_id,
++                    stmt.span,
++                    "binding to `_` prefixed variable with no side-effect"
++                );
++                return true;
++            }
++        }
++    }
++    false
++}
++
 +fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if expr.span.from_expansion() {
 +        return false;
 +    }
 +    match expr.kind {
 +        ExprKind::Lit(..) | ExprKind::Closure(..) => true,
 +        ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)),
 +        ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b),
 +        ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => has_no_effect(cx, inner),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            !has_drop(cx, cx.typeck_results().expr_ty(expr))
 +                && fields.iter().all(|field| has_no_effect(cx, field.expr))
 +                && base.as_ref().map_or(true, |base| has_no_effect(cx, base))
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                let def_matched = matches!(
 +                    res,
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                );
 +                if def_matched || is_range_literal(expr) {
 +                    !has_drop(cx, cx.typeck_results().expr_ty(expr)) && args.iter().all(|arg| has_no_effect(cx, arg))
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| has_no_effect(cx, expr))
 +        },
 +        _ => false,
 +    }
 +}
 +
-                     let mut snippet = String::new();
-                     for e in reduced {
-                         if let Some(snip) = snippet_opt(cx, e.span) {
-                             snippet.push_str(&snip);
-                             snippet.push(';');
-                         } else {
-                             return;
-                         }
++fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
++    if_chain! {
++        if let StmtKind::Semi(expr) = stmt.kind;
++        if let Some(reduced) = reduce_expression(cx, expr);
++        if !&reduced.iter().any(|e| e.span.from_expansion());
++        then {
++            if let ExprKind::Index(..) = &expr.kind {
++                let snippet;
++                if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
++                    snippet = format!("assert!({}.len() > {});", &arr, &func);
 +                } else {
-                     span_lint_hir_and_then(
-                         cx,
-                         UNNECESSARY_OPERATION,
-                         expr.hir_id,
-                         stmt.span,
-                         "unnecessary operation",
-                         |diag| {
-                             diag.span_suggestion(
-                                 stmt.span,
-                                 "statement can be reduced to",
-                                 snippet,
-                                 Applicability::MachineApplicable,
-                             );
-                         },
-                     );
++                    return;
++                }
++                span_lint_hir_and_then(
++                    cx,
++                    UNNECESSARY_OPERATION,
++                    expr.hir_id,
++                    stmt.span,
++                    "unnecessary operation",
++                    |diag| {
++                        diag.span_suggestion(
++                            stmt.span,
++                            "statement can be written as",
++                            snippet,
++                            Applicability::MaybeIncorrect,
++                        );
++                    },
++                );
++            } else {
++                let mut snippet = String::new();
++                for e in reduced {
++                    if let Some(snip) = snippet_opt(cx, e.span) {
++                        snippet.push_str(&snip);
++                        snippet.push(';');
++                    } else {
++                        return;
 +                    }
 +                }
++                span_lint_hir_and_then(
++                    cx,
++                    UNNECESSARY_OPERATION,
++                    expr.hir_id,
++                    stmt.span,
++                    "unnecessary operation",
++                    |diag| {
++                        diag.span_suggestion(
++                            stmt.span,
++                            "statement can be reduced to",
++                            snippet,
++                            Applicability::MachineApplicable,
++                        );
++                    },
++                );
 +            }
 +        }
 +    }
 +}
 +
 +fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec<&'a Expr<'a>>> {
 +    if expr.span.from_expansion() {
 +        return None;
 +    }
 +    match expr.kind {
 +        ExprKind::Index(a, b) => Some(vec![a, b]),
 +        ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => {
 +            Some(vec![a, b])
 +        },
 +        ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
 +        ExprKind::Repeat(inner, _)
 +        | ExprKind::Cast(inner, _)
 +        | ExprKind::Type(inner, _)
 +        | ExprKind::Unary(_, inner)
 +        | ExprKind::Field(inner, _)
 +        | ExprKind::AddrOf(_, _, inner)
 +        | ExprKind::Box(inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
 +        ExprKind::Struct(_, fields, ref base) => {
 +            if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
 +                None
 +            } else {
 +                Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
 +            }
 +        },
 +        ExprKind::Call(callee, args) => {
 +            if let ExprKind::Path(ref qpath) = callee.kind {
 +                let res = cx.qpath_res(qpath, callee.hir_id);
 +                match res {
 +                    Res::Def(DefKind::Struct | DefKind::Variant | DefKind::Ctor(..), ..)
 +                        if !has_drop(cx, cx.typeck_results().expr_ty(expr)) =>
 +                    {
 +                        Some(args.iter().collect())
 +                    },
 +                    _ => None,
 +                }
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::Block(block, _) => {
 +            if block.stmts.is_empty() {
 +                block.expr.as_ref().and_then(|e| {
 +                    match block.rules {
 +                        BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
 +                        BlockCheckMode::DefaultBlock => Some(vec![&**e]),
 +                        // in case of compiler-inserted signaling blocks
 +                        BlockCheckMode::UnsafeBlock(_) => reduce_expression(cx, e),
 +                    }
 +                })
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index d180d6f922710ddc01f1e822184a4630c0e08048,0000000000000000000000000000000000000000..92a4801a8468ae3d3e592212280e8dd7959c9bc5
mode 100644,000000..100644
--- /dev/null
@@@ -1,433 -1,0 +1,434 @@@
 +//! Checks for usage of  `&Vec[_]` and `&String`.
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::ptr::get_spans;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
 +use clippy_utils::{expr_path_res, is_lint_allowed, match_any_diagnostic_items, paths};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{
 +    BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, Impl, ImplItem, ImplItemKind, Item,
 +    ItemKind, Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, MultiSpan};
 +use std::borrow::Cow;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint checks for function arguments of type `&String`
 +    /// or `&Vec` unless the references are mutable. 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
 +    /// The lint does not follow data. So if you have an
 +    /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
 +    /// changing that `.clone()` to `.to_owned()`.
 +    ///
 +    /// Other functions called from this function taking a `&String` or `&Vec`
 +    /// argument may also fail to compile if you change the argument. Applying
 +    /// this lint on them will fix the problem, but they may be in other crates.
 +    ///
 +    /// One notable example of a function that may cause issues, and which cannot
 +    /// easily be changed due to being in the standard library is `Vec::contains`.
 +    /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
 +    /// it will compile, but if a `&[T]` is passed then it will not compile.
 +    ///
 +    /// ```ignore
 +    /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
 +    ///     let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
 +    ///
 +    ///     vec_of_vecs.contains(v)
 +    /// }
 +    /// ```
 +    ///
 +    /// Also 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]) { .. }
 +    /// ```
 +    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() {
 +    ///     ..
 +    /// }
 +    /// ```
 +    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 { .. }
 +    /// ```
 +    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); }
 +    /// ```
 +    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_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
 +            check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
 +        }
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
 +        if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
 +            let parent_item = cx.tcx.hir().get_parent_item(item.hir_id());
 +            if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
 +                if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = it.kind {
 +                    return; // ignore trait impls
 +                }
 +            }
 +            check_fn(cx, sig.decl, item.hir_id(), Some(body_id));
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
 +            let body_id = if let TraitFn::Provided(b) = *trait_method {
 +                Some(b)
 +            } else {
 +                None
 +            };
 +            check_fn(cx, sig.decl, item.hir_id(), body_id);
 +        }
 +    }
 +
 +    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,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
 +    let sig = cx.tcx.fn_sig(fn_def_id);
 +    let fn_ty = sig.skip_binder();
 +    let body = opt_body_id.map(|id| cx.tcx.hir().body(id));
 +
 +    for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
 +        // Honor the allow attribute on parameters. See issue 5644.
 +        if let Some(body) = &body {
 +            if is_lint_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
 +                continue;
 +            }
 +        }
 +
 +        if let ty::Ref(_, ty, Mutability::Not) = ty.kind() {
 +            if is_type_diagnostic_item(cx, ty, sym::Vec) {
 +                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
 +                    span_lint_and_then(
 +                        cx,
 +                        PTR_ARG,
 +                        arg.span,
 +                        "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
 +                         with non-Vec-based slices",
 +                        |diag| {
 +                            if let Some(ref snippet) = get_only_generic_arg_snippet(cx, arg) {
 +                                diag.span_suggestion(
 +                                    arg.span,
 +                                    "change this to",
 +                                    format!("&[{}]", snippet),
 +                                    Applicability::Unspecified,
 +                                );
 +                            }
 +                            for (clonespan, suggestion) in spans {
 +                                diag.span_suggestion(
 +                                    clonespan,
 +                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
 +                                        Cow::Owned(format!("change `{}` to", x))
 +                                    }),
 +                                    suggestion.into(),
 +                                    Applicability::Unspecified,
 +                                );
 +                            }
 +                        },
 +                    );
 +                }
 +            } else if is_type_diagnostic_item(cx, ty, sym::String) {
 +                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
 +                    span_lint_and_then(
 +                        cx,
 +                        PTR_ARG,
 +                        arg.span,
 +                        "writing `&String` instead of `&str` involves a new object where a slice will do",
 +                        |diag| {
 +                            diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
 +                            for (clonespan, suggestion) in spans {
 +                                diag.span_suggestion_short(
 +                                    clonespan,
 +                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
 +                                        Cow::Owned(format!("change `{}` to", x))
 +                                    }),
 +                                    suggestion.into(),
 +                                    Applicability::Unspecified,
 +                                );
 +                            }
 +                        },
 +                    );
 +                }
 +            } else if is_type_diagnostic_item(cx, ty, sym::PathBuf) {
 +                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_path_buf()"), ("as_path", "")]) {
 +                    span_lint_and_then(
 +                        cx,
 +                        PTR_ARG,
 +                        arg.span,
 +                        "writing `&PathBuf` instead of `&Path` involves a new object where a slice will do",
 +                        |diag| {
 +                            diag.span_suggestion(
 +                                arg.span,
 +                                "change this to",
 +                                "&Path".into(),
 +                                Applicability::Unspecified,
 +                            );
 +                            for (clonespan, suggestion) in spans {
 +                                diag.span_suggestion_short(
 +                                    clonespan,
 +                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
 +                                        Cow::Owned(format!("change `{}` to", x))
 +                                    }),
 +                                    suggestion.into(),
 +                                    Applicability::Unspecified,
 +                                );
 +                            }
 +                        },
 +                    );
 +                }
 +            } else if match_type(cx, ty, &paths::COW) {
 +                if_chain! {
 +                    if let TyKind::Rptr(_, MutTy { ty, ..} ) = arg.kind;
 +                    if let TyKind::Path(QPath::Resolved(None, pp)) = ty.kind;
 +                    if let [ref bx] = *pp.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,
 +                    });
 +                    let replacement = snippet_opt(cx, inner.span);
 +                    if let Some(r) = replacement;
 +                    then {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            PTR_ARG,
 +                            arg.span,
 +                            "using a reference to `Cow` is not recommended",
 +                            "change this to",
 +                            "&".to_owned() + &r,
 +                            Applicability::Unspecified,
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    if let FnRetTy::Return(ty) = decl.output {
 +        if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
 +            let mut immutables = vec![];
 +            for (_, ref mutbl, ref 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");
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn get_only_generic_arg_snippet(cx: &LateContext<'_>, arg: &Ty<'_>) -> Option<String> {
 +    if_chain! {
 +        if let TyKind::Path(QPath::Resolved(_, path)) = walk_ptrs_hir_ty(arg).kind;
 +        if let Some(&PathSegment{args: Some(parameters), ..}) = path.segments.last();
 +        let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
 +            GenericArg::Type(ty) => Some(ty),
 +            _ => None,
 +        }).collect();
 +        if types.len() == 1;
 +        then {
 +            snippet_opt(cx, types[0].span)
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn get_rptr_lm<'tcx>(ty: &'tcx 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 {
 +        expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
 +            match_any_diagnostic_items(cx, id, &[sym::ptr_null, sym::ptr_null_mut]).is_some()
 +        })
 +    } else {
 +        false
 +    }
 +}
index aa6d254e7a54476acc0c109a431ae6b33d01f388,0000000000000000000000000000000000000000..4d616e26bfc1da1dab23a26aaaf0dcbc3b009cf5
mode 100644,000000..100644
--- /dev/null
@@@ -1,195 -1,0 +1,227 @@@
- use clippy_utils::{eq_expr_value, path_to_local_id};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::is_lang_ctor;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
- use rustc_hir::LangItem::{OptionNone, OptionSome};
++use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
-     fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
++use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
 +use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions that could be replaced by the question mark operator.
 +    ///
 +    /// ### Why is this bad?
 +    /// Question mark usage is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///     return None;
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```ignore
 +    /// option?;
 +    /// ```
 +    pub QUESTION_MARK,
 +    style,
 +    "checks for expressions that could be replaced by the question mark operator"
 +}
 +
 +declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
 +
 +impl QuestionMark {
 +    /// Checks if the given expression on the given context matches the following structure:
 +    ///
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///    return None;
 +    /// }
 +    /// ```
 +    ///
++    /// ```ignore
++    /// if result.is_err() {
++    ///     return result;
++    /// }
++    /// ```
++    ///
 +    /// If it matches, it will suggest to use the question mark operator instead
-             if segment.ident.name == sym!(is_none);
-             if Self::expression_returns_none(cx, then);
++    fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
 +            if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind;
-             if Self::is_option(cx, subject);
 +            if let Some(subject) = args.get(0);
-     fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
++            if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) ||
++                (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err));
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
 +                let mut replacement: Option<String> = None;
 +                if let Some(else_inner) = r#else {
 +                    if_chain! {
 +                        if let ExprKind::Block(block, None) = &else_inner.kind;
 +                        if block.stmts.is_empty();
 +                        if let Some(block_expr) = &block.expr;
 +                        if eq_expr_value(cx, subject, block_expr);
 +                        then {
 +                            replacement = Some(format!("Some({}?)", receiver_str));
 +                        }
 +                    }
 +                } else if Self::moves_by_default(cx, subject)
 +                    && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
 +                {
 +                    replacement = Some(format!("{}.as_ref()?;", receiver_str));
 +                } else {
 +                    replacement = Some(format!("{}?;", receiver_str));
 +                }
 +
 +                if let Some(replacement_str) = replacement {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        QUESTION_MARK,
 +                        expr.span,
 +                        "this block may be rewritten with the `?` operator",
 +                        "replace it with",
 +                        replacement_str,
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
-             if Self::is_option(cx, let_expr);
++    fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        if_chain! {
 +            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
 +                = higher::IfLet::hir(cx, expr);
-             if is_lang_ctor(cx, path1, OptionSome);
 +            if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
++            if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) ||
++                (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk));
++
 +            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
 +            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
-             if Self::expression_returns_none(cx, if_else);
 +            if let ExprKind::Block(block, None) = if_then.kind;
 +            if block.stmts.is_empty();
 +            if let Some(trailing_expr) = &block.expr;
 +            if path_to_local_id(trailing_expr, bind_id);
-                 let replacement = format!(
-                     "{}{}?",
-                     receiver_str,
-                     if by_ref { ".as_ref()" } else { "" },
-                 );
 +            then {
 +                let mut applicability = Applicability::MachineApplicable;
 +                let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
-         Self::check_is_none_and_early_return_none(cx, expr);
-         Self::check_if_let_some_and_early_return_none(cx, expr);
++                let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },);
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    QUESTION_MARK,
 +                    expr.span,
 +                    "this if-let-else may be rewritten with the `?` operator",
 +                    "replace it with",
 +                    replacement,
 +                    applicability,
 +                );
 +            }
 +        }
 +    }
 +
++    fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
++        Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(cx, nested_expr, expr)
++    }
++
++    fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
++        Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr)
++    }
++
 +    fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
 +        let expr_ty = cx.typeck_results().expr_ty(expression);
 +
 +        !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
 +    }
 +
 +    fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
 +        let expr_ty = cx.typeck_results().expr_ty(expression);
 +
 +        is_type_diagnostic_item(cx, expr_ty, sym::Option)
 +    }
 +
++    fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
++        let expr_ty = cx.typeck_results().expr_ty(expression);
++
++        is_type_diagnostic_item(cx, expr_ty, sym::Result)
++    }
++
 +    fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
 +        match expression.kind {
 +            ExprKind::Block(block, _) => {
 +                if let Some(return_expression) = Self::return_expression(block) {
 +                    return Self::expression_returns_none(cx, return_expression);
 +                }
 +
 +                false
 +            },
 +            ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
 +            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +            _ => false,
 +        }
 +    }
 +
++    fn expression_returns_unmodified_err(
++        cx: &LateContext<'_>,
++        expression: &Expr<'_>,
++        origin_hir_id: &Expr<'_>,
++    ) -> bool {
++        match expression.kind {
++            ExprKind::Block(block, _) => {
++                if let Some(return_expression) = Self::return_expression(block) {
++                    return Self::expression_returns_unmodified_err(cx, return_expression, origin_hir_id);
++                }
++
++                false
++            },
++            ExprKind::Ret(Some(expr)) | ExprKind::Call(expr, _) => {
++                Self::expression_returns_unmodified_err(cx, expr, origin_hir_id)
++            },
++            ExprKind::Path(_) => path_to_local(expression) == path_to_local(origin_hir_id),
++            _ => false,
++        }
++    }
++
 +    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +        // Check if last expression is a return statement. Then, return the expression
 +        if_chain! {
 +            if block.stmts.len() == 1;
 +            if let Some(expr) = block.stmts.iter().last();
 +            if let StmtKind::Semi(expr) = expr.kind;
 +            if let ExprKind::Ret(Some(ret_expr)) = expr.kind;
 +
 +            then {
 +                return Some(ret_expr);
 +            }
 +        }
 +
 +        // Check for `return` without a semicolon.
 +        if_chain! {
 +            if block.stmts.is_empty();
 +            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
 +            then {
 +                return Some(ret_expr);
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for QuestionMark {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
++        Self::check_is_none_or_err_and_early_return(cx, expr);
++        Self::check_if_let_some_or_err_and_early_return(cx, expr);
 +    }
 +}
index 6966230156cfa205060c6656f8b5ceb56a2f5df1,0000000000000000000000000000000000000000..c0e4914efe0bd1778a8ba68d4505c65048bdb310
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,69 @@@
- use clippy_utils::{in_macro, sugg};
 +use crate::rustc_lint::LintContext;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_macro_callsite;
-             if !in_macro(block.span);
++use clippy_utils::sugg;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Block, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for blocks of expressions and fires if the last expression returns
 +    /// `()` but is not followed by a semicolon.
 +    ///
 +    /// ### Why is this bad?
 +    /// The semicolon might be optional but when extending the block with new
 +    /// code, it doesn't require a change in previous last line.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     println!("Hello world")
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     println!("Hello world");
 +    /// }
 +    /// ```
 +    pub SEMICOLON_IF_NOTHING_RETURNED,
 +    pedantic,
 +    "add a semicolon if nothing is returned"
 +}
 +
 +declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]);
 +
 +impl LateLintPass<'_> for SemicolonIfNothingReturned {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
 +        if_chain! {
++            if !block.span.from_expansion();
 +            if let Some(expr) = block.expr;
 +            let t_expr = cx.typeck_results().expr_ty(expr);
 +            if t_expr.is_unit();
 +            if let snippet = snippet_with_macro_callsite(cx, expr.span, "}");
 +            if !snippet.ends_with('}');
 +            if cx.sess().source_map().is_multiline(block.span);
 +            then {
 +                // filter out the desugared `for` loop
 +                if let ExprKind::DropTemps(..) = &expr.kind {
 +                    return;
 +                }
 +
 +                let sugg = sugg::Sugg::hir_with_macro_callsite(cx, expr, "..");
 +                let suggestion = format!("{0};", sugg);
 +                span_lint_and_sugg(
 +                    cx,
 +                    SEMICOLON_IF_NOTHING_RETURNED,
 +                    expr.span.source_callsite(),
 +                    "consider adding a `;` to the last statement for consistent formatting",
 +                    "add a `;` here",
 +                    suggestion,
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
index 2ca7c18800ee2e362e299393058239cf35324ce3,0000000000000000000000000000000000000000..64841f33cc385afa389801c7ab9f19d2d3032514
mode 100644,000000..100644
--- /dev/null
@@@ -1,235 -1,0 +1,231 @@@
-             let msg = format!(
-                 "`{}` is shadowed by `{}` which reuses the original value",
-                 snippet(cx, pat.span, "_"),
-                 snippet(cx, expr.span, "..")
-             );
 +use clippy_utils::diagnostics::span_lint_and_note;
 +use clippy_utils::source::snippet;
 +use clippy_utils::visitors::is_local_used;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def::Res;
 +use rustc_hir::def_id::LocalDefId;
 +use rustc_hir::hir_id::ItemLocalId;
 +use rustc_hir::{Block, Body, BodyOwnerKind, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, while just changing reference level or mutability.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not much, in fact it's a very common pattern in Rust
 +    /// code. Still, some may opt to avoid it in their code base, they can set this
 +    /// lint to `Warn`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 1;
 +    /// // Bad
 +    /// let x = &x;
 +    ///
 +    /// // Good
 +    /// let y = &x; // use different variable name
 +    /// ```
 +    pub SHADOW_SAME,
 +    restriction,
 +    "rebinding a name to itself, e.g., `let mut x = &mut x`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, while reusing the original value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not too much, in fact it's a common pattern in Rust
 +    /// code. Still, some argue that name shadowing like this hurts readability,
 +    /// because a value may be bound to different things depending on position in
 +    /// the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 2;
 +    /// let x = x + 1;
 +    /// ```
 +    /// use different variable name:
 +    /// ```rust
 +    /// let x = 2;
 +    /// let y = x + 1;
 +    /// ```
 +    pub SHADOW_REUSE,
 +    restriction,
 +    "rebinding a name to an expression that re-uses the original value, e.g., `let x = x + 1`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for bindings that shadow other bindings already in
 +    /// scope, either without an initialization or with one that does not even use
 +    /// the original value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Name shadowing can hurt readability, especially in
 +    /// large code bases, because it is easy to lose track of the active binding at
 +    /// any place in the code. This can be alleviated by either giving more specific
 +    /// names to bindings or introducing more scopes to contain the bindings.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y = 1;
 +    /// # let z = 2;
 +    /// let x = y;
 +    ///
 +    /// // Bad
 +    /// let x = z; // shadows the earlier binding
 +    ///
 +    /// // Good
 +    /// let w = z; // use different variable name
 +    /// ```
 +    pub SHADOW_UNRELATED,
 +    restriction,
 +    "rebinding a name without even using the original value"
 +}
 +
 +#[derive(Default)]
 +pub(crate) struct Shadow {
 +    bindings: Vec<FxHashMap<Symbol, Vec<ItemLocalId>>>,
 +}
 +
 +impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Shadow {
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        let (id, ident) = match pat.kind {
 +            PatKind::Binding(_, hir_id, ident, _) => (hir_id, ident),
 +            _ => return,
 +        };
 +        if ident.span.from_expansion() || ident.span.is_dummy() {
 +            return;
 +        }
 +        let HirId { owner, local_id } = id;
 +
 +        // get (or insert) the list of items for this owner and symbol
 +        let data = self.bindings.last_mut().unwrap();
 +        let items_with_name = data.entry(ident.name).or_default();
 +
 +        // check other bindings with the same name, most recently seen first
 +        for &prev in items_with_name.iter().rev() {
 +            if prev == local_id {
 +                // repeated binding in an `Or` pattern
 +                return;
 +            }
 +
 +            if is_shadow(cx, owner, prev, local_id) {
 +                let prev_hir_id = HirId { owner, local_id: prev };
 +                lint_shadow(cx, pat, prev_hir_id, ident.span);
 +                // only lint against the "nearest" shadowed binding
 +                break;
 +            }
 +        }
 +        // store the binding
 +        items_with_name.push(local_id);
 +    }
 +
 +    fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) {
 +            self.bindings.push(FxHashMap::default());
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
 +        let hir = cx.tcx.hir();
 +        if !matches!(hir.body_owner_kind(hir.body_owner(body.id())), BodyOwnerKind::Closure) {
 +            self.bindings.pop();
 +        }
 +    }
 +}
 +
 +fn is_shadow(cx: &LateContext<'_>, owner: LocalDefId, first: ItemLocalId, second: ItemLocalId) -> bool {
 +    let scope_tree = cx.tcx.region_scope_tree(owner.to_def_id());
 +    let first_scope = scope_tree.var_scope(first);
 +    let second_scope = scope_tree.var_scope(second);
 +    scope_tree.is_subscope_of(second_scope, first_scope)
 +}
 +
 +fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span) {
 +    let (lint, msg) = match find_init(cx, pat.hir_id) {
 +        Some(expr) if is_self_shadow(cx, pat, expr, shadowed) => {
 +            let msg = format!(
 +                "`{}` is shadowed by itself in `{}`",
 +                snippet(cx, pat.span, "_"),
 +                snippet(cx, expr.span, "..")
 +            );
 +            (SHADOW_SAME, msg)
 +        },
 +        Some(expr) if is_local_used(cx, expr, shadowed) => {
++            let msg = format!("`{}` is shadowed", snippet(cx, pat.span, "_"));
 +            (SHADOW_REUSE, msg)
 +        },
 +        _ => {
 +            let msg = format!("`{}` shadows a previous, unrelated binding", snippet(cx, pat.span, "_"));
 +            (SHADOW_UNRELATED, msg)
 +        },
 +    };
 +    span_lint_and_note(
 +        cx,
 +        lint,
 +        span,
 +        &msg,
 +        Some(cx.tcx.hir().span(shadowed)),
 +        "previous binding is here",
 +    );
 +}
 +
 +/// Returns true if the expression is a simple transformation of a local binding such as `&x`
 +fn is_self_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, mut expr: &Expr<'_>, hir_id: HirId) -> bool {
 +    let hir = cx.tcx.hir();
 +    let is_direct_binding = hir
 +        .parent_iter(pat.hir_id)
 +        .map_while(|(_id, node)| match node {
 +            Node::Pat(pat) => Some(pat),
 +            _ => None,
 +        })
 +        .all(|pat| matches!(pat.kind, PatKind::Ref(..) | PatKind::Or(_)));
 +    if !is_direct_binding {
 +        return false;
 +    }
 +    loop {
 +        expr = match expr.kind {
 +            ExprKind::Box(e)
 +            | ExprKind::AddrOf(_, _, e)
 +            | ExprKind::Block(
 +                &Block {
 +                    stmts: [],
 +                    expr: Some(e),
 +                    ..
 +                },
 +                _,
 +            )
 +            | ExprKind::Unary(UnOp::Deref, e) => e,
 +            ExprKind::Path(QPath::Resolved(None, path)) => break path.res == Res::Local(hir_id),
 +            _ => break false,
 +        }
 +    }
 +}
 +
 +/// Finds the "init" expression for a pattern: `let <pat> = <init>;` or
 +/// `match <init> { .., <pat> => .., .. }`
 +fn find_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(hir_id) {
 +        let init = match node {
 +            Node::Arm(_) | Node::Pat(_) => continue,
 +            Node::Expr(expr) => match expr.kind {
 +                ExprKind::Match(e, _, _) => Some(e),
 +                _ => None,
 +            },
 +            Node::Local(local) => local.init,
 +            _ => None,
 +        };
 +        return init;
 +    }
 +    None
 +}
index 44d5ff0b63ad51a5fd2c014f44969d2858644df8,0000000000000000000000000000000000000000..201aa06782405c17921c5c08e2c2b47b9162ef78
mode 100644,000000..100644
--- /dev/null
@@@ -1,693 -1,0 +1,693 @@@
-             new_ident.to_string(),
 +use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use core::ops::{Add, AddAssign};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind};
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Ident;
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unlikely usages of binary operators that are almost
 +    /// certainly typos and/or copy/paste errors, given the other usages
 +    /// of binary operators nearby.
 +    ///
 +    /// ### Why is this bad?
 +    /// They are probably bugs and if they aren't then they look like bugs
 +    /// and you should add a comment explaining why you are doing such an
 +    /// odd set of operations.
 +    ///
 +    /// ### Known problems
 +    /// There may be some false positives if you are trying to do something
 +    /// unusual that happens to look like a typo.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Vec3 {
 +    ///     x: f64,
 +    ///     y: f64,
 +    ///     z: f64,
 +    /// }
 +    ///
 +    /// impl Eq for Vec3 {}
 +    ///
 +    /// impl PartialEq for Vec3 {
 +    ///     fn eq(&self, other: &Self) -> bool {
 +    ///         // This should trigger the lint because `self.x` is compared to `other.y`
 +    ///         self.x == other.y && self.y == other.y && self.z == other.z
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct Vec3 {
 +    /// #     x: f64,
 +    /// #     y: f64,
 +    /// #     z: f64,
 +    /// # }
 +    /// // same as above except:
 +    /// impl PartialEq for Vec3 {
 +    ///     fn eq(&self, other: &Self) -> bool {
 +    ///         // Note we now compare other.x to self.x
 +    ///         self.x == other.x && self.y == other.y && self.z == other.z
 +    ///     }
 +    /// }
 +    /// ```
 +    pub SUSPICIOUS_OPERATION_GROUPINGS,
 +    nursery,
 +    "groupings of binary operations that look suspiciously like typos"
 +}
 +
 +declare_lint_pass!(SuspiciousOperationGroupings => [SUSPICIOUS_OPERATION_GROUPINGS]);
 +
 +impl EarlyLintPass for SuspiciousOperationGroupings {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let Some(binops) = extract_related_binops(&expr.kind) {
 +            check_binops(cx, &binops.iter().collect::<Vec<_>>());
 +
 +            let mut op_types = Vec::with_capacity(binops.len());
 +            // We could use a hashmap, etc. to avoid being O(n*m) here, but
 +            // we want the lints to be emitted in a consistent order. Besides,
 +            // m, (the number of distinct `BinOpKind`s in `binops`)
 +            // will often be small, and does have an upper limit.
 +            binops.iter().map(|b| b.op).for_each(|op| {
 +                if !op_types.contains(&op) {
 +                    op_types.push(op);
 +                }
 +            });
 +
 +            for op_type in op_types {
 +                let ops: Vec<_> = binops.iter().filter(|b| b.op == op_type).collect();
 +
 +                check_binops(cx, &ops);
 +            }
 +        }
 +    }
 +}
 +
 +fn check_binops(cx: &EarlyContext<'_>, binops: &[&BinaryOp<'_>]) {
 +    let binop_count = binops.len();
 +    if binop_count < 2 {
 +        // Single binary operation expressions would likely be false
 +        // positives.
 +        return;
 +    }
 +
 +    let mut one_ident_difference_count = 0;
 +    let mut no_difference_info = None;
 +    let mut double_difference_info = None;
 +    let mut expected_ident_loc = None;
 +
 +    let mut paired_identifiers = FxHashSet::default();
 +
 +    for (i, BinaryOp { left, right, op, .. }) in binops.iter().enumerate() {
 +        match ident_difference_expr(left, right) {
 +            IdentDifference::NoDifference => {
 +                if is_useless_with_eq_exprs(*op) {
 +                    // The `eq_op` lint should catch this in this case.
 +                    return;
 +                }
 +
 +                no_difference_info = Some(i);
 +            },
 +            IdentDifference::Single(ident_loc) => {
 +                one_ident_difference_count += 1;
 +                if let Some(previous_expected) = expected_ident_loc {
 +                    if previous_expected != ident_loc {
 +                        // This expression doesn't match the form we're
 +                        // looking for.
 +                        return;
 +                    }
 +                } else {
 +                    expected_ident_loc = Some(ident_loc);
 +                }
 +
 +                // If there was only a single difference, all other idents
 +                // must have been the same, and thus were paired.
 +                for id in skip_index(IdentIter::from(*left), ident_loc.index) {
 +                    paired_identifiers.insert(id);
 +                }
 +            },
 +            IdentDifference::Double(ident_loc1, ident_loc2) => {
 +                double_difference_info = Some((i, ident_loc1, ident_loc2));
 +            },
 +            IdentDifference::Multiple | IdentDifference::NonIdent => {
 +                // It's too hard to know whether this is a bug or not.
 +                return;
 +            },
 +        }
 +    }
 +
 +    let mut applicability = Applicability::MachineApplicable;
 +
 +    if let Some(expected_loc) = expected_ident_loc {
 +        match (no_difference_info, double_difference_info) {
 +            (Some(i), None) => attempt_to_emit_no_difference_lint(cx, binops, i, expected_loc),
 +            (None, Some((double_difference_index, ident_loc1, ident_loc2))) => {
 +                if_chain! {
 +                    if one_ident_difference_count == binop_count - 1;
 +                    if let Some(binop) = binops.get(double_difference_index);
 +                    then {
 +                        let changed_loc = if ident_loc1 == expected_loc {
 +                            ident_loc2
 +                        } else if ident_loc2 == expected_loc {
 +                            ident_loc1
 +                        } else {
 +                            // This expression doesn't match the form we're
 +                            // looking for.
 +                            return;
 +                        };
 +
 +                        if let Some(sugg) = ident_swap_sugg(
 +                            cx,
 +                            &paired_identifiers,
 +                            binop,
 +                            changed_loc,
 +                            &mut applicability,
 +                        ) {
 +                            emit_suggestion(
 +                                cx,
 +                                binop.span,
 +                                sugg,
 +                                applicability,
 +                            );
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +fn attempt_to_emit_no_difference_lint(
 +    cx: &EarlyContext<'_>,
 +    binops: &[&BinaryOp<'_>],
 +    i: usize,
 +    expected_loc: IdentLocation,
 +) {
 +    if let Some(binop) = binops.get(i).copied() {
 +        // We need to try and figure out which identifier we should
 +        // suggest using instead. Since there could be multiple
 +        // replacement candidates in a given expression, and we're
 +        // just taking the first one, we may get some bad lint
 +        // messages.
 +        let mut applicability = Applicability::MaybeIncorrect;
 +
 +        // We assume that the correct ident is one used elsewhere in
 +        // the other binops, in a place that there was a single
 +        // difference between idents before.
 +        let old_left_ident = get_ident(binop.left, expected_loc);
 +        let old_right_ident = get_ident(binop.right, expected_loc);
 +
 +        for b in skip_index(binops.iter(), i) {
 +            if_chain! {
 +                if let (Some(old_ident), Some(new_ident)) =
 +                (old_left_ident, get_ident(b.left, expected_loc));
 +                if old_ident != new_ident;
 +                if let Some(sugg) = suggestion_with_swapped_ident(
 +                    cx,
 +                    binop.left,
 +                    expected_loc,
 +                    new_ident,
 +                    &mut applicability,
 +                );
 +                then {
 +                    emit_suggestion(
 +                        cx,
 +                        binop.span,
 +                        replace_left_sugg(cx, binop, &sugg, &mut applicability),
 +                        applicability,
 +                    );
 +                    return;
 +                }
 +            }
 +
 +            if_chain! {
 +                if let (Some(old_ident), Some(new_ident)) =
 +                    (old_right_ident, get_ident(b.right, expected_loc));
 +                if old_ident != new_ident;
 +                if let Some(sugg) = suggestion_with_swapped_ident(
 +                    cx,
 +                    binop.right,
 +                    expected_loc,
 +                    new_ident,
 +                    &mut applicability,
 +                );
 +                then {
 +                    emit_suggestion(
 +                        cx,
 +                        binop.span,
 +                        replace_right_sugg(cx, binop, &sugg, &mut applicability),
 +                        applicability,
 +                    );
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn emit_suggestion(cx: &EarlyContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        SUSPICIOUS_OPERATION_GROUPINGS,
 +        span,
 +        "this sequence of operators looks suspiciously like a bug",
 +        "did you mean",
 +        sugg,
 +        applicability,
 +    );
 +}
 +
 +fn ident_swap_sugg(
 +    cx: &EarlyContext<'_>,
 +    paired_identifiers: &FxHashSet<Ident>,
 +    binop: &BinaryOp<'_>,
 +    location: IdentLocation,
 +    applicability: &mut Applicability,
 +) -> Option<String> {
 +    let left_ident = get_ident(binop.left, location)?;
 +    let right_ident = get_ident(binop.right, location)?;
 +
 +    let sugg = match (
 +        paired_identifiers.contains(&left_ident),
 +        paired_identifiers.contains(&right_ident),
 +    ) {
 +        (true, true) | (false, false) => {
 +            // We don't have a good guess of what ident should be
 +            // used instead, in these cases.
 +            *applicability = Applicability::MaybeIncorrect;
 +
 +            // We arbitraily choose one side to suggest changing,
 +            // since we don't have a better guess. If the user
 +            // ends up duplicating a clause, the `logic_bug` lint
 +            // should catch it.
 +
 +            let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?;
 +
 +            replace_right_sugg(cx, binop, &right_suggestion, applicability)
 +        },
 +        (false, true) => {
 +            // We haven't seen a pair involving the left one, so
 +            // it's probably what is wanted.
 +
 +            let right_suggestion = suggestion_with_swapped_ident(cx, binop.right, location, left_ident, applicability)?;
 +
 +            replace_right_sugg(cx, binop, &right_suggestion, applicability)
 +        },
 +        (true, false) => {
 +            // We haven't seen a pair involving the right one, so
 +            // it's probably what is wanted.
 +            let left_suggestion = suggestion_with_swapped_ident(cx, binop.left, location, right_ident, applicability)?;
 +
 +            replace_left_sugg(cx, binop, &left_suggestion, applicability)
 +        },
 +    };
 +
 +    Some(sugg)
 +}
 +
 +fn replace_left_sugg(
 +    cx: &EarlyContext<'_>,
 +    binop: &BinaryOp<'_>,
 +    left_suggestion: &str,
 +    applicability: &mut Applicability,
 +) -> String {
 +    format!(
 +        "{} {} {}",
 +        left_suggestion,
 +        binop.op.to_string(),
 +        snippet_with_applicability(cx, binop.right.span, "..", applicability),
 +    )
 +}
 +
 +fn replace_right_sugg(
 +    cx: &EarlyContext<'_>,
 +    binop: &BinaryOp<'_>,
 +    right_suggestion: &str,
 +    applicability: &mut Applicability,
 +) -> String {
 +    format!(
 +        "{} {} {}",
 +        snippet_with_applicability(cx, binop.left.span, "..", applicability),
 +        binop.op.to_string(),
 +        right_suggestion,
 +    )
 +}
 +
 +#[derive(Clone, Debug)]
 +struct BinaryOp<'exprs> {
 +    op: BinOpKind,
 +    span: Span,
 +    left: &'exprs Expr,
 +    right: &'exprs Expr,
 +}
 +
 +impl BinaryOp<'exprs> {
 +    fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self {
 +        Self { op, span, left, right }
 +    }
 +}
 +
 +fn strip_non_ident_wrappers(expr: &Expr) -> &Expr {
 +    let mut output = expr;
 +    loop {
 +        output = match &output.kind {
 +            ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner,
 +            _ => {
 +                return output;
 +            },
 +        };
 +    }
 +}
 +
 +fn extract_related_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 +    append_opt_vecs(chained_binops(kind), if_statment_binops(kind))
 +}
 +
 +fn if_statment_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 +    match kind {
 +        ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind),
 +        ExprKind::Paren(ref e) => if_statment_binops(&e.kind),
 +        ExprKind::Block(ref block, _) => {
 +            let mut output = None;
 +            for stmt in &block.stmts {
 +                match stmt.kind {
 +                    StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => {
 +                        output = append_opt_vecs(output, if_statment_binops(&e.kind));
 +                    },
 +                    _ => {},
 +                }
 +            }
 +            output
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) -> Option<Vec<A>> {
 +    match (target_opt, source_opt) {
 +        (Some(mut target), Some(mut source)) => {
 +            target.reserve(source.len());
 +            for op in source.drain(..) {
 +                target.push(op);
 +            }
 +            Some(target)
 +        },
 +        (Some(v), None) | (None, Some(v)) => Some(v),
 +        (None, None) => None,
 +    }
 +}
 +
 +fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 +    match kind {
 +        ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer),
 +        ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind),
 +        _ => None,
 +    }
 +}
 +
 +fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
 +    match (&left_outer.kind, &right_outer.kind) {
 +        (
 +            ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e),
 +            ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e),
 +        ) => chained_binops_helper(left_e, right_e),
 +        (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer),
 +        (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => {
 +            chained_binops_helper(left_outer, right_e)
 +        },
 +        (
 +            ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right),
 +            ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right),
 +        ) => match (
 +            chained_binops_helper(left_left, left_right),
 +            chained_binops_helper(right_left, right_right),
 +        ) {
 +            (Some(mut left_ops), Some(mut right_ops)) => {
 +                left_ops.reserve(right_ops.len());
 +                for op in right_ops.drain(..) {
 +                    left_ops.push(op);
 +                }
 +                Some(left_ops)
 +            },
 +            (Some(mut left_ops), _) => {
 +                left_ops.push(BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)));
 +                Some(left_ops)
 +            },
 +            (_, Some(mut right_ops)) => {
 +                right_ops.insert(0, BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)));
 +                Some(right_ops)
 +            },
 +            (None, None) => Some(vec![
 +                BinaryOp::new(*left_op, left_outer.span, (left_left, left_right)),
 +                BinaryOp::new(*right_op, right_outer.span, (right_left, right_right)),
 +            ]),
 +        },
 +        _ => None,
 +    }
 +}
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
 +struct IdentLocation {
 +    index: usize,
 +}
 +
 +impl Add for IdentLocation {
 +    type Output = IdentLocation;
 +
 +    fn add(self, other: Self) -> Self::Output {
 +        Self {
 +            index: self.index + other.index,
 +        }
 +    }
 +}
 +
 +impl AddAssign for IdentLocation {
 +    fn add_assign(&mut self, other: Self) {
 +        *self = *self + other;
 +    }
 +}
 +
 +#[derive(Clone, Copy, Debug)]
 +enum IdentDifference {
 +    NoDifference,
 +    Single(IdentLocation),
 +    Double(IdentLocation, IdentLocation),
 +    Multiple,
 +    NonIdent,
 +}
 +
 +impl Add for IdentDifference {
 +    type Output = IdentDifference;
 +
 +    fn add(self, other: Self) -> Self::Output {
 +        match (self, other) {
 +            (Self::NoDifference, output) | (output, Self::NoDifference) => output,
 +            (Self::Multiple, _)
 +            | (_, Self::Multiple)
 +            | (Self::Double(_, _), Self::Single(_))
 +            | (Self::Single(_) | Self::Double(_, _), Self::Double(_, _)) => Self::Multiple,
 +            (Self::NonIdent, _) | (_, Self::NonIdent) => Self::NonIdent,
 +            (Self::Single(il1), Self::Single(il2)) => Self::Double(il1, il2),
 +        }
 +    }
 +}
 +
 +impl AddAssign for IdentDifference {
 +    fn add_assign(&mut self, other: Self) {
 +        *self = *self + other;
 +    }
 +}
 +
 +impl IdentDifference {
 +    /// Returns true if learning about more differences will not change the value
 +    /// of this `IdentDifference`, and false otherwise.
 +    fn is_complete(&self) -> bool {
 +        match self {
 +            Self::NoDifference | Self::Single(_) | Self::Double(_, _) => false,
 +            Self::Multiple | Self::NonIdent => true,
 +        }
 +    }
 +}
 +
 +fn ident_difference_expr(left: &Expr, right: &Expr) -> IdentDifference {
 +    ident_difference_expr_with_base_location(left, right, IdentLocation::default()).0
 +}
 +
 +fn ident_difference_expr_with_base_location(
 +    left: &Expr,
 +    right: &Expr,
 +    mut base: IdentLocation,
 +) -> (IdentDifference, IdentLocation) {
 +    // Ideally, this function should not use IdentIter because it should return
 +    // early if the expressions have any non-ident differences. We want that early
 +    // return because if without that restriction the lint would lead to false
 +    // positives.
 +    //
 +    // But, we cannot (easily?) use a `rustc_ast::visit::Visitor`, since we need
 +    // the two expressions to be walked in lockstep. And without a `Visitor`, we'd
 +    // have to do all the AST traversal ourselves, which is a lot of work, since to
 +    // do it properly we'd need to be able to handle more or less every possible
 +    // AST node since `Item`s can be written inside `Expr`s.
 +    //
 +    // In practice, it seems likely that expressions, above a certain size, that
 +    // happen to use the exact same idents in the exact same order, and which are
 +    // not structured the same, would be rare. Therefore it seems likely that if
 +    // we do only the first layer of matching ourselves and eventually fallback on
 +    // IdentIter, then the output of this function will be almost always be correct
 +    // in practice.
 +    //
 +    // If it turns out that problematic cases are more prelavent than we assume,
 +    // then we should be able to change this function to do the correct traversal,
 +    // without needing to change the rest of the code.
 +
 +    #![allow(clippy::enum_glob_use)]
 +    use ExprKind::*;
 +
 +    match (
 +        &strip_non_ident_wrappers(left).kind,
 +        &strip_non_ident_wrappers(right).kind,
 +    ) {
 +        (Yield(_), Yield(_))
 +        | (Try(_), Try(_))
 +        | (Paren(_), Paren(_))
 +        | (Repeat(_, _), Repeat(_, _))
 +        | (Struct(_), Struct(_))
 +        | (MacCall(_), MacCall(_))
 +        | (LlvmInlineAsm(_), LlvmInlineAsm(_))
 +        | (InlineAsm(_), InlineAsm(_))
 +        | (Ret(_), Ret(_))
 +        | (Continue(_), Continue(_))
 +        | (Break(_, _), Break(_, _))
 +        | (AddrOf(_, _, _), AddrOf(_, _, _))
 +        | (Path(_, _), Path(_, _))
 +        | (Range(_, _, _), Range(_, _, _))
 +        | (Index(_, _), Index(_, _))
 +        | (Field(_, _), Field(_, _))
 +        | (AssignOp(_, _, _), AssignOp(_, _, _))
 +        | (Assign(_, _, _), Assign(_, _, _))
 +        | (TryBlock(_), TryBlock(_))
 +        | (Await(_), Await(_))
 +        | (Async(_, _, _), Async(_, _, _))
 +        | (Block(_, _), Block(_, _))
 +        | (Closure(_, _, _, _, _, _), Closure(_, _, _, _, _, _))
 +        | (Match(_, _), Match(_, _))
 +        | (Loop(_, _), Loop(_, _))
 +        | (ForLoop(_, _, _, _), ForLoop(_, _, _, _))
 +        | (While(_, _, _), While(_, _, _))
 +        | (If(_, _, _), If(_, _, _))
 +        | (Let(_, _, _), Let(_, _, _))
 +        | (Type(_, _), Type(_, _))
 +        | (Cast(_, _), Cast(_, _))
 +        | (Lit(_), Lit(_))
 +        | (Unary(_, _), Unary(_, _))
 +        | (Binary(_, _, _), Binary(_, _, _))
 +        | (Tup(_), Tup(_))
 +        | (MethodCall(_, _, _), MethodCall(_, _, _))
 +        | (Call(_, _), Call(_, _))
 +        | (ConstBlock(_), ConstBlock(_))
 +        | (Array(_), Array(_))
 +        | (Box(_), Box(_)) => {
 +            // keep going
 +        },
 +        _ => {
 +            return (IdentDifference::NonIdent, base);
 +        },
 +    }
 +
 +    let mut difference = IdentDifference::NoDifference;
 +
 +    for (left_attr, right_attr) in left.attrs.iter().zip(right.attrs.iter()) {
 +        let (new_difference, new_base) =
 +            ident_difference_via_ident_iter_with_base_location(left_attr, right_attr, base);
 +        base = new_base;
 +        difference += new_difference;
 +        if difference.is_complete() {
 +            return (difference, base);
 +        }
 +    }
 +
 +    let (new_difference, new_base) = ident_difference_via_ident_iter_with_base_location(left, right, base);
 +    base = new_base;
 +    difference += new_difference;
 +
 +    (difference, base)
 +}
 +
 +fn ident_difference_via_ident_iter_with_base_location<Iterable: Into<IdentIter>>(
 +    left: Iterable,
 +    right: Iterable,
 +    mut base: IdentLocation,
 +) -> (IdentDifference, IdentLocation) {
 +    // See the note in `ident_difference_expr_with_base_location` about `IdentIter`
 +    let mut difference = IdentDifference::NoDifference;
 +
 +    let mut left_iterator = left.into();
 +    let mut right_iterator = right.into();
 +
 +    loop {
 +        match (left_iterator.next(), right_iterator.next()) {
 +            (Some(left_ident), Some(right_ident)) => {
 +                if !eq_id(left_ident, right_ident) {
 +                    difference += IdentDifference::Single(base);
 +                    if difference.is_complete() {
 +                        return (difference, base);
 +                    }
 +                }
 +            },
 +            (Some(_), None) | (None, Some(_)) => {
 +                return (IdentDifference::NonIdent, base);
 +            },
 +            (None, None) => {
 +                return (difference, base);
 +            },
 +        }
 +        base += IdentLocation { index: 1 };
 +    }
 +}
 +
 +fn get_ident(expr: &Expr, location: IdentLocation) -> Option<Ident> {
 +    IdentIter::from(expr).nth(location.index)
 +}
 +
 +fn suggestion_with_swapped_ident(
 +    cx: &EarlyContext<'_>,
 +    expr: &Expr,
 +    location: IdentLocation,
 +    new_ident: Ident,
 +    applicability: &mut Applicability,
 +) -> Option<String> {
 +    get_ident(expr, location).and_then(|current_ident| {
 +        if eq_id(current_ident, new_ident) {
 +            // We never want to suggest a non-change
 +            return None;
 +        }
 +
 +        Some(format!(
 +            "{}{}{}",
 +            snippet_with_applicability(cx, expr.span.with_hi(current_ident.span.lo()), "..", applicability),
++            new_ident,
 +            snippet_with_applicability(cx, expr.span.with_lo(current_ident.span.hi()), "..", applicability),
 +        ))
 +    })
 +}
 +
 +fn skip_index<A, Iter>(iter: Iter, index: usize) -> impl Iterator<Item = A>
 +where
 +    Iter: Iterator<Item = A>,
 +{
 +    iter.enumerate()
 +        .filter_map(move |(i, a)| if i == index { None } else { Some(a) })
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c216a1f81ea547630840e7403bd0f8ebe4820204
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use rustc_hir::{HirId, Item, ItemKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty::Const;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::sym;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Displays a warning when a struct with a trailing zero-sized array is declared without a `repr` attribute.
++    ///
++    /// ### Why is this bad?
++    /// Zero-sized arrays aren't very useful in Rust itself, so such a struct is likely being created to pass to C code or in some other situation where control over memory layout matters (for example, in conjuction with manual allocation to make it easy to compute the offset of the array). Either way, `#[repr(C)]` (or another `repr` attribute) is needed.
++    ///
++    /// ### Example
++    /// ```rust
++    /// struct RarelyUseful {
++    ///     some_field: u32,
++    ///     last: [u32; 0],
++    /// }
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// #[repr(C)]
++    /// struct MoreOftenUseful {
++    ///     some_field: usize,
++    ///     last: [u32; 0],
++    /// }
++    /// ```
++    pub TRAILING_EMPTY_ARRAY,
++    nursery,
++    "struct with a trailing zero-sized array but without `#[repr(C)]` or another `repr` attribute"
++}
++declare_lint_pass!(TrailingEmptyArray => [TRAILING_EMPTY_ARRAY]);
++
++impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
++    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
++        if is_struct_with_trailing_zero_sized_array(cx, item) && !has_repr_attr(cx, item.hir_id()) {
++            span_lint_and_help(
++                cx,
++                TRAILING_EMPTY_ARRAY,
++                item.span,
++                "trailing zero-sized array in a struct which is not marked with a `repr` attribute",
++                None,
++                &format!(
++                    "consider annotating `{}` with `#[repr(C)]` or another `repr` attribute",
++                    cx.tcx.def_path_str(item.def_id.to_def_id())
++                ),
++            );
++        }
++    }
++}
++
++fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool {
++    if_chain! {
++        // First check if last field is an array
++        if let ItemKind::Struct(data, _) = &item.kind;
++        if let Some(last_field) = data.fields().last();
++        if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind;
++
++        // Then check if that that array zero-sized
++        let length_ldid = cx.tcx.hir().local_def_id(length.hir_id);
++        let length = Const::from_anon_const(cx.tcx, length_ldid);
++        let length = length.try_eval_usize(cx.tcx, cx.param_env);
++        if let Some(length) = length;
++        then {
++            length == 0
++        } else {
++            false
++        }
++    }
++}
++
++fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
++    cx.tcx.hir().attrs(hir_id).iter().any(|attr| attr.has_name(sym::repr))
++}
index 33ec9c331ce5689c95055c7687823fd957539bbe,0000000000000000000000000000000000000000..e6acf1a94c9299c6b2c59abfaa83a15da0441cb7
mode 100644,000000..100644
--- /dev/null
@@@ -1,376 -1,0 +1,401 @@@
 +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 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')
 +    /// ```
 +    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
 +    /// ```
 +    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];
 +    /// ```
 +    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
 +    /// ```
 +    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;
 +    /// ```
 +    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();
 +    /// ```
 +    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();
 +    /// ```
 +    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;
 +    /// ```
 +    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);
 +    /// ```
 +    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();
 +    /// ```
 +    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();
++    /// ```
++    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) };
 +    /// ```
 +    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<_>>();
 +    /// ```
 +    pub UNSOUND_COLLECTION_TRANSMUTE,
 +    correctness,
 +    "transmute between collections of layout-incompatible types"
 +}
 +
 +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,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Transmute {
 +    #[allow(clippy::similar_names, clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(path_expr, args) = e.kind;
 +            if let ExprKind::Path(ref 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(&args[0]);
 +                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, args) {
 +                    return;
 +                }
 +
 +                let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty);
 +                linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty);
 +                linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
 +                linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
 +                linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
 +                linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
 +                linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
 +                linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
 +                linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
++                linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context);
 +                linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty);
 +
 +                if !linted {
 +                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
 +                }
 +            }
 +        }
 +    }
 +}
index 8f884e6a4a17b74203247569d2f9b3a925a60c7c,0000000000000000000000000000000000000000..e83d2e06b9a8d26800e7b0a65b6f02e2b42221c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,45 @@@
-                         format!("std::char::from_u32({}).unwrap()", arg.to_string()),
 +use super::TRANSMUTE_INT_TO_CHAR;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::sugg;
 +use rustc_ast as ast;
 +use rustc_errors::Applicability;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +
 +/// Checks for `transmute_int_to_char` lint.
 +/// Returns `true` if it's triggered, otherwise returns `false`.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +    args: &'tcx [Expr<'_>],
 +) -> bool {
 +    match (&from_ty.kind(), &to_ty.kind()) {
 +        (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => {
 +            span_lint_and_then(
 +                cx,
 +                TRANSMUTE_INT_TO_CHAR,
 +                e.span,
 +                &format!("transmute from a `{}` to a `char`", from_ty),
 +                |diag| {
 +                    let arg = sugg::Sugg::hir(cx, &args[0], "..");
 +                    let arg = if let ty::Int(_) = from_ty.kind() {
 +                        arg.as_ty(ast::UintTy::U32.name_str())
 +                    } else {
 +                        arg
 +                    };
 +                    diag.span_suggestion(
 +                        e.span,
 +                        "consider using",
++                        format!("std::char::from_u32({}).unwrap()", arg),
 +                        Applicability::Unspecified,
 +                    );
 +                },
 +            );
 +            true
 +        },
 +        _ => false,
 +    }
 +}
index 2b6a4cff81eb5d60549b0dc4d97497e37f89e783,0000000000000000000000000000000000000000..05eee380d6f409bdf275384d089a16b4334abf80
mode 100644,000000..100644
--- /dev/null
@@@ -1,48 -1,0 +1,48 @@@
-                         format!("{}::from_bits({})", to_ty, arg.to_string()),
 +use super::TRANSMUTE_INT_TO_FLOAT;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::sugg;
 +use rustc_errors::Applicability;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::{self, Ty};
 +
 +/// Checks for `transmute_int_to_float` lint.
 +/// Returns `true` if it's triggered, otherwise returns `false`.
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty: Ty<'tcx>,
 +    to_ty: Ty<'tcx>,
 +    args: &'tcx [Expr<'_>],
 +    const_context: bool,
 +) -> bool {
 +    match (&from_ty.kind(), &to_ty.kind()) {
 +        (ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context => {
 +            span_lint_and_then(
 +                cx,
 +                TRANSMUTE_INT_TO_FLOAT,
 +                e.span,
 +                &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
 +                |diag| {
 +                    let arg = sugg::Sugg::hir(cx, &args[0], "..");
 +                    let arg = if let ty::Int(int_ty) = from_ty.kind() {
 +                        arg.as_ty(format!(
 +                            "u{}",
 +                            int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())
 +                        ))
 +                    } else {
 +                        arg
 +                    };
 +                    diag.span_suggestion(
 +                        e.span,
 +                        "consider using",
++                        format!("{}::from_bits({})", to_ty, arg),
 +                        Applicability::Unspecified,
 +                    );
 +                },
 +            );
 +            true
 +        },
 +        _ => false,
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5ba58a764940191728d0266f5c77b2e2cc113ef2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,49 @@@
++use super::TRANSMUTE_NUM_TO_BYTES;
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::sugg;
++use rustc_errors::Applicability;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, Ty, UintTy};
++
++/// Checks for `transmute_int_to_float` lint.
++/// Returns `true` if it's triggered, otherwise returns `false`.
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    e: &'tcx Expr<'_>,
++    from_ty: Ty<'tcx>,
++    to_ty: Ty<'tcx>,
++    args: &'tcx [Expr<'_>],
++    const_context: bool,
++) -> bool {
++    match (&from_ty.kind(), &to_ty.kind()) {
++        (ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => {
++            if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) {
++                return false;
++            }
++            if matches!(from_ty.kind(), ty::Float(_)) && const_context {
++                // TODO: Remove when const_float_bits_conv is stabilized
++                // rust#72447
++                return false;
++            }
++
++            span_lint_and_then(
++                cx,
++                TRANSMUTE_NUM_TO_BYTES,
++                e.span,
++                &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
++                |diag| {
++                    let arg = sugg::Sugg::hir(cx, &args[0], "..");
++                    diag.span_suggestion(
++                        e.span,
++                        "consider using `to_ne_bytes()`",
++                        format!("{}.to_ne_bytes()", arg),
++                        Applicability::Unspecified,
++                    );
++                },
++            );
++            true
++        },
++        _ => false,
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e08e4d03c7efef5c651cdd46fcfb4ada153400d3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,225 @@@
++use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
++use clippy_utils::source::{indent_of, reindent_multiline, snippet};
++use clippy_utils::{in_macro, is_lint_allowed};
++use rustc_errors::Applicability;
++use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
++use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
++use rustc_lexer::TokenKind;
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::hir::map::Map;
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::TyCtxt;
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::{BytePos, Span};
++use std::borrow::Cow;
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `unsafe` blocks without a `// Safety: ` comment
++    /// explaining why the unsafe operations performed inside
++    /// the block are safe.
++    ///
++    /// ### Why is this bad?
++    /// Undocumented unsafe blocks can make it difficult to
++    /// read and maintain code, as well as uncover unsoundness
++    /// and bugs.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::ptr::NonNull;
++    /// let a = &mut 42;
++    ///
++    /// let ptr = unsafe { NonNull::new_unchecked(a) };
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::ptr::NonNull;
++    /// let a = &mut 42;
++    ///
++    /// // Safety: references are guaranteed to be non-null.
++    /// let ptr = unsafe { NonNull::new_unchecked(a) };
++    /// ```
++    pub UNDOCUMENTED_UNSAFE_BLOCKS,
++    restriction,
++    "creating an unsafe block without explaining why it is safe"
++}
++
++impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
++
++#[derive(Default)]
++pub struct UndocumentedUnsafeBlocks {
++    pub local_level: u32,
++    pub local_span: Option<Span>,
++    // The local was already checked for an overall safety comment
++    // There is no need to continue checking the blocks in the local
++    pub local_checked: bool,
++    // Since we can only check the blocks from expanded macros
++    // We have to omit the suggestion due to the actual definition
++    // Not being available to us
++    pub macro_expansion: bool,
++}
++
++impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
++    fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
++        if_chain! {
++            if !self.local_checked;
++            if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id);
++            if !in_external_macro(cx.tcx.sess, block.span);
++            if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules;
++            if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id);
++            if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false);
++            then {
++                let mut span = block.span;
++
++                if let Some(local_span) = self.local_span {
++                    span = local_span;
++
++                    let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span);
++
++                    if result.unwrap_or(true) {
++                        self.local_checked = true;
++                        return;
++                    }
++                }
++
++                self.lint(cx, span);
++            }
++        }
++    }
++
++    fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) {
++        if_chain! {
++            if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id);
++            if !in_external_macro(cx.tcx.sess, local.span);
++            if let Some(init) = local.init;
++            then {
++                self.visit_expr(init);
++
++                if self.local_level > 0 {
++                    self.local_span = Some(local.span);
++                }
++            }
++        }
++    }
++
++    fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) {
++        self.local_level = self.local_level.saturating_sub(1);
++
++        if self.local_level == 0 {
++            self.local_checked = false;
++            self.local_span = None;
++        }
++    }
++}
++
++impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks {
++    type Map = Map<'hir>;
++
++    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
++        NestedVisitorMap::None
++    }
++
++    fn visit_expr(&mut self, ex: &'v Expr<'v>) {
++        match ex.kind {
++            ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1),
++            _ => walk_expr(self, ex),
++        }
++    }
++}
++
++impl UndocumentedUnsafeBlocks {
++    fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option<bool> {
++        let map = tcx.hir();
++        let source_map = tcx.sess.source_map();
++
++        let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
++
++        let between_span = if in_macro(block_span) {
++            self.macro_expansion = true;
++            enclosing_scope_span.with_hi(block_span.hi())
++        } else {
++            self.macro_expansion = false;
++            enclosing_scope_span.to(block_span)
++        };
++
++        let file_name = source_map.span_to_filename(between_span);
++        let source_file = source_map.get_source_file(&file_name)?;
++
++        let lex_start = (between_span.lo().0 + 1) as usize;
++        let src_str = source_file.src.as_ref()?[lex_start..between_span.hi().0 as usize].to_string();
++
++        let mut pos = 0;
++        let mut comment = false;
++
++        for token in rustc_lexer::tokenize(&src_str) {
++            match token.kind {
++                TokenKind::LineComment { doc_style: None }
++                | TokenKind::BlockComment {
++                    doc_style: None,
++                    terminated: true,
++                } => {
++                    let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase();
++
++                    if comment_str.contains("SAFETY:") {
++                        comment = true;
++                    }
++                },
++                // We need to add all whitespace to `pos` before checking the comment's line number
++                TokenKind::Whitespace => {},
++                _ => {
++                    if comment {
++                        // Get the line number of the "comment" (really wherever the trailing whitespace ended)
++                        let comment_line_num = source_file
++                            .lookup_file_pos_with_col_display(BytePos((lex_start + pos).try_into().unwrap()))
++                            .0;
++                        // Find the block/local's line number
++                        let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
++
++                        // Check the comment is immediately followed by the block/local
++                        if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
++                            return Some(true);
++                        }
++
++                        comment = false;
++                    }
++                },
++            }
++
++            pos += token.len;
++        }
++
++        Some(false)
++    }
++
++    fn lint(&self, cx: &LateContext<'_>, mut span: Span) {
++        let source_map = cx.tcx.sess.source_map();
++
++        if source_map.is_multiline(span) {
++            span = source_map.span_until_char(span, '\n');
++        }
++
++        if self.macro_expansion {
++            span_lint_and_help(
++                cx,
++                UNDOCUMENTED_UNSAFE_BLOCKS,
++                span,
++                "unsafe block in macro expansion missing a safety comment",
++                None,
++                "consider adding a safety comment in the macro definition",
++            );
++        } else {
++            let block_indent = indent_of(cx, span);
++            let suggestion = format!("// Safety: ...\n{}", snippet(cx, span, ".."));
++
++            span_lint_and_sugg(
++                cx,
++                UNDOCUMENTED_UNSAFE_BLOCKS,
++                span,
++                "unsafe block missing a safety comment",
++                "consider adding a safety comment",
++                reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(),
++                Applicability::HasPlaceholders,
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f3e8b6881058f93907762baedb2a472ab74fc1bc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,223 @@@
++use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
++use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
++use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty};
++use clippy_utils::{is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq};
++use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{sym, Span};
++
++// TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `set_len()` call that creates `Vec` with uninitialized elements.
++    /// This is commonly caused by calling `set_len()` right after allocating or
++    /// reserving a buffer with `new()`, `default()`, `with_capacity()`, or `reserve()`.
++    ///
++    /// ### Why is this bad?
++    /// It creates a `Vec` with uninitialized data, which leads to
++    /// undefined behavior with most safe operations. Notably, uninitialized
++    /// `Vec<u8>` must not be used with generic `Read`.
++    ///
++    /// Moreover, calling `set_len()` on a `Vec` created with `new()` or `default()`
++    /// creates out-of-bound values that lead to heap memory corruption when used.
++    ///
++    /// ### Known Problems
++    /// This lint only checks directly adjacent statements.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// let mut vec: Vec<u8> = Vec::with_capacity(1000);
++    /// unsafe { vec.set_len(1000); }
++    /// reader.read(&mut vec); // undefined behavior!
++    /// ```
++    ///
++    /// ### How to fix?
++    /// 1. Use an initialized buffer:
++    ///    ```rust,ignore
++    ///    let mut vec: Vec<u8> = vec![0; 1000];
++    ///    reader.read(&mut vec);
++    ///    ```
++    /// 2. Wrap the content in `MaybeUninit`:
++    ///    ```rust,ignore
++    ///    let mut vec: Vec<MaybeUninit<T>> = Vec::with_capacity(1000);
++    ///    vec.set_len(1000);  // `MaybeUninit` can be uninitialized
++    ///    ```
++    /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
++    ///    ```rust,ignore
++    ///    let mut vec: Vec<u8> = Vec::with_capacity(1000);
++    ///    let remaining = vec.spare_capacity_mut();  // `&mut [MaybeUninit<u8>]`
++    ///    // perform initialization with `remaining`
++    ///    vec.set_len(...);  // Safe to call `set_len()` on initialized part
++    ///    ```
++    pub UNINIT_VEC,
++    correctness,
++    "Vec with uninitialized data"
++}
++
++declare_lint_pass!(UninitVec => [UNINIT_VEC]);
++
++// FIXME: update to a visitor-based implementation.
++// Threads: https://github.com/rust-lang/rust-clippy/pull/7682#discussion_r710998368
++impl<'tcx> LateLintPass<'tcx> for UninitVec {
++    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
++        if !in_external_macro(cx.tcx.sess, block.span) {
++            for w in block.stmts.windows(2) {
++                if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = w[1].kind {
++                    handle_uninit_vec_pair(cx, &w[0], expr);
++                }
++            }
++
++            if let (Some(stmt), Some(expr)) = (block.stmts.last(), block.expr) {
++                handle_uninit_vec_pair(cx, stmt, expr);
++            }
++        }
++    }
++}
++
++fn handle_uninit_vec_pair(
++    cx: &LateContext<'tcx>,
++    maybe_init_or_reserve: &'tcx Stmt<'tcx>,
++    maybe_set_len: &'tcx Expr<'tcx>,
++) {
++    if_chain! {
++        if let Some(vec) = extract_init_or_reserve_target(cx, maybe_init_or_reserve);
++        if let Some((set_len_self, call_span)) = extract_set_len_self(cx, maybe_set_len);
++        if vec.location.eq_expr(cx, set_len_self);
++        if let ty::Ref(_, vec_ty, _) = cx.typeck_results().expr_ty_adjusted(set_len_self).kind();
++        if let ty::Adt(_, substs) = vec_ty.kind();
++        // `#[allow(...)]` attribute can be set on enclosing unsafe block of `set_len()`
++        if !is_lint_allowed(cx, UNINIT_VEC, maybe_set_len.hir_id);
++        then {
++            if vec.has_capacity() {
++                // with_capacity / reserve -> set_len
++
++                // Check T of Vec<T>
++                if !is_uninit_value_valid_for_ty(cx, substs.type_at(0)) {
++                    // FIXME: #7698, false positive of the internal lints
++                    #[allow(clippy::collapsible_span_lint_calls)]
++                    span_lint_and_then(
++                        cx,
++                        UNINIT_VEC,
++                        vec![call_span, maybe_init_or_reserve.span],
++                        "calling `set_len()` immediately after reserving a buffer creates uninitialized values",
++                        |diag| {
++                            diag.help("initialize the buffer or wrap the content in `MaybeUninit`");
++                        },
++                    );
++                }
++            } else {
++                // new / default -> set_len
++                span_lint(
++                    cx,
++                    UNINIT_VEC,
++                    vec![call_span, maybe_init_or_reserve.span],
++                    "calling `set_len()` on empty `Vec` creates out-of-bound values",
++                );
++            }
++        }
++    }
++}
++
++/// The target `Vec` that is initialized or reserved
++#[derive(Clone, Copy)]
++struct TargetVec<'tcx> {
++    location: VecLocation<'tcx>,
++    /// `None` if `reserve()`
++    init_kind: Option<VecInitKind>,
++}
++
++impl TargetVec<'_> {
++    pub fn has_capacity(self) -> bool {
++        !matches!(self.init_kind, Some(VecInitKind::New | VecInitKind::Default))
++    }
++}
++
++#[derive(Clone, Copy)]
++enum VecLocation<'tcx> {
++    Local(HirId),
++    Expr(&'tcx Expr<'tcx>),
++}
++
++impl<'tcx> VecLocation<'tcx> {
++    pub fn eq_expr(self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
++        match self {
++            VecLocation::Local(hir_id) => path_to_local_id(expr, hir_id),
++            VecLocation::Expr(self_expr) => SpanlessEq::new(cx).eq_expr(self_expr, expr),
++        }
++    }
++}
++
++/// Finds the target location where the result of `Vec` initialization is stored
++/// or `self` expression for `Vec::reserve()`.
++fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<TargetVec<'tcx>> {
++    match stmt.kind {
++        StmtKind::Local(local) => {
++            if_chain! {
++                if let Some(init_expr) = local.init;
++                if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind;
++                if let Some(init_kind) = get_vec_init_kind(cx, init_expr);
++                then {
++                    return Some(TargetVec {
++                        location: VecLocation::Local(hir_id),
++                        init_kind: Some(init_kind),
++                    })
++                }
++            }
++        },
++        StmtKind::Expr(expr) | StmtKind::Semi(expr) => match expr.kind {
++            ExprKind::Assign(lhs, rhs, _span) => {
++                if let Some(init_kind) = get_vec_init_kind(cx, rhs) {
++                    return Some(TargetVec {
++                        location: VecLocation::Expr(lhs),
++                        init_kind: Some(init_kind),
++                    });
++                }
++            },
++            ExprKind::MethodCall(path, _, [self_expr, _], _) if is_reserve(cx, path, self_expr) => {
++                return Some(TargetVec {
++                    location: VecLocation::Expr(self_expr),
++                    init_kind: None,
++                });
++            },
++            _ => (),
++        },
++        StmtKind::Item(_) => (),
++    }
++    None
++}
++
++fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_>) -> bool {
++    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr).peel_refs(), sym::Vec)
++        && path.ident.name.as_str() == "reserve"
++}
++
++/// Returns self if the expression is `Vec::set_len()`
++fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> {
++    // peel unsafe blocks in `unsafe { vec.set_len() }`
++    let expr = peel_hir_expr_while(expr, |e| {
++        if let ExprKind::Block(block, _) = e.kind {
++            // Extract the first statement/expression
++            match (block.stmts.get(0).map(|stmt| &stmt.kind), block.expr) {
++                (None, Some(expr)) => Some(expr),
++                (Some(StmtKind::Expr(expr) | StmtKind::Semi(expr)), _) => Some(expr),
++                _ => None,
++            }
++        } else {
++            None
++        }
++    });
++    match expr.kind {
++        ExprKind::MethodCall(path, _, [self_expr, _], _) => {
++            let self_type = cx.typeck_results().expr_ty(self_expr).peel_refs();
++            if is_type_diagnostic_item(cx, self_type, sym::Vec) && path.ident.name.as_str() == "set_len" {
++                Some((self_expr, expr.span))
++            } else {
++                None
++            }
++        },
++        _ => None,
++    }
++}
index dd74bf367f3a58c810d484fe927561d27fb3cf9f,0000000000000000000000000000000000000000..26b56e0f2f316c69b7e53e9f5f50020f25290c3b
mode 100644,000000..100644
--- /dev/null
@@@ -1,269 -1,0 +1,274 @@@
- use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
-             })) = &left_expr.kind {
-                 if left_name == left_ident {
++use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::{self, subst::GenericArgKind};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +use rustc_span::symbol::Ident;
 +use std::iter;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects uses of `Vec::sort_by` passing in a closure
 +    /// which compares the two arguments, either directly or indirectly.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
 +    /// possible) than to use `Vec::sort_by` and a more complicated
 +    /// closure.
 +    ///
 +    /// ### Known problems
 +    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
 +    /// imported by a use statement, then it will need to be added manually.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # struct A;
 +    /// # impl A { fn foo(&self) {} }
 +    /// # let mut vec: Vec<A> = Vec::new();
 +    /// vec.sort_by_key(|a| a.foo());
 +    /// ```
 +    pub UNNECESSARY_SORT_BY,
 +    complexity,
 +    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
 +}
 +
 +declare_lint_pass!(UnnecessarySortBy => [UNNECESSARY_SORT_BY]);
 +
 +enum LintTrigger {
 +    Sort(SortDetection),
 +    SortByKey(SortByKeyDetection),
 +}
 +
 +struct SortDetection {
 +    vec_name: String,
 +    unstable: bool,
 +}
 +
 +struct SortByKeyDetection {
 +    vec_name: String,
 +    closure_arg: String,
 +    closure_body: String,
 +    reverse: bool,
 +    unstable: bool,
 +}
 +
 +/// Detect if the two expressions are mirrored (identical, except one
 +/// contains a and the other replaces it with b)
 +fn mirrored_exprs(
 +    cx: &LateContext<'_>,
 +    a_expr: &Expr<'_>,
 +    a_ident: &Ident,
 +    b_expr: &Expr<'_>,
 +    b_ident: &Ident,
 +) -> bool {
 +    match (&a_expr.kind, &b_expr.kind) {
 +        // Two boxes with mirrored contents
 +        (ExprKind::Box(left_expr), ExprKind::Box(right_expr)) => {
 +            mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
 +        },
 +        // Two arrays with mirrored contents
 +        (ExprKind::Array(left_exprs), ExprKind::Array(right_exprs)) => {
 +            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // The two exprs are function calls.
 +        // Check to see that the function itself and its arguments are mirrored
 +        (ExprKind::Call(left_expr, left_args), ExprKind::Call(right_expr, right_args)) => {
 +            mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
 +                && iter::zip(*left_args, *right_args)
 +                    .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // The two exprs are method calls.
 +        // Check to see that the function is the same and the arguments are mirrored
 +        // This is enough because the receiver of the method is listed in the arguments
 +        (
 +            ExprKind::MethodCall(left_segment, _, left_args, _),
 +            ExprKind::MethodCall(right_segment, _, right_args, _),
 +        ) => {
 +            left_segment.ident == right_segment.ident
 +                && iter::zip(*left_args, *right_args)
 +                    .all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // Two tuples with mirrored contents
 +        (ExprKind::Tup(left_exprs), ExprKind::Tup(right_exprs)) => {
 +            iter::zip(*left_exprs, *right_exprs).all(|(left, right)| mirrored_exprs(cx, left, a_ident, right, b_ident))
 +        },
 +        // Two binary ops, which are the same operation and which have mirrored arguments
 +        (ExprKind::Binary(left_op, left_left, left_right), ExprKind::Binary(right_op, right_left, right_right)) => {
 +            left_op.node == right_op.node
 +                && mirrored_exprs(cx, left_left, a_ident, right_left, b_ident)
 +                && mirrored_exprs(cx, left_right, a_ident, right_right, b_ident)
 +        },
 +        // Two unary ops, which are the same operation and which have the same argument
 +        (ExprKind::Unary(left_op, left_expr), ExprKind::Unary(right_op, right_expr)) => {
 +            left_op == right_op && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident)
 +        },
 +        // The two exprs are literals of some kind
 +        (ExprKind::Lit(left_lit), ExprKind::Lit(right_lit)) => left_lit.node == right_lit.node,
 +        (ExprKind::Cast(left, _), ExprKind::Cast(right, _)) => mirrored_exprs(cx, left, a_ident, right, b_ident),
 +        (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
 +            mirrored_exprs(cx, left_block, a_ident, right_block, b_ident)
 +        },
 +        (ExprKind::Field(left_expr, left_ident), ExprKind::Field(right_expr, right_ident)) => {
 +            left_ident.name == right_ident.name && mirrored_exprs(cx, left_expr, a_ident, right_expr, right_ident)
 +        },
 +        // Two paths: either one is a and the other is b, or they're identical to each other
 +        (
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: left_segments,
 +                    ..
 +                },
 +            )),
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: right_segments,
 +                    ..
 +                },
 +            )),
 +        ) => {
 +            (iter::zip(*left_segments, *right_segments).all(|(left, right)| left.ident == right.ident)
 +                && left_segments
 +                    .iter()
 +                    .all(|seg| &seg.ident != a_ident && &seg.ident != b_ident))
 +                || (left_segments.len() == 1
 +                    && &left_segments[0].ident == a_ident
 +                    && right_segments.len() == 1
 +                    && &right_segments[0].ident == b_ident)
 +        },
 +        // Matching expressions, but one or both is borrowed
 +        (
 +            ExprKind::AddrOf(left_kind, Mutability::Not, left_expr),
 +            ExprKind::AddrOf(right_kind, Mutability::Not, right_expr),
 +        ) => left_kind == right_kind && mirrored_exprs(cx, left_expr, a_ident, right_expr, b_ident),
 +        (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => {
 +            mirrored_exprs(cx, a_expr, a_ident, right_expr, b_ident)
 +        },
 +        (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(cx, left_expr, a_ident, b_expr, b_ident),
 +        _ => false,
 +    }
 +}
 +
 +fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind;
 +        if let name = name_ident.ident.name.to_ident_string();
 +        if name == "sort_by" || name == "sort_unstable_by";
 +        if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
 +        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::Vec);
 +        if let closure_body = cx.tcx.hir().body(*closure_body_id);
 +        if let &[
 +            Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
 +            Param { pat: Pat { kind: PatKind::Binding(_, _, right_ident, _), .. }, .. }
 +        ] = &closure_body.params;
 +        if let ExprKind::MethodCall(method_path, _, [ref left_expr, ref right_expr], _) = &closure_body.value.kind;
 +        if method_path.ident.name == sym::cmp;
 +        then {
 +            let (closure_body, closure_arg, reverse) = if mirrored_exprs(
 +                cx,
 +                left_expr,
 +                left_ident,
 +                right_expr,
 +                right_ident
 +            ) {
 +                (Sugg::hir(cx, left_expr, "..").to_string(), left_ident.name.to_string(), false)
 +            } else if mirrored_exprs(cx, left_expr, right_ident, right_expr, left_ident) {
 +                (Sugg::hir(cx, left_expr, "..").to_string(), right_ident.name.to_string(), true)
 +            } else {
 +                return None;
 +            };
 +            let vec_name = Sugg::hir(cx, &args[0], "..").to_string();
 +            let unstable = name == "sort_unstable_by";
 +
++            if_chain! {
 +            if let ExprKind::Path(QPath::Resolved(_, Path {
 +                segments: [PathSegment { ident: left_name, .. }], ..
++            })) = &left_expr.kind;
++            if left_name == left_ident;
++            if cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
++                implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
++            });
++                then {
 +                    return Some(LintTrigger::Sort(SortDetection { vec_name, unstable }));
 +                }
 +            }
 +
 +            if !expr_borrows(cx, left_expr) {
 +                return Some(LintTrigger::SortByKey(SortByKeyDetection {
 +                    vec_name,
 +                    closure_arg,
 +                    closure_body,
 +                    reverse,
 +                    unstable,
 +                }));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let ty = cx.typeck_results().expr_ty(expr);
 +    matches!(ty.kind(), ty::Ref(..))
 +        || ty
 +            .walk(cx.tcx)
 +            .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
 +}
 +
 +impl LateLintPass<'_> for UnnecessarySortBy {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        match detect_lint(cx, expr) {
 +            Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_SORT_BY,
 +                expr.span,
 +                "use Vec::sort_by_key here instead",
 +                "try",
 +                format!(
 +                    "{}.sort{}_by_key(|{}| {})",
 +                    trigger.vec_name,
 +                    if trigger.unstable { "_unstable" } else { "" },
 +                    trigger.closure_arg,
 +                    if trigger.reverse {
 +                        format!("Reverse({})", trigger.closure_body)
 +                    } else {
 +                        trigger.closure_body.to_string()
 +                    },
 +                ),
 +                if trigger.reverse {
 +                    Applicability::MaybeIncorrect
 +                } else {
 +                    Applicability::MachineApplicable
 +                },
 +            ),
 +            Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_SORT_BY,
 +                expr.span,
 +                "use Vec::sort here instead",
 +                "try",
 +                format!(
 +                    "{}.sort{}()",
 +                    trigger.vec_name,
 +                    if trigger.unstable { "_unstable" } else { "" },
 +                ),
 +                Applicability::MachineApplicable,
 +            ),
 +            None => {},
 +        }
 +    }
 +}
index 6cbada4c1505b5374758d951ca2df152eec9a4d7,0000000000000000000000000000000000000000..d05c52122d5ee518ab1d00f0349851909026151b
mode 100644,000000..100644
--- /dev/null
@@@ -1,331 -1,0 +1,339 @@@
-     (disallowed_types: Vec<String> = Vec::new()),
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::{env, fmt, fs, io};
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +/// A single disallowed method, used by the `DISALLOWED_METHOD` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedMethod {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
++/// A single disallowed type, used by the `DISALLOWED_TYPE` lint.
++#[derive(Clone, Debug, Deserialize)]
++#[serde(untagged)]
++pub enum DisallowedType {
++    Simple(String),
++    WithReason { path: String, reason: Option<String> },
++}
++
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<String>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![error.to_string()],
 +        }
 +    }
 +}
 +
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $($(#[doc = $doc])+ pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(errors.push(format!("deprecated field `{}`. {}", name, $dep));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(e.to_string()),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(format!("duplicate field `{}`", name)),
 +                                    None => $name = Some(value),
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors })
 +            }
 +        }
 +
 +        #[cfg(feature = "metadata-collector-lint")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                concat!($($doc, '\n',)*),
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
 +define_Conf! {
 +    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// Lint: BLACKLISTED_NAME.
 +    ///
 +    /// The list of blacklisted names to lint about. NB: `bar` is not here since it has legitimate uses
 +    (blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
 +    /// Lint: COGNITIVE_COMPLEXITY.
 +    ///
 +    /// The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
 +    ///
 +    /// Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
 +    (cyclomatic_complexity_threshold: Option<u64> = None),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks
 +    (doc_valid_idents: Vec<String> = [
 +        "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +        "DirectX",
 +        "ECMAScript",
 +        "GPLv2", "GPLv3",
 +        "GitHub", "GitLab",
 +        "IPv4", "IPv6",
 +        "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +        "NaN", "NaNs",
 +        "OAuth", "GraphQL",
 +        "OCaml",
 +        "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +        "WebGL",
 +        "TensorFlow",
 +        "TrueType",
 +        "iOS", "macOS", "FreeBSD",
 +        "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +        "MinGW",
 +        "CamelCase",
 +    ].iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS.
 +    ///
 +    /// The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY.
 +    ///
 +    /// The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES.
 +    ///
 +    /// The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC.
 +    ///
 +    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES.
 +    ///
 +    /// The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT.
 +    ///
 +    /// The maximum size of an enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK.
 +    ///
 +    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
 +    ///
 +    /// The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
 +    ///
 +    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE.
 +    ///
 +    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES.
 +    ///
 +    /// The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
 +    ///
 +    /// The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX.
 +    ///
 +    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS.
 +    ///
 +    /// The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS.
 +    ///
 +    /// Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHOD.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPE.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
++    (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL.
 +    ///
 +    /// Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS.
 +    ///
 +    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA.
 +    ///
 +    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
 +    /// Lint: NONSTANDARD_MACRO_BRACES.
 +    ///
 +    /// Enforce the named macros always use the braces specified.
 +    ///
 +    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
 +    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
 +    /// `crate_name::macro_name` and one with just the macro name.
 +    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
 +    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
 +    ///
 +    /// The list of imports to always rename, a fully qualified path followed by the rename.
 +    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
 +    /// Lint: RESTRICTED_SCRIPTS.
 +    ///
 +    /// The list of unicode scripts allowed to be used in the scope.
 +    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
 +    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
 +    ///
 +    /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
 +    (enable_raw_pointer_heuristic_for_send: bool = true),
 +}
 +
 +/// Search for the configuration file.
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => return Ok(Some(config_file)),
 +                }
 +            }
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    toml::from_str(&content).unwrap_or_else(TryConf::from_error)
 +}
index 9f9edbf258ac277831739b1fe2f0fb549ade8b22,0000000000000000000000000000000000000000..824ec53ab9c75301f822805eaf5cf860fe03ff74
mode 100644,000000..100644
--- /dev/null
@@@ -1,1231 -1,0 +1,1230 @@@
-             let diag_items = cx.tcx.diagnostic_items(ty_did.krate);
-             if let Some(item_name) = diag_items.iter().find_map(|(k, v)| if *v == ty_did { Some(k) } else { None });
 +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::higher;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls, path_to_res,
 +    paths, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +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::{NestedVisitorMap, 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::map::Map;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::{Symbol, SymbolStr};
 +use rustc_span::{BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "metadata-collector-lint")]
 +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_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<SymbolStr> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(ref 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]);
 +
 +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) {
 +                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 is_expn_of(item.span, "impl_lint_pass").is_some()
 +            || is_expn_of(item.span, "declare_lint_pass").is_some()
 +        {
 +            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
 +}
 +
 +struct LintCollector<'a, 'tcx> {
 +    output: &'a mut FxHashSet<Symbol>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type Map = Map<'tcx>;
 +
 +    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) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::All(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<SymbolStr> = method_names.iter().map(|s| s.as_str()).collect();
 +        let method_names: Vec<&str> = method_names.iter().map(|s| &**s).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);
 +            if let ExprKind::Block(block, _) = &body.value.kind;
 +            let stmts = &block.stmts;
 +            if stmts.len() == 1 && block.expr.is_none();
 +            if let StmtKind::Semi(only_expr) = &stmts[0].kind;
 +            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(|sym| &**sym).collect();
 +            if let Some(ty_did) = path_to_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<SymbolStr>> {
 +    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<SymbolStr> = exprs
 +                .iter()
 +                .filter_map(|expr| {
 +                    if let ExprKind::Lit(lit) = &expr.kind {
 +                        if let LitKind::Str(sym, _) = lit.node {
 +                            return Some(sym.as_str());
 +                        }
 +                    }
 +
 +                    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 path_to_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()..] {
 +                for child in cx.tcx.item_children(*item_def_id) {
 +                    if child.ident.name == *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) = path_to_res(cx, module).opt_def_id() {
 +                for item in cx.tcx.item_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,
 +        ];
 +        // SymbolStr might be de-referenced: `&*symbol.as_str()`
 +        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);
 +    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 d8e241d72af48fa51222e9a3e86fbd01c0eb347b,0000000000000000000000000000000000000000..b92b6ca4f4380f981ee9f9ed163d44470994555c
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,148 @@@
- use clippy_utils::ty::is_type_diagnostic_item;
- use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::higher::{get_vec_init_kind, VecInitKind};
 +use clippy_utils::source::snippet;
- use rustc_ast::ast::LitKind;
++use clippy_utils::{path_to_local, path_to_local_id};
 +use if_chain::if_chain;
- use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, QPath, Stmt, StmtKind};
 +use rustc_errors::Applicability;
- use rustc_span::{symbol::sym, Span};
- use std::convert::TryInto;
++use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, HirId, Local, PatKind, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
- #[derive(Clone, Copy)]
- enum VecInitKind {
-     New,
-     WithCapacity(u64),
- }
++use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `push` immediately after creating a new `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `vec![]` macro is both more performant and easier to read than
 +    /// multiple `push` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut v = Vec::new();
 +    /// v.push(0);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let v = vec![0];
 +    /// ```
 +    pub VEC_INIT_THEN_PUSH,
 +    perf,
 +    "`push` immediately after `Vec` creation"
 +}
 +
 +impl_lint_pass!(VecInitThenPush => [VEC_INIT_THEN_PUSH]);
 +
 +#[derive(Default)]
 +pub struct VecInitThenPush {
 +    searcher: Option<VecPushSearcher>,
 +}
 +
-             VecInitKind::WithCapacity(x) if x > self.found => return,
 +struct VecPushSearcher {
 +    local_id: HirId,
 +    init: VecInitKind,
 +    lhs_is_local: bool,
 +    lhs_span: Span,
 +    err_span: Span,
 +    found: u64,
 +}
 +impl VecPushSearcher {
 +    fn display_err(&self, cx: &LateContext<'_>) {
 +        match self.init {
 +            _ if self.found == 0 => return,
- fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
-     if let ExprKind::Call(func, args) = expr.kind {
-         match func.kind {
-             ExprKind::Path(QPath::TypeRelative(ty, name))
-                 if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
-             {
-                 if name.ident.name == sym::new {
-                     return Some(VecInitKind::New);
-                 } else if name.ident.name.as_str() == "with_capacity" {
-                     return args.get(0).and_then(|arg| {
-                         if_chain! {
-                             if let ExprKind::Lit(lit) = &arg.kind;
-                             if let LitKind::Int(num, _) = lit.node;
-                             then {
-                                 Some(VecInitKind::WithCapacity(num.try_into().ok()?))
-                             } else {
-                                 None
-                             }
-                         }
-                     });
-                 }
-             }
-             ExprKind::Path(QPath::Resolved(_, path))
-                 if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
-                     && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
-             {
-                 return Some(VecInitKind::New);
-             }
-             _ => (),
-         }
-     }
-     None
- }
++            VecInitKind::WithLiteralCapacity(x) if x > self.found => return,
++            VecInitKind::WithExprCapacity(_) => return,
 +            _ => (),
 +        };
 +
 +        let mut s = if self.lhs_is_local {
 +            String::from("let ")
 +        } else {
 +            String::new()
 +        };
 +        s.push_str(&snippet(cx, self.lhs_span, ".."));
 +        s.push_str(" = vec![..];");
 +
 +        span_lint_and_sugg(
 +            cx,
 +            VEC_INIT_THEN_PUSH,
 +            self.err_span,
 +            "calls to `push` immediately after creation",
 +            "consider using the `vec![]` macro",
 +            s,
 +            Applicability::HasPlaceholders,
 +        );
 +    }
 +}
 +
 +impl LateLintPass<'_> for VecInitThenPush {
 +    fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
 +        self.searcher = None;
 +    }
 +
 +    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
 +        if_chain! {
 +            if !in_external_macro(cx.sess(), local.span);
 +            if let Some(init) = local.init;
 +            if let PatKind::Binding(BindingAnnotation::Mutable, id, _, None) = local.pat.kind;
 +            if let Some(init_kind) = get_vec_init_kind(cx, init);
 +            then {
 +                self.searcher = Some(VecPushSearcher {
 +                        local_id: id,
 +                        init: init_kind,
 +                        lhs_is_local: true,
 +                        lhs_span: local.ty.map_or(local.pat.span, |t| local.pat.span.to(t.span)),
 +                        err_span: local.span,
 +                        found: 0,
 +                    });
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if self.searcher.is_none();
 +            if !in_external_macro(cx.sess(), expr.span);
 +            if let ExprKind::Assign(left, right, _) = expr.kind;
 +            if let Some(id) = path_to_local(left);
 +            if let Some(init_kind) = get_vec_init_kind(cx, right);
 +            then {
 +                self.searcher = Some(VecPushSearcher {
 +                    local_id: id,
 +                    init: init_kind,
 +                    lhs_is_local: false,
 +                    lhs_span: left.span,
 +                    err_span: expr.span,
 +                    found: 0,
 +                });
 +            }
 +        }
 +    }
 +
 +    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
 +        if let Some(searcher) = self.searcher.take() {
 +            if_chain! {
 +                if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind;
 +                if let ExprKind::MethodCall(path, _, [self_arg, _], _) = expr.kind;
 +                if path_to_local_id(self_arg, searcher.local_id);
 +                if path.ident.name.as_str() == "push";
 +                then {
 +                    self.searcher = Some(VecPushSearcher {
 +                        found: searcher.found + 1,
 +                        err_span: searcher.err_span.to(stmt.span),
 +                        .. searcher
 +                    });
 +                } else {
 +                    searcher.display_err(cx);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx Block<'tcx>) {
 +        if let Some(searcher) = self.searcher.take() {
 +            searcher.display_err(cx);
 +        }
 +    }
 +}
index e7fca3ae5d401b8b3f84c4c71c340145a95d27cb,0000000000000000000000000000000000000000..d99a3d9359e1f439ba973431874f2b2b3c3941d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,18 @@@
- version = "0.1.57"
 +[package]
 +name = "clippy_utils"
++version = "0.1.58"
 +edition = "2021"
 +publish = false
 +
 +[dependencies]
 +if_chain = "1.0"
 +rustc-semver = "1.1"
 +
 +[features]
 +deny-warnings = []
 +internal-lints = []
 +metadata-collector-lint = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 74cf323720cbb400528a0cd416a361315f1288cb,0000000000000000000000000000000000000000..60c4cb361aa6c0dfbc2d9ff534ca2569d9e7db18
mode 100644,000000..100644
--- /dev/null
@@@ -1,633 -1,0 +1,784 @@@
- use crate::{is_expn_of, match_def_path, paths};
 +//! This module contains functions that retrieves specifiec elements.
 +
 +#![deny(clippy::missing_docs_in_private_items)]
 +
- use rustc_hir::{Arm, Block, BorrowKind, Expr, ExprKind, LoopSource, MatchSource, Node, Pat, StmtKind, UnOp};
++use crate::ty::is_type_diagnostic_item;
++use crate::{is_expn_of, last_path_segment, match_def_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_hir as hir;
- use rustc_span::{sym, ExpnKind, Span, Symbol};
++use rustc_hir::{
++    Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp,
++};
 +use rustc_lint::LateContext;
++use rustc_span::{sym, symbol, ExpnKind, Span, Symbol};
 +
 +/// The essential nodes of a desugared for loop as well as the entire span:
 +/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
 +pub struct ForLoop<'tcx> {
 +    /// `for` loop item
 +    pub pat: &'tcx hir::Pat<'tcx>,
 +    /// `IntoIterator` argument
 +    pub arg: &'tcx hir::Expr<'tcx>,
 +    /// `for` loop body
 +    pub body: &'tcx hir::Expr<'tcx>,
 +    /// entire `for` loop span
 +    pub span: Span,
 +}
 +
 +impl<'tcx> ForLoop<'tcx> {
 +    #[inline]
 +    /// Parses a desugared `for` loop
 +    pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
 +            if let Some(first_arm) = arms.get(0);
 +            if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
 +            if let Some(first_arg) = iterargs.get(0);
 +            if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
 +            if let hir::ExprKind::Loop(block, ..) = first_arm.body.kind;
 +            if block.expr.is_none();
 +            if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
 +            if let hir::StmtKind::Local(local) = let_stmt.kind;
 +            if let hir::StmtKind::Expr(body_expr) = body.kind;
 +            then {
 +                return Some(Self {
 +                    pat: &*local.pat,
 +                    arg: first_arg,
 +                    body: body_expr,
 +                    span: first_arm.span
 +                });
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +/// An `if` expression without `DropTemps`
 +pub struct If<'hir> {
 +    /// `if` condition
 +    pub cond: &'hir Expr<'hir>,
 +    /// `if` then expression
 +    pub then: &'hir Expr<'hir>,
 +    /// `else` expression
 +    pub r#else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> If<'hir> {
 +    #[inline]
 +    /// Parses an `if` expression
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(
 +            Expr {
 +                kind: ExprKind::DropTemps(cond),
 +                ..
 +            },
 +            then,
 +            r#else,
 +        ) = expr.kind
 +        {
 +            Some(Self { cond, then, r#else })
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// An `if let` expression
 +pub struct IfLet<'hir> {
 +    /// `if let` pattern
 +    pub let_pat: &'hir Pat<'hir>,
 +    /// `if let` scrutinee
 +    pub let_expr: &'hir Expr<'hir>,
 +    /// `if let` then expression
 +    pub if_then: &'hir Expr<'hir>,
 +    /// `if let` else expression
 +    pub if_else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> IfLet<'hir> {
 +    /// Parses an `if let` expression
 +    pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(
 +            Expr {
 +                kind: ExprKind::Let(let_pat, let_expr, _),
 +                ..
 +            },
 +            if_then,
 +            if_else,
 +        ) = expr.kind
 +        {
 +            let mut iter = cx.tcx.hir().parent_iter(expr.hir_id);
 +            if let Some((_, Node::Block(Block { stmts: [], .. }))) = iter.next() {
 +                if let Some((
 +                    _,
 +                    Node::Expr(Expr {
 +                        kind: ExprKind::Loop(_, _, LoopSource::While, _),
 +                        ..
 +                    }),
 +                )) = iter.next()
 +                {
 +                    // while loop desugar
 +                    return None;
 +                }
 +            }
 +            return Some(Self {
 +                let_pat,
 +                let_expr,
 +                if_then,
 +                if_else,
 +            });
 +        }
 +        None
 +    }
 +}
 +
 +/// An `if let` or `match` expression. Useful for lints that trigger on one or the other.
 +pub enum IfLetOrMatch<'hir> {
 +    /// Any `match` expression
 +    Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource),
 +    /// scrutinee, pattern, then block, else block
 +    IfLet(
 +        &'hir Expr<'hir>,
 +        &'hir Pat<'hir>,
 +        &'hir Expr<'hir>,
 +        Option<&'hir Expr<'hir>>,
 +    ),
 +}
 +
 +impl<'hir> IfLetOrMatch<'hir> {
 +    /// Parses an `if let` or `match` expression
 +    pub fn parse(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
 +        match expr.kind {
 +            ExprKind::Match(expr, arms, source) => Some(Self::Match(expr, arms, source)),
 +            _ => IfLet::hir(cx, expr).map(
 +                |IfLet {
 +                     let_expr,
 +                     let_pat,
 +                     if_then,
 +                     if_else,
 +                 }| { Self::IfLet(let_expr, let_pat, if_then, if_else) },
 +            ),
 +        }
 +    }
 +}
 +
 +/// An `if` or `if let` expression
 +pub struct IfOrIfLet<'hir> {
 +    /// `if` condition that is maybe a `let` expression
 +    pub cond: &'hir Expr<'hir>,
 +    /// `if` then expression
 +    pub then: &'hir Expr<'hir>,
 +    /// `else` expression
 +    pub r#else: Option<&'hir Expr<'hir>>,
 +}
 +
 +impl<'hir> IfOrIfLet<'hir> {
 +    #[inline]
 +    /// Parses an `if` or `if let` expression
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::If(cond, then, r#else) = expr.kind {
 +            if let ExprKind::DropTemps(new_cond) = cond.kind {
 +                return Some(Self {
 +                    cond: new_cond,
 +                    r#else,
 +                    then,
 +                });
 +            }
 +            if let ExprKind::Let(..) = cond.kind {
 +                return Some(Self { cond, then, r#else });
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +/// Represent a range akin to `ast::ExprKind::Range`.
 +#[derive(Debug, Copy, Clone)]
 +pub struct Range<'a> {
 +    /// The lower bound of the range, or `None` for ranges such as `..X`.
 +    pub start: Option<&'a hir::Expr<'a>>,
 +    /// The upper bound of the range, or `None` for ranges such as `X..`.
 +    pub end: Option<&'a hir::Expr<'a>>,
 +    /// Whether the interval is open or closed.
 +    pub limits: ast::RangeLimits,
 +}
 +
 +impl<'a> Range<'a> {
 +    /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
 +    pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
 +        /// Finds the field named `name` in the field. Always return `Some` for
 +        /// convenience.
 +        fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
 +            let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
 +            Some(expr)
 +        }
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(path, args)
 +                if matches!(
 +                    path.kind,
 +                    hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
 +                ) =>
 +            {
 +                Some(Range {
 +                    start: Some(&args[0]),
 +                    end: Some(&args[1]),
 +                    limits: ast::RangeLimits::Closed,
 +                })
 +            },
 +            hir::ExprKind::Struct(path, fields, None) => match &path {
 +                hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
 +                    start: None,
 +                    end: None,
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
 +                    start: Some(get_field("start", fields)?),
 +                    end: None,
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
 +                    start: Some(get_field("start", fields)?),
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
 +                    start: None,
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::Closed,
 +                }),
 +                hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
 +                    start: None,
 +                    end: Some(get_field("end", fields)?),
 +                    limits: ast::RangeLimits::HalfOpen,
 +                }),
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    }
 +}
 +
 +/// Represent the pre-expansion arguments of a `vec!` invocation.
 +pub enum VecArgs<'a> {
 +    /// `vec![elem; len]`
 +    Repeat(&'a hir::Expr<'a>, &'a hir::Expr<'a>),
 +    /// `vec![a, b, c]`
 +    Vec(&'a [hir::Expr<'a>]),
 +}
 +
 +impl<'a> VecArgs<'a> {
 +    /// Returns the arguments of the `vec!` macro if this expression was expanded
 +    /// from `vec!`.
 +    pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
 +        if_chain! {
 +            if let hir::ExprKind::Call(fun, args) = expr.kind;
 +            if let hir::ExprKind::Path(ref qpath) = fun.kind;
 +            if is_expn_of(fun.span, "vec").is_some();
 +            if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +            then {
 +                return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
 +                    // `vec![elem; size]` case
 +                    Some(VecArgs::Repeat(&args[0], &args[1]))
 +                }
 +                else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
 +                    // `vec![a, b, c]` case
 +                    if_chain! {
 +                        if let hir::ExprKind::Box(boxed) = args[0].kind;
 +                        if let hir::ExprKind::Array(args) = boxed.kind;
 +                        then {
 +                            return Some(VecArgs::Vec(args));
 +                        }
 +                    }
 +
 +                    None
 +                }
 +                else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
 +                    Some(VecArgs::Vec(&[]))
 +                }
 +                else {
 +                    None
 +                };
 +            }
 +        }
 +
 +        None
 +    }
 +}
 +
 +/// A desugared `while` loop
 +pub struct While<'hir> {
 +    /// `while` loop condition
 +    pub condition: &'hir Expr<'hir>,
 +    /// `while` loop body
 +    pub body: &'hir Expr<'hir>,
 +}
 +
 +impl<'hir> While<'hir> {
 +    #[inline]
 +    /// Parses a desugared `while` loop
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::Loop(
 +            Block {
 +                expr:
 +                    Some(Expr {
 +                        kind:
 +                            ExprKind::If(
 +                                Expr {
 +                                    kind: ExprKind::DropTemps(condition),
 +                                    ..
 +                                },
 +                                body,
 +                                _,
 +                            ),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +            LoopSource::While,
 +            _,
 +        ) = expr.kind
 +        {
 +            return Some(Self { condition, body });
 +        }
 +        None
 +    }
 +}
 +
 +/// A desugared `while let` loop
 +pub struct WhileLet<'hir> {
 +    /// `while let` loop item pattern
 +    pub let_pat: &'hir Pat<'hir>,
 +    /// `while let` loop scrutinee
 +    pub let_expr: &'hir Expr<'hir>,
 +    /// `while let` loop body
 +    pub if_then: &'hir Expr<'hir>,
 +}
 +
 +impl<'hir> WhileLet<'hir> {
 +    #[inline]
 +    /// Parses a desugared `while let` loop
 +    pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
 +        if let ExprKind::Loop(
 +            Block {
 +                expr:
 +                    Some(Expr {
 +                        kind:
 +                            ExprKind::If(
 +                                Expr {
 +                                    kind: ExprKind::Let(let_pat, let_expr, _),
 +                                    ..
 +                                },
 +                                if_then,
 +                                _,
 +                            ),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +            LoopSource::While,
 +            _,
 +        ) = expr.kind
 +        {
 +            return Some(Self {
 +                let_pat,
 +                let_expr,
 +                if_then,
 +            });
 +        }
 +        None
 +    }
 +}
 +
 +/// Converts a hir binary operator to the corresponding `ast` type.
 +#[must_use]
 +pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
 +    match op {
 +        hir::BinOpKind::Eq => ast::BinOpKind::Eq,
 +        hir::BinOpKind::Ge => ast::BinOpKind::Ge,
 +        hir::BinOpKind::Gt => ast::BinOpKind::Gt,
 +        hir::BinOpKind::Le => ast::BinOpKind::Le,
 +        hir::BinOpKind::Lt => ast::BinOpKind::Lt,
 +        hir::BinOpKind::Ne => ast::BinOpKind::Ne,
 +        hir::BinOpKind::Or => ast::BinOpKind::Or,
 +        hir::BinOpKind::Add => ast::BinOpKind::Add,
 +        hir::BinOpKind::And => ast::BinOpKind::And,
 +        hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
 +        hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
 +        hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
 +        hir::BinOpKind::Div => ast::BinOpKind::Div,
 +        hir::BinOpKind::Mul => ast::BinOpKind::Mul,
 +        hir::BinOpKind::Rem => ast::BinOpKind::Rem,
 +        hir::BinOpKind::Shl => ast::BinOpKind::Shl,
 +        hir::BinOpKind::Shr => ast::BinOpKind::Shr,
 +        hir::BinOpKind::Sub => ast::BinOpKind::Sub,
 +    }
 +}
 +
 +/// Extract args from an assert-like macro.
 +/// Currently working with:
 +/// - `assert!`, `assert_eq!` and `assert_ne!`
 +/// - `debug_assert!`, `debug_assert_eq!` and `debug_assert_ne!`
 +/// For example:
 +/// `assert!(expr)` will return `Some([expr])`
 +/// `debug_assert_eq!(a, b)` will return `Some([a, b])`
 +pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx Expr<'tcx>>> {
 +    /// Try to match the AST for a pattern that contains a match, for example when two args are
 +    /// compared
 +    fn ast_matchblock(matchblock_expr: &'tcx Expr<'tcx>) -> Option<Vec<&Expr<'_>>> {
 +        if_chain! {
 +            if let ExprKind::Match(headerexpr, _, _) = &matchblock_expr.kind;
 +            if let ExprKind::Tup([lhs, rhs]) = &headerexpr.kind;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, lhs) = lhs.kind;
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, rhs) = rhs.kind;
 +            then {
 +                return Some(vec![lhs, rhs]);
 +            }
 +        }
 +        None
 +    }
 +
 +    if let ExprKind::Block(block, _) = e.kind {
 +        if block.stmts.len() == 1 {
 +            if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
 +                // macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
 +                if_chain! {
 +                    if let Some(If { cond, .. }) = If::hir(matchexpr);
 +                    if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
 +                    then {
 +                        return Some(vec![condition]);
 +                    }
 +                }
 +
 +                // debug macros with two args: `debug_assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
 +                if_chain! {
 +                    if let ExprKind::Block(matchblock,_) = matchexpr.kind;
 +                    if let Some(matchblock_expr) = matchblock.expr;
 +                    then {
 +                        return ast_matchblock(matchblock_expr);
 +                    }
 +                }
 +            }
 +        } else if let Some(matchblock_expr) = block.expr {
 +            // macros with two args: `assert_{ne, eq}` (e.g., `assert_ne!(a, b)`)
 +            return ast_matchblock(matchblock_expr);
 +        }
 +    }
 +    None
 +}
 +
 +/// A parsed `format!` expansion
 +pub struct FormatExpn<'tcx> {
 +    /// Span of `format!(..)`
 +    pub call_site: Span,
 +    /// Inner `format_args!` expansion
 +    pub format_args: FormatArgsExpn<'tcx>,
 +}
 +
 +impl FormatExpn<'tcx> {
 +    /// Parses an expanded `format!` invocation
 +    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let ExprKind::Block(block, _) = expr.kind;
 +            if let [stmt] = block.stmts;
 +            if let StmtKind::Local(local) = stmt.kind;
 +            if let Some(init) = local.init;
 +            if let ExprKind::Call(_, [format_args]) = init.kind;
 +            let expn_data = expr.span.ctxt().outer_expn_data();
 +            if let ExpnKind::Macro(_, sym::format) = expn_data.kind;
 +            if let Some(format_args) = FormatArgsExpn::parse(format_args);
 +            then {
 +                Some(FormatExpn {
 +                    call_site: expn_data.call_site,
 +                    format_args,
 +                })
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +}
 +
 +/// A parsed `format_args!` expansion
 +pub struct FormatArgsExpn<'tcx> {
 +    /// Span of the first argument, the format string
 +    pub format_string_span: Span,
 +    /// Values passed after the format string
 +    pub value_args: Vec<&'tcx Expr<'tcx>>,
 +
 +    /// String literal expressions which represent the format string split by "{}"
 +    pub format_string_parts: &'tcx [Expr<'tcx>],
 +    /// Symbols corresponding to [`Self::format_string_parts`]
 +    pub format_string_symbols: Vec<Symbol>,
 +    /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)`
 +    pub args: &'tcx [Expr<'tcx>],
 +    /// The final argument passed to `Arguments::new_v1_formatted`, if applicable
 +    pub fmt_expr: Option<&'tcx Expr<'tcx>>,
 +}
 +
 +impl FormatArgsExpn<'tcx> {
 +    /// Parses an expanded `format_args!` or `format_args_nl!` invocation
 +    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let ExpnKind::Macro(_, name) = expr.span.ctxt().outer_expn_data().kind;
 +            let name = name.as_str();
 +            if name.ends_with("format_args") || name.ends_with("format_args_nl");
 +            if let ExprKind::Call(_, args) = expr.kind;
 +            if let Some((strs_ref, args, fmt_expr)) = match args {
 +                // Arguments::new_v1
 +                [strs_ref, args] => Some((strs_ref, args, None)),
 +                // Arguments::new_v1_formatted
 +                [strs_ref, args, fmt_expr, _unsafe_arg] => Some((strs_ref, args, Some(fmt_expr))),
 +                _ => None,
 +            };
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, strs_arr) = strs_ref.kind;
 +            if let ExprKind::Array(format_string_parts) = strs_arr.kind;
 +            if let Some(format_string_symbols) = format_string_parts
 +                .iter()
 +                .map(|e| {
 +                    if let ExprKind::Lit(lit) = &e.kind {
 +                        if let LitKind::Str(symbol, _style) = lit.node {
 +                            return Some(symbol);
 +                        }
 +                    }
 +                    None
 +                })
 +                .collect();
 +            if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args.kind;
 +            if let ExprKind::Match(args, [arm], _) = args.kind;
 +            if let ExprKind::Tup(value_args) = args.kind;
 +            if let Some(value_args) = value_args
 +                .iter()
 +                .map(|e| match e.kind {
 +                    ExprKind::AddrOf(_, _, e) => Some(e),
 +                    _ => None,
 +                })
 +                .collect();
 +            if let ExprKind::Array(args) = arm.body.kind;
 +            then {
 +                Some(FormatArgsExpn {
 +                    format_string_span: strs_ref.span,
 +                    value_args,
 +                    format_string_parts,
 +                    format_string_symbols,
 +                    args,
 +                    fmt_expr,
 +                })
 +            } else {
 +                None
 +            }
 +        }
 +    }
++
++    /// Returns a vector of `FormatArgsArg`.
++    pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
++        if let Some(expr) = self.fmt_expr {
++            if_chain! {
++                if let ExprKind::AddrOf(BorrowKind::Ref, _, expr) = expr.kind;
++                if let ExprKind::Array(exprs) = expr.kind;
++                then {
++                    exprs.iter().map(|fmt| {
++                        if_chain! {
++                            // struct `core::fmt::rt::v1::Argument`
++                            if let ExprKind::Struct(_, fields, _) = fmt.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;
++                            then {
++                                let i = usize::try_from(position).unwrap();
++                                Some(FormatArgsArg { value: self.value_args[i], arg: &self.args[i], fmt: Some(fmt) })
++                            } else {
++                                None
++                            }
++                        }
++                    }).collect()
++                } else {
++                    None
++                }
++            }
++        } else {
++            Some(
++                self.value_args
++                    .iter()
++                    .zip(self.args.iter())
++                    .map(|(value, arg)| FormatArgsArg { value, arg, fmt: None })
++                    .collect(),
++            )
++        }
++    }
++}
++
++/// 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 arg: &'tcx Expr<'tcx>,
++    /// An element of `fmt_expn`
++    pub fmt: 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.fmt.map_or(false, |fmt| {
++            // `!` because these conditions check that `self` is unformatted.
++            !if_chain! {
++                // struct `core::fmt::rt::v1::Argument`
++                if let ExprKind::Struct(_, fields, _) = fmt.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;
++                let mut precision_found = false;
++                let mut width_found = false;
++                if subfields.iter().all(|field| {
++                    match field.ident.name {
++                        sym::precision => {
++                            precision_found = true;
++                            if let ExprKind::Path(ref precision_path) = field.expr.kind {
++                                last_path_segment(precision_path).ident.name == sym::Implied
++                            } else {
++                                false
++                            }
++                        }
++                        sym::width => {
++                            width_found = true;
++                            if let ExprKind::Path(ref width_qpath) = field.expr.kind {
++                                last_path_segment(width_qpath).ident.name == sym::Implied
++                            } else {
++                                false
++                            }
++                        }
++                        _ => true,
++                    }
++                });
++                if precision_found && width_found;
++                then { true } else { false }
++            }
++        })
++    }
++
++    /// Returns true if the argument is formatted using `Display::fmt`.
++    pub fn is_display(&self) -> bool {
++        if_chain! {
++            if let ExprKind::Call(_, [_, format_field]) = self.arg.kind;
++            if let ExprKind::Path(QPath::Resolved(_, path)) = format_field.kind;
++            if let [.., t, _] = path.segments;
++            if t.ident.name == sym::Display;
++            then { true } else { false }
++        }
++    }
 +}
 +
 +/// Checks if a `let` statement is from a `for` loop desugaring.
 +pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
 +    // This will detect plain for-loops without an actual variable binding:
 +    //
 +    // ```
 +    // for x in some_vec {
 +    //     // do stuff
 +    // }
 +    // ```
 +    if_chain! {
 +        if let Some(expr) = local.init;
 +        if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
 +        then {
 +            return true;
 +        }
 +    }
 +
 +    // This detects a variable binding in for loop to avoid `let_unit_value`
 +    // lint (see issue #1964).
 +    //
 +    // ```
 +    // for _ in vec![()] {
 +    //     // anything
 +    // }
 +    // ```
 +    if let hir::LocalSource::ForLoopDesugar = local.source {
 +        return true;
 +    }
 +
 +    false
 +}
 +
 +/// A parsed `panic!` expansion
 +pub struct PanicExpn<'tcx> {
 +    /// Span of `panic!(..)`
 +    pub call_site: Span,
 +    /// Inner `format_args!` expansion
 +    pub format_args: FormatArgsExpn<'tcx>,
 +}
 +
 +impl PanicExpn<'tcx> {
 +    /// Parses an expanded `panic!` invocation
 +    pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> {
 +        if_chain! {
 +            if let ExprKind::Block(block, _) = expr.kind;
 +            if let Some(init) = block.expr;
 +            if let ExprKind::Call(_, [format_args]) = init.kind;
 +            let expn_data = expr.span.ctxt().outer_expn_data();
 +            if let Some(format_args) = FormatArgsExpn::parse(format_args);
 +            then {
 +                Some(PanicExpn {
 +                    call_site: expn_data.call_site,
 +                    format_args,
 +                })
 +            } else {
 +                None
 +            }
 +        }
 +    }
 +}
++
++/// A parsed `Vec` initialization expression
++#[derive(Clone, Copy)]
++pub enum VecInitKind {
++    /// `Vec::new()`
++    New,
++    /// `Vec::default()` or `Default::default()`
++    Default,
++    /// `Vec::with_capacity(123)`
++    WithLiteralCapacity(u64),
++    /// `Vec::with_capacity(slice.len())`
++    WithExprCapacity(HirId),
++}
++
++/// Checks if given expression is an initialization of `Vec` and returns its kind.
++pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<VecInitKind> {
++    if let ExprKind::Call(func, args) = expr.kind {
++        match func.kind {
++            ExprKind::Path(QPath::TypeRelative(ty, name))
++                if is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::Vec) =>
++            {
++                if name.ident.name == sym::new {
++                    return Some(VecInitKind::New);
++                } else if name.ident.name == symbol::kw::Default {
++                    return Some(VecInitKind::Default);
++                } else if name.ident.name.as_str() == "with_capacity" {
++                    let arg = args.get(0)?;
++                    if_chain! {
++                        if let ExprKind::Lit(lit) = &arg.kind;
++                        if let LitKind::Int(num, _) = lit.node;
++                        then {
++                            return Some(VecInitKind::WithLiteralCapacity(num.try_into().ok()?))
++                        }
++                    }
++                    return Some(VecInitKind::WithExprCapacity(arg.hir_id));
++                }
++            }
++            ExprKind::Path(QPath::Resolved(_, path))
++                if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD)
++                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) =>
++            {
++                return Some(VecInitKind::Default);
++            }
++            _ => (),
++        }
++    }
++    None
++}
index 8e94d16a33a0e41b1a167447e50b098bca917cf6,0000000000000000000000000000000000000000..9bc380ca6caa6501c526c4edd18b2faa11df4a5c
mode 100644,000000..100644
--- /dev/null
@@@ -1,2117 -1,0 +1,2165 @@@
-     def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl,
-     ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node, Param, Pat,
-     PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
 +#![feature(box_patterns)]
 +#![feature(in_band_lifetimes)]
 +#![feature(iter_zip)]
 +#![feature(rustc_private)]
 +#![feature(control_flow_enum)]
 +#![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 camel_case;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +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 if_chain::if_chain;
 +use rustc_ast::ast::{self, Attribute, LitKind};
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
++use rustc_hir::itemlikevisit::ItemLikeVisitor;
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
-     if span.from_expansion() {
-         !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
-     } else {
-         false
-     }
++    def, Arm, BindingAnnotation, Block, Body, Constness, Destination, Expr, ExprKind, FnDecl, ForeignItem, GenericArgs,
++    HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource, Mutability, Node,
++    Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitRef, TyKind,
++    UnOp,
 +};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::exports::Export;
 +use rustc_middle::hir::map::Map;
 +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};
 +
 +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 {
 +    (LateContext) => {
 +        extract_msrv_attr!(@LateContext, ());
 +    };
 +    (EarlyContext) => {
 +        extract_msrv_attr!(@EarlyContext);
 +    };
 +    (@$context:ident$(, $call:tt)?) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'tcx>, attrs: &'tcx [rustc_ast::ast::Attribute]) {
 +            use $crate::get_unique_inner_attr;
 +            match get_unique_inner_attr(cx.sess$($call)?, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(
 +                            &msrv.to_string(),
 +                            Some(cx.sess$($call)?),
 +                            Some(msrv_attr.span),
 +                        );
 +                    } else {
 +                        cx.sess$($call)?.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// Returns `true` if the two spans come from differing expansions (i.e., one is
 +/// from a macro and one isn't).
 +#[must_use]
 +pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
 +    rhs.ctxt() != lhs.ctxt()
 +}
 +
 +/// 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:
 +/// ```ignore
 +/// 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(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
 +}
 +
 +/// Returns `true` if this `span` was expanded by any macro.
 +#[must_use]
 +pub fn in_macro(span: Span) -> bool {
-         value == v
-     } else {
-         false
++    span.from_expansion() && !matches!(span.ctxt().outer_expn_data().kind, ExpnKind::Desugaring(..))
 +}
 +
 +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 first type parameter is a lang item.
 +pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
 +    let ty = get_qpath_generic_tys(qpath).next()?;
 +
 +    if let TyKind::Path(qpath) = &ty.kind {
 +        cx.qpath_res(qpath, ty.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |id| {
 +                cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
 +            })
 +            .then(|| ty)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Checks if the first type parameter is a diagnostic item.
 +pub fn is_ty_param_diagnostic_item(
 +    cx: &LateContext<'_>,
 +    qpath: &QPath<'tcx>,
 +    item: Symbol,
 +) -> Option<&'tcx hir::Ty<'tcx>> {
 +    let ty = get_qpath_generic_tys(qpath).next()?;
 +
 +    if let TyKind::Path(qpath) = &ty.kind {
 +        cx.qpath_res(qpath, ty.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
 +            .then(|| ty)
 +    } else {
 +        None
 +    }
 +}
 +
 +/// 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 get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
 +    match path {
 +        QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
 +        QPath::TypeRelative(_, s) => s.args,
 +        QPath::LangItem(..) => None,
 +    }
 +}
 +
 +pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    get_qpath_generics(path)
 +        .map_or([].as_ref(), |a| a.args)
 +        .iter()
 +        .filter_map(|a| {
 +            if let hir::GenericArg::Type(ty) = a {
 +                Some(ty)
 +            } else {
 +                None
 +            }
 +        })
 +}
 +
 +pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.get(0),
 +        QPath::TypeRelative(_, seg) => Some(seg),
 +        QPath::LangItem(..) => 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, resolve it. Otherwise, return `Res::Err`.
 +pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
 +    if let ExprKind::Path(p) = &expr.kind {
 +        cx.qpath_res(p, expr.hir_id)
 +    } else {
 +        Res::Err
 +    }
 +}
 +
 +/// Resolves the path to a `DefId` and checks if it matches the given path.
 +pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
 +    cx.qpath_res(path, hir_id)
 +        .opt_def_id()
 +        .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 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 {
 +    expr_path_res(cx, expr)
 +        .opt_def_id()
 +        .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 {
 +    expr_path_res(cx, expr)
 +        .opt_def_id()
 +        .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)
 +}
 +
 +/// Gets the definition associated to a path.
 +pub fn path_to_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>(tcx: TyCtxt<'tcx>, def_id: DefId, name: &str) -> Option<&'tcx Export> {
 +        tcx.item_children(def_id)
 +            .iter()
 +            .find(|item| item.ident.name.as_str() == name)
 +    }
 +
 +    let (krate, first, path) = match *path {
 +        [krate, first, ref path @ ..] => (krate, first, path),
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let crates = tcx.crates(());
 +    let krate = try_res!(crates.iter().find(|&&num| tcx.crate_name(num).as_str() == krate));
 +    let first = try_res!(item_child_by_name(tcx, krate.as_def_id(), first));
 +    let last = path
 +        .iter()
 +        .copied()
 +        // `get_def_path` seems to generate these empty segments for extern blocks.
 +        // We can just ignore them.
 +        .filter(|segment| !segment.is_empty())
 +        // for each segment, find the child item
 +        .try_fold(first, |item, segment| {
 +            let def_id = item.res.def_id();
 +            if let Some(item) = item_child_by_name(tcx, def_id, segment) {
 +                Some(item)
 +            } else if matches!(item.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).res.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 path_to_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>, hir_id: HirId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != hir::CRATE_HIR_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get(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)
 +}
 +
 +/// 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;
 +    }
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        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
 +}
 +
 +/// 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, y) => if_chain! {
 +            if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(y.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, _) => if_chain! {
 +            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();
 +            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
 +            }
 +        },
 +        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 statments.
 +/// * 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(
 +    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(_)
 +        | ExprKind::LlvmInlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage(cx: &LateContext<'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(pat, ..) => {
 +                    let mutability = match pat_capture_kind(cx, 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(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 Visitor<'tcx> for V<'_, 'tcx> {
 +        type Map = ErasedMap<'tcx>;
 +        fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +            NestedVisitorMap::None
 +        }
 +
 +        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(borrow) => match borrow.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, span, 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(*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);
 +    let def_id = cx.tcx.hir().local_def_id(parent).to_def_id();
 +    Some(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(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 {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_name(&mut self, _: Span, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// 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 {
 +    struct RetCallFinder {
 +        found: bool,
 +    }
 +
 +    impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
 +        type Map = Map<'tcx>;
 +
 +        fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
 +            if self.found {
 +                return;
 +            }
 +            if let hir::ExprKind::Ret(..) = &expr.kind {
 +                self.found = true;
 +            } else {
 +                hir::intravisit::walk_expr(self, expr);
 +            }
 +        }
 +
 +        fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
 +            hir::intravisit::NestedVisitorMap::None
 +        }
 +    }
 +
 +    let mut visitor = RetCallFinder { found: false };
 +    visitor.visit_expr(expr);
 +    visitor.found
 +}
 +
 +struct FindMacroCalls<'a, 'b> {
 +    names: &'a [&'b str],
 +    result: Vec<Span>,
 +}
 +
 +impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
 +    type Map = Map<'tcx>;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
 +            self.result.push(expr.span);
 +        }
 +        // and check sub-expressions
 +        intravisit::walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
 +        NestedVisitorMap::None
 +    }
 +}
 +
 +/// Finds calls of the specified macros in a function body.
 +pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
 +    let mut fmc = FindMacroCalls {
 +        names,
 +        result: Vec::new(),
 +    };
 +    fmc.visit_expr(&body.value);
 +    fmc.result
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust,ignore
 +///        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: 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,
 +    }
 +}
 +
 +/// 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) {
- /// See also `is_direct_expn_of`.
++        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 is this comes from an expansion of the
 +/// macro `name`.
- /// The difference with `is_expn_of` is that in
- /// ```rust,ignore
++/// 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`.
- /// `bar!` by
- /// `is_direct_expn_of`.
++/// The difference with [`is_expn_of`] is that in
++/// ```rust
++/// # macro_rules! foo { ($e:tt) => { $e } }; macro_rules! bar { ($e:expr) => { $e } }
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
-     if_chain! {
-         if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind;
-         if let Res::SelfTy(..) = path.res;
-         then {
-             return true
++/// 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)
 +}
 +
 +/// 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 {
 +    attrs.iter().any(|attr| attr.has_name(sym::automatically_derived))
 +}
 +
 +/// Remove blocks around an expression.
 +///
 +/// Ie. `x`, `{ x }` and `{{{{ x }}}}` all give `x`. `{ x; y }` and `{}` return
 +/// themselves.
 +pub fn remove_blocks<'tcx>(mut expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
 +    while let ExprKind::Block(block, ..) = expr.kind {
 +        match (block.stmts.is_empty(), block.expr.as_ref()) {
 +            (true, Some(e)) => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +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_chain! {
-         if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-         if let Res::Def(_, def_id) = path.res;
-         then {
-             cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr)
-         } else {
-             false
++    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 any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if is_automatically_derived(map.attrs(enclosing_node)) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.get_parent_item(enclosing_node);
 +    }
 +    false
 +}
 +
 +/// 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 `match_any_diagnostic_items` 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 any of provided diagnostic items. Returns the index of
 +/// matching path, if any.
 +pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
 +    diag_items
 +        .iter()
 +        .position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
 +}
 +
 +/// 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())
 +}
 +
 +pub fn match_panic_call(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(func, [arg]) = expr.kind {
 +        expr_path_res(cx, func)
 +            .opt_def_id()
 +            .map_or(false, |id| match_panic_def_id(cx, id))
 +            .then(|| arg)
 +    } else {
 +        None
 +    }
 +}
 +
 +pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
 +    match_any_def_paths(
 +        cx,
 +        did,
 +        &[
 +            &paths::BEGIN_PANIC,
 +            &paths::PANIC_ANY,
 +            &paths::PANICKING_PANIC,
 +            &paths::PANICKING_PANIC_FMT,
 +            &paths::PANICKING_PANIC_STR,
 +        ],
 +    )
 +    .is_some()
 +}
 +
 +/// 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: 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)),
 +        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
 +        _ => false,
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +pub fn get_expr_use_or_unification_node(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<Node<'tcx>> {
 +    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)),
 +            },
 +            Some((_, node)) => break Some(node),
 +        }
 +    }
 +}
 +
 +/// 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 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
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust,ignore
 +/// 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(cx.tcx) { 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(cx.tcx).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(cx.tcx).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(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel(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(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(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
 +}
 +
 +#[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;
 +            },
 +        }
 +    }};
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
- /// Checks whether item either has `test` attribute applied, or
- /// is a module with `test` in its name.
- pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
-     if let Some(def_id) = tcx.hir().opt_local_def_id(item.hir_id()) {
-         if tcx.has_attr(def_id.to_def_id(), sym::test) {
-             return true;
++    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
 +}
 +
-     matches!(item.kind, ItemKind::Mod(..)) && item.ident.name.as_str().contains("test")
++struct VisitConstTestStruct<'tcx> {
++    tcx: TyCtxt<'tcx>,
++    names: Vec<Symbol>,
++    found: bool,
++}
++impl<'hir> ItemLikeVisitor<'hir> for VisitConstTestStruct<'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`
++                // and the `#[rustc_test_marker]` attribute?
++                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.contains(&item.ident.name) {
++                        self.found = true;
++                    }
++                }
++            }
 +        }
 +    }
++    fn visit_trait_item(&mut self, _: &TraitItem<'_>) {}
++    fn visit_impl_item(&mut self, _: &ImplItem<'_>) {}
++    fn visit_foreign_item(&mut self, _: &ForeignItem<'_>) {}
++}
++
++/// Checks if the function containing the given `HirId` is a `#[test]` function
++///
++/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
++pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
++    let names: Vec<_> = tcx
++        .hir()
++        .parent_iter(id)
++        // Since you can nest functions we need to collect all until we leave
++        // function scope
++        .filter_map(|(_id, node)| {
++            if let Node::Item(item) = node {
++                if let ItemKind::Fn(_, _, _) = item.kind {
++                    return Some(item.ident.name);
++                }
++            }
++            None
++        })
++        .collect();
++    let parent_mod = tcx.parent_module(id);
++    let mut vis = VisitConstTestStruct {
++        tcx,
++        names,
++        found: false,
++    };
++    tcx.hir().visit_item_likes_in_module(parent_mod, &mut vis);
++    vis.found
++}
 +
++/// Checks whether item either has `test` attribute appelied, or
++/// is a module with `test` in its name.
++///
++/// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`.
++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 81aff585ded1be868421a758467c3a6ec84065f3,0000000000000000000000000000000000000000..501b08a47f161103b9f34b46760ae2502df8d4bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,206 @@@
 +//! 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.
 +
 +pub const ANY_TRAIT: [&str; 3] = ["core", "any", "Any"];
 +#[cfg(feature = "metadata-collector-lint")]
 +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 +#[cfg(feature = "metadata-collector-lint")]
 +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 = "metadata-collector-lint")]
 +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
++#[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"];
 +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
 +/// Preferably use the diagnostic item `sym::Borrow` where possible
 +pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
 +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 CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
 +pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
 +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"];
 +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"];
 +#[cfg(feature = "internal-lints")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
++#[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"];
 +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"];
++#[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 FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 +pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
 +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"];
 +pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
 +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-lints")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal-lints")]
 +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-lints")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal-lints")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +pub const LIBC_STRLEN: [&str; 2] = ["libc", "strlen"];
 +#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))]
 +pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 +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 MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 +pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
 +/// 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(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
 +pub(super) const PANICKING_PANIC_FMT: [&str; 3] = ["core", "panicking", "panic_fmt"];
 +pub(super) const PANICKING_PANIC_STR: [&str; 3] = ["core", "panicking", "panic_str"];
 +pub(super) const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
 +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"];
 +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"];
 +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 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 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-lints")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal-lints")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal-lints")]
 +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"];
 +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"];
++#[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"];
index 6ebe1a0028a315fff24a116237b52c245cf0ac5f,0000000000000000000000000000000000000000..ca64ac7de3eea4b98fd9f609a82d153008dbc9df
mode 100644,000000..100644
--- /dev/null
@@@ -1,369 -1,0 +1,379 @@@
 +//! 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_id::DefId;
 +use rustc_hir::{TyKind, Unsafety};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
 +use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
 +use rustc_span::sym;
 +use rustc_span::symbol::{Ident, Symbol};
 +use rustc_span::DUMMY_SP;
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::AtExt;
 +
 +use crate::{match_def_path, must_use_attr};
 +
 +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(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if has_drop(cx, ty) || is_copy(cx, ty) {
 +        return false;
 +    }
 +    match ty.kind() {
 +        ty::Param(_) => false,
 +        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
 +        _ => true,
 +    }
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
 +pub fn contains_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
 +    ty.walk(tcx).any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 +/// constructor.
 +pub fn contains_adt_constructor<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, adt: &'tcx AdtDef) -> bool {
 +    ty.walk(tcx).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| {
 +            cx.tcx.associated_items(iter_did).find_by_name_and_kind(
 +                cx.tcx,
 +                Ident::from_str("Item"),
 +                ty::AssocKind::Type,
 +                iter_did,
 +            )
 +        })
 +        .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 also [`get_trait_def_id`](super::get_trait_def_id).
 +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.types().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(cx.tcx).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.types().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
 +///
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +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(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.types().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
++        ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did),
++        _ => false,
++    }
++}
index ff2e0417435bfe72edb635fd725cb476cf47a335,0000000000000000000000000000000000000000..57a90a924ec3cf9fd0a58ab6d3d969a2b1b91ef8
mode 100644,000000..100644
--- /dev/null
@@@ -1,173 -1,0 +1,174 @@@
 +# Basics for hacking on Clippy
 +
 +This document explains the basics for hacking on Clippy. Besides others, this
 +includes how to build and test Clippy. For a more in depth description on
 +the codebase take a look at [Adding Lints] or [Common Tools].
 +
 +[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
 +[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
 +
 +- [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
 +  - [Get the Code](#get-the-code)
 +  - [Building and Testing](#building-and-testing)
 +  - [`cargo dev`](#cargo-dev)
 +  - [lintcheck](#lintcheck)
 +  - [PR](#pr)
 +  - [Common Abbreviations](#common-abbreviations)
 +  - [Install from source](#install-from-source)
 +
 +## Get the Code
 +
 +First, make sure you have checked out the latest version of Clippy. If this is
 +your first time working on Clippy, create a fork of the repository and clone it
 +afterwards with the following command:
 +
 +```bash
 +git clone git@github.com:<your-username>/rust-clippy
 +```
 +
 +If you've already cloned Clippy in the past, update it to the latest version:
 +
 +```bash
 +# If the upstream remote has not been added yet
 +git remote add upstream https://github.com/rust-lang/rust-clippy
 +# upstream has to be the remote of the rust-lang/rust-clippy repo
 +git fetch upstream
 +# make sure that you are on the master branch
 +git checkout master
 +# rebase your master branch on the upstream master
 +git rebase upstream/master
 +# push to the master branch of your fork
 +git push
 +```
 +
 +## Building and Testing
 +
 +You can build and test Clippy like every other Rust project:
 +
 +```bash
 +cargo build  # builds Clippy
 +cargo test   # tests Clippy
 +```
 +
 +Since Clippy's test suite is pretty big, there are some commands that only run a
 +subset of Clippy's tests:
 +
 +```bash
 +# only run UI tests
 +cargo uitest
 +# only run UI tests starting with `test_`
 +TESTNAME="test_" cargo uitest
 +# only run dogfood tests
 +cargo test --test dogfood
 +```
 +
 +If the output of a [UI test] differs from the expected output, you can update the
 +reference file with:
 +
 +```bash
 +cargo dev bless
 +```
 +
 +For example, this is necessary, if you fix a typo in an error message of a lint
 +or if you modify a test file to add a test case.
 +
 +_Note:_ This command may update more files than you intended. In that case only
 +commit the files you wanted to update.
 +
 +[UI test]: https://rustc-dev-guide.rust-lang.org/tests/adding.html#guide-to-the-ui-tests
 +
 +## `cargo dev`
 +
 +Clippy has some dev tools to make working on Clippy more convenient. These tools
 +can be accessed through the `cargo dev` command. Available tools are listed
 +below. To get more information about these commands, just call them with
 +`--help`.
 +
 +```bash
 +# formats the whole Clippy codebase and all tests
 +cargo dev fmt
 +# register or update lint names/groups/...
 +cargo dev update_lints
 +# create a new lint and register it
 +cargo dev new_lint
 +# automatically formatting all code before each commit
 +cargo dev setup git-hook
 +# (experimental) Setup Clippy to work with IntelliJ-Rust
 +cargo dev setup intellij
 +```
++More about intellij command usage and reasons [here](../CONTRIBUTING.md#intellij-rust)
 +
 +## lintcheck
 +`cargo lintcheck` will build and run clippy on a fixed set of crates and generate a log of the results.  
 +You can `git diff` the updated log against its previous version and
 +see what impact your lint made on a small set of crates.  
 +If you add a new lint, please audit the resulting warnings and make sure
 +there are no false positives and that the suggestions are valid.
 +
 +Refer to the tools [README] for more details.
 +
 +[README]: https://github.com/rust-lang/rust-clippy/blob/master/lintcheck/README.md
 +## PR
 +
 +We follow a rustc no merge-commit policy.
 +See <https://rustc-dev-guide.rust-lang.org/contributing.html#opening-a-pr>.
 +
 +## Common Abbreviations
 +
 +| Abbreviation | Meaning                                |
 +| ------------ | -------------------------------------- |
 +| UB           | Undefined Behavior                     |
 +| FP           | False Positive                         |
 +| FN           | False Negative                         |
 +| ICE          | Internal Compiler Error                |
 +| AST          | Abstract Syntax Tree                   |
 +| MIR          | Mid-Level Intermediate Representation  |
 +| HIR          | High-Level Intermediate Representation |
 +| TCX          | Type context                           |
 +
 +This is a concise list of abbreviations that can come up during Clippy development. An extensive
 +general list can be found in the [rustc-dev-guide glossary][glossary]. Always feel free to ask if
 +an abbreviation or meaning is unclear to you.
 +
 +## Install from source
 +
 +If you are hacking on Clippy and want to install it from source, do the following:
 +
 +First, take note of the toolchain [override](https://rust-lang.github.io/rustup/overrides.html) in `/rust-toolchain`.
 +We will use this override to install Clippy into the right toolchain.
 +
 +> Tip: You can view the active toolchain for the current directory with `rustup show active-toolchain`.
 +
 +From the Clippy project root, run the following command to build the Clippy binaries and copy them into the
 +toolchain directory. This will override the currently installed Clippy component.
 +
 +```terminal
 +cargo build --release --bin cargo-clippy --bin clippy-driver -Zunstable-options --out-dir "$(rustc --print=sysroot)/bin"
 +```
 +
 +Now you may run `cargo clippy` in any project, using the toolchain where you just installed Clippy.
 +
 +```terminal
 +cd my-project
 +cargo +nightly-2021-07-01 clippy
 +```
 +
 +...or `clippy-driver`
 +
 +```terminal
 +clippy-driver +nightly-2021-07-01 <filename>
 +```
 +
 +If you need to restore the default Clippy installation, run the following (from the Clippy project root).
 +
 +```terminal
 +rustup component remove clippy
 +rustup component add clippy
 +```
 +
 +> **DO NOT** install using `cargo install --path . --force` since this will overwrite rustup
 +> [proxies](https://rust-lang.github.io/rustup/concepts/proxies.html). That is, `~/.cargo/bin/cargo-clippy` and
 +> `~/.cargo/bin/clippy-driver` should be hard or soft links to `~/.cargo/bin/rustup`. You can repair these by running
 +> `rustup update`.
 +
 +[glossary]: https://rustc-dev-guide.rust-lang.org/appendix/glossary.html
index f98819303e6827c80e373954d725f0483de628d2,0000000000000000000000000000000000000000..67eaf286004f9515cabbfefe3d889ec4b9c74b3f
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2021-10-07"
 +[toolchain]
++channel = "nightly-2021-10-21"
 +components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index e8b1640c8693e5907cf93e491a713658b17e6a63,0000000000000000000000000000000000000000..c15835ef2995687327d9227c861b1decd8b3f85d
mode 100644,000000..100644
--- /dev/null
@@@ -1,342 -1,0 +1,356 @@@
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![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::path::{Path, PathBuf};
 +
 +mod cargo;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal-lints");
 +
 +/// All crates used in UI tests are listed here
 +static TEST_DEPENDENCIES: &[&str] = &[
 +    "clippy_utils",
 +    "derive_new",
 +    "if_chain",
 +    "itertools",
 +    "quote",
 +    "regex",
 +    "serde",
 +    "serde_derive",
 +    "syn",
 +];
 +
 +// 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 if_chain;
 +#[allow(unused_extern_crates)]
 +extern crate itertools;
 +#[allow(unused_extern_crates)]
 +extern crate quote;
 +#[allow(unused_extern_crates)]
 +extern crate syn;
 +
 +/// 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.
 +fn extern_flags() -> String {
 +    let current_exe_depinfo = {
 +        let mut path = env::current_exe().unwrap();
 +        path.set_extension("d");
 +        std::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 default_config() -> compiletest::Config {
 +    let mut config = compiletest::Config::default();
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(std::string::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 = std::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.build_base = profile_path.join("test");
 +    config.rustc_path = profile_path.join(if cfg!(windows) {
 +        "clippy-driver.exe"
 +    } else {
 +        "clippy-driver"
 +    });
 +    config
 +}
 +
 +fn run_ui(cfg: &mut compiletest::Config) {
 +    cfg.mode = TestMode::Ui;
 +    cfg.src_base = Path::new("tests").join("ui");
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
 +    compiletest::run_tests(cfg);
 +}
 +
++fn run_ui_test(cfg: &mut compiletest::Config) {
++    cfg.mode = TestMode::Ui;
++    cfg.src_base = Path::new("tests").join("ui_test");
++    let _g = VarGuard::set("CARGO_MANIFEST_DIR", std::fs::canonicalize("tests").unwrap());
++    let rustcflags = cfg.target_rustcflags.get_or_insert_with(Default::default);
++    let len = rustcflags.len();
++    rustcflags.push_str(" --test");
++    compiletest::run_tests(cfg);
++    if let Some(ref mut flags) = &mut cfg.target_rustcflags {
++        flags.truncate(len);
++    }
++}
++
 +fn run_internal_tests(cfg: &mut compiletest::Config) {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    cfg.mode = TestMode::Ui;
 +    cfg.src_base = Path::new("tests").join("ui-internal");
 +    compiletest::run_tests(cfg);
 +}
 +
 +fn run_ui_toml(config: &mut compiletest::Config) {
 +    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)
 +    }
 +
 +    config.mode = TestMode::Ui;
 +    config.src_base = Path::new("tests").join("ui-toml").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(config: &mut compiletest::Config) {
 +    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 cargo::is_rustc_test_suite() {
 +        return;
 +    }
 +
 +    config.mode = TestMode::Ui;
 +    config.src_base = Path::new("tests").join("ui-cargo").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);
 +        },
 +    }
 +}
 +
 +fn prepare_env() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    set_var("__CLIPPY_INTERNAL_TESTS", "true");
 +    //set_var("RUST_BACKTRACE", "0");
 +}
 +
 +#[test]
 +fn compile_test() {
 +    prepare_env();
 +    let mut config = default_config();
 +    run_ui(&mut config);
++    run_ui_test(&mut config);
 +    run_ui_toml(&mut config);
 +    run_ui_cargo(&mut config);
 +    run_internal_tests(&mut config);
 +}
 +
 +/// 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 8e04d447fbcaaa3de92c81f26ff4cc450c5f39cd,0000000000000000000000000000000000000000..12e05eaa7a09ab1070681f50275dbffff46e065d
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,39 @@@
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 +error: unnecessary `Symbol` to string conversion
 +  --> $DIR/unnecessary_symbol_str.rs:11:5
 +   |
 +LL |     Symbol::intern("foo").as_str() == "clippy";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
 +   |
 +note: the lint level is defined here
 +  --> $DIR/unnecessary_symbol_str.rs:3:9
 +   |
 +LL | #![deny(clippy::internal)]
 +   |         ^^^^^^^^^^^^^^^^
 +   = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
 +
 +error: unnecessary `Symbol` to string conversion
 +  --> $DIR/unnecessary_symbol_str.rs:12:5
 +   |
 +LL |     Symbol::intern("foo").to_string() == "self";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
 +
 +error: unnecessary `Symbol` to string conversion
 +  --> $DIR/unnecessary_symbol_str.rs:13:5
 +   |
 +LL |     Symbol::intern("foo").to_ident_string() != "Self";
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
 +
 +error: unnecessary `Symbol` to string conversion
 +  --> $DIR/unnecessary_symbol_str.rs:14:5
 +   |
 +LL |     &*Ident::empty().as_str() == "clippy";
-    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 +
 +error: unnecessary `Symbol` to string conversion
 +  --> $DIR/unnecessary_symbol_str.rs:15:5
 +   |
 +LL |     "clippy" == Ident::empty().to_string();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
 +
 +error: aborting due to 5 previous errors
++
index dac4446703b0f5e24977580aa667815b866b286f,0000000000000000000000000000000000000000..6cb9e2ef95467be59c0ba74c9dc8d25de2411c09
mode 100644,000000..100644
--- /dev/null
@@@ -1,11 -1,0 +1,15 @@@
-     "bool"
 +disallowed-types = [
 +    "std::collections::HashMap",
 +    "std::sync::atomic::AtomicU32",
 +    "syn::TypePath",
 +    "proc_macro2::Ident",
 +    "std::thread::Thread",
 +    "std::time::Instant",
 +    "std::io::Read",
 +    "std::primitive::usize",
++    "bool",
++    # can give path and reason with an inline table
++    { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
++    # can use an inline table but omit reason
++    { path = "std::net::TcpListener" },
 +]
index 0871a3073abd302c4f86b342f01b6203af164fae,0000000000000000000000000000000000000000..410f076505511c6d84c4eae6224ae4625517d516
mode 100644,000000..100644
--- /dev/null
@@@ -1,38 -1,0 +1,42 @@@
 +#![warn(clippy::disallowed_type)]
 +
 +extern crate quote;
 +extern crate syn;
 +
 +use std::sync as foo;
 +use std::sync::atomic::AtomicU32;
 +use std::time::Instant as Sneaky;
 +
 +struct HashMap;
 +
 +fn bad_return_type() -> fn() -> Sneaky {
 +    todo!()
 +}
 +
 +fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
 +
 +fn trait_obj(_: &dyn std::io::Read) {}
 +
 +fn full_and_single_path_prim(_: usize, _: bool) {}
 +
 +fn const_generics<const C: usize>() {}
 +
 +struct GenArg<const U: usize>([u8; U]);
 +
 +static BAD: foo::atomic::AtomicPtr<()> = foo::atomic::AtomicPtr::new(std::ptr::null_mut());
 +
++fn ip(_: std::net::Ipv4Addr) {}
++
++fn listener(_: std::net::TcpListener) {}
++
 +#[allow(clippy::diverging_sub_expression)]
 +fn main() {
 +    let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
 +    let _ = Sneaky::now();
 +    let _ = foo::atomic::AtomicU32::new(0);
 +    static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
 +    let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
 +    let _ = syn::Ident::new("", todo!());
 +    let _ = HashMap;
 +    let _: usize = 64_usize;
 +}
index 90ce7db2cc4e6b4007fe5d439784b09a5abba4e9,0000000000000000000000000000000000000000..08a400a83675b916a59ca597472bdd6a7c6cb524
mode 100644,000000..100644
--- /dev/null
@@@ -1,118 -1,0 +1,132 @@@
-   --> $DIR/conf_disallowed_type.rs:30:48
 +error: `std::sync::atomic::AtomicU32` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:7:1
 +   |
 +LL | use std::sync::atomic::AtomicU32;
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::disallowed-type` implied by `-D warnings`
 +
 +error: `std::time::Instant` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:8:1
 +   |
 +LL | use std::time::Instant as Sneaky;
 +   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `std::time::Instant` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:12:33
 +   |
 +LL | fn bad_return_type() -> fn() -> Sneaky {
 +   |                                 ^^^^^^
 +
 +error: `std::time::Instant` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:16:28
 +   |
 +LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
 +   |                            ^^^^^^
 +
 +error: `std::sync::atomic::AtomicU32` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:16:39
 +   |
 +LL | fn bad_arg_type(_: impl Fn(Sneaky) -> foo::atomic::AtomicU32) {}
 +   |                                       ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `std::io::Read` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:18:22
 +   |
 +LL | fn trait_obj(_: &dyn std::io::Read) {}
 +   |                      ^^^^^^^^^^^^^
 +
 +error: `usize` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:20:33
 +   |
 +LL | fn full_and_single_path_prim(_: usize, _: bool) {}
 +   |                                 ^^^^^
 +
 +error: `bool` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:20:43
 +   |
 +LL | fn full_and_single_path_prim(_: usize, _: bool) {}
 +   |                                           ^^^^
 +
 +error: `usize` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:22:28
 +   |
 +LL | fn const_generics<const C: usize>() {}
 +   |                            ^^^^^
 +
 +error: `usize` is not allowed according to config
 +  --> $DIR/conf_disallowed_type.rs:24:24
 +   |
 +LL | struct GenArg<const U: usize>([u8; U]);
 +   |                        ^^^^^
 +
++error: `std::net::Ipv4Addr` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:28:10
++   |
++LL | fn ip(_: std::net::Ipv4Addr) {}
++   |          ^^^^^^^^^^^^^^^^^^
++   |
++   = note: no IPv4 allowed (from clippy.toml)
++
++error: `std::net::TcpListener` is not allowed according to config
++  --> $DIR/conf_disallowed_type.rs:30:16
++   |
++LL | fn listener(_: std::net::TcpListener) {}
++   |                ^^^^^^^^^^^^^^^^^^^^^
++
 +error: `std::collections::HashMap` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:30:12
++  --> $DIR/conf_disallowed_type.rs:34:48
 +   |
 +LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
 +   |                                                ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `std::collections::HashMap` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:31:13
++  --> $DIR/conf_disallowed_type.rs:34:12
 +   |
 +LL |     let _: std::collections::HashMap<(), ()> = std::collections::HashMap::new();
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `std::time::Instant` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:32:13
++  --> $DIR/conf_disallowed_type.rs:35:13
 +   |
 +LL |     let _ = Sneaky::now();
 +   |             ^^^^^^
 +
 +error: `std::sync::atomic::AtomicU32` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:33:17
++  --> $DIR/conf_disallowed_type.rs:36:13
 +   |
 +LL |     let _ = foo::atomic::AtomicU32::new(0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `std::sync::atomic::AtomicU32` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:33:48
++  --> $DIR/conf_disallowed_type.rs:37:17
 +   |
 +LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `std::sync::atomic::AtomicU32` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:34:43
++  --> $DIR/conf_disallowed_type.rs:37:48
 +   |
 +LL |     static FOO: std::sync::atomic::AtomicU32 = foo::atomic::AtomicU32::new(1);
 +   |                                                ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: `syn::TypePath` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:35:13
++  --> $DIR/conf_disallowed_type.rs:38:43
 +   |
 +LL |     let _: std::collections::BTreeMap<(), syn::TypePath> = Default::default();
 +   |                                           ^^^^^^^^^^^^^
 +
 +error: `syn::Ident` is not allowed according to config
-   --> $DIR/conf_disallowed_type.rs:37:12
++  --> $DIR/conf_disallowed_type.rs:39:13
 +   |
 +LL |     let _ = syn::Ident::new("", todo!());
 +   |             ^^^^^^^^^^
 +
 +error: `usize` is not allowed according to config
- error: aborting due to 19 previous errors
++  --> $DIR/conf_disallowed_type.rs:41:12
 +   |
 +LL |     let _: usize = 64_usize;
 +   |            ^^^^^
 +
++error: aborting due to 21 previous errors
 +
index 170955e726cc573853764fbaaf16b187450bada7,0000000000000000000000000000000000000000..0251fada9e85a6ea8aa5dbfc954292835fab394c
mode 100644,000000..100644
--- /dev/null
@@@ -1,115 -1,0 +1,122 @@@
 +#![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;
++    };
++}
index 4e583a25b94c2a517555585caf76f5631e263847,0000000000000000000000000000000000000000..061a4ab9b2ef8da2809901e986d07b3b45ab64ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,31 @@@
- #![allow(unused, clippy::no_effect)]
 +// run-rustfix
 +#![feature(stmt_expr_attributes)]
 +
++#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::deprecated_cfg_attr)]
 +
 +// This doesn't get linted, see known problems
 +#![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +#[rustfmt::skip]
 +trait Foo
 +{
 +fn foo(
 +);
 +}
 +
 +fn skip_on_statements() {
 +    #[rustfmt::skip]
 +    5+3;
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    foo::f();
 +}
 +
 +mod foo {
 +    #![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +    pub fn f() {}
 +}
index 9c0fcf6fb454c4ac00a89b8bcb1b5b34aa58e2cf,0000000000000000000000000000000000000000..035169fab85befb570e7d06ebcc5ff518652109b
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,31 @@@
- #![allow(unused, clippy::no_effect)]
 +// run-rustfix
 +#![feature(stmt_expr_attributes)]
 +
++#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::deprecated_cfg_attr)]
 +
 +// This doesn't get linted, see known problems
 +#![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +#[rustfmt::skip]
 +trait Foo
 +{
 +fn foo(
 +);
 +}
 +
 +fn skip_on_statements() {
 +    #[cfg_attr(rustfmt, rustfmt::skip)]
 +    5+3;
 +}
 +
 +#[cfg_attr(rustfmt, rustfmt_skip)]
 +fn main() {
 +    foo::f();
 +}
 +
 +mod foo {
 +    #![cfg_attr(rustfmt, rustfmt_skip)]
 +
 +    pub fn f() {}
 +}
index 8f823f1672ba2d30e868414cdf87104a39caa175,0000000000000000000000000000000000000000..03bb30f9083ac4f3c21ab23a635747ef8ba128fd
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,127 @@@
 +// aux-build:doc_unsafe_macros.rs
 +
 +#[macro_use]
 +extern crate doc_unsafe_macros;
 +
 +/// This is not sufficiently documented
 +pub unsafe fn destroy_the_planet() {
 +    unimplemented!();
 +}
 +
 +/// This one is
 +///
 +/// # Safety
 +///
 +/// This function shouldn't be called unless the horsemen are ready
 +pub unsafe fn apocalypse(universe: &mut ()) {
 +    unimplemented!();
 +}
 +
 +/// This is a private function, so docs aren't necessary
 +unsafe fn you_dont_see_me() {
 +    unimplemented!();
 +}
 +
 +mod private_mod {
 +    pub unsafe fn only_crate_wide_accessible() {
 +        unimplemented!();
 +    }
 +
 +    pub unsafe fn republished() {
 +        unimplemented!();
 +    }
 +}
 +
 +pub use private_mod::republished;
 +
 +pub trait SafeTraitUnsafeMethods {
 +    unsafe fn woefully_underdocumented(self);
 +
 +    /// # Safety
 +    unsafe fn at_least_somewhat_documented(self);
 +}
 +
 +pub unsafe trait UnsafeTrait {
 +    fn method();
 +}
 +
 +/// # Safety
 +pub unsafe trait DocumentedUnsafeTrait {
 +    fn method2();
 +}
 +
 +pub struct Struct;
 +
 +impl SafeTraitUnsafeMethods for Struct {
 +    unsafe fn woefully_underdocumented(self) {
 +        // all is well
 +    }
 +
 +    unsafe fn at_least_somewhat_documented(self) {
 +        // all is still well
 +    }
 +}
 +
 +unsafe impl UnsafeTrait for Struct {
 +    fn method() {}
 +}
 +
 +unsafe impl DocumentedUnsafeTrait for Struct {
 +    fn method2() {}
 +}
 +
 +impl Struct {
 +    pub unsafe fn more_undocumented_unsafe() -> Self {
 +        unimplemented!();
 +    }
 +
 +    /// # Safety
 +    pub unsafe fn somewhat_documented(&self) {
 +        unimplemented!();
 +    }
 +
 +    unsafe fn private(&self) {
 +        unimplemented!();
 +    }
 +}
 +
 +macro_rules! very_unsafe {
 +    () => {
 +        pub unsafe fn whee() {
 +            unimplemented!()
 +        }
 +
 +        /// # Safety
 +        ///
 +        /// Please keep the seat belt fastened
 +        pub unsafe fn drive() {
 +            whee()
 +        }
 +    };
 +}
 +
 +very_unsafe!();
 +
 +// we don't lint code from external macros
 +undocd_unsafe!();
 +
 +fn main() {
 +    unsafe {
 +        you_dont_see_me();
 +        destroy_the_planet();
 +        let mut universe = ();
 +        apocalypse(&mut universe);
 +        private_mod::only_crate_wide_accessible();
 +        drive();
 +    }
 +}
++
++// do not lint if any parent has `#[doc(hidden)]` attribute
++// see #7347
++#[doc(hidden)]
++pub mod __macro {
++    pub struct T;
++    impl T {
++        pub unsafe fn f() {}
++    }
++}
index ba72cc237b4a58e1b0c51f5470f88273e6f87292,0000000000000000000000000000000000000000..88918d9671e42f6d90037eae22fc8985ab5de651
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,78 @@@
 +// run-rustfix
 +
 +#![allow(unused_variables, dead_code)]
 +#![warn(clippy::equatable_if_let)]
 +
 +use std::cmp::Ordering;
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    TupleVariant(i32, u64),
 +    RecordVariant { a: i64, b: u32 },
 +    UnitVariant,
 +    Recursive(Struct),
 +}
 +
 +#[derive(PartialEq)]
 +struct Struct {
 +    a: i32,
 +    b: bool,
 +}
 +
 +enum NotPartialEq {
 +    A,
 +    B,
 +}
 +
 +enum NotStructuralEq {
 +    A,
 +    B,
 +}
 +
 +impl PartialEq for NotStructuralEq {
 +    fn eq(&self, _: &NotStructuralEq) -> bool {
 +        false
 +    }
 +}
 +
 +fn main() {
 +    let a = 2;
 +    let b = 3;
 +    let c = Some(2);
 +    let d = Struct { a: 2, b: false };
 +    let e = Enum::UnitVariant;
 +    let f = NotPartialEq::A;
 +    let g = NotStructuralEq::A;
 +
 +    // true
 +
 +    if a == 2 {}
 +    if a.cmp(&b) == Ordering::Greater {}
 +    if c == Some(2) {}
 +    if d == (Struct { a: 2, b: false }) {}
 +    if e == Enum::TupleVariant(32, 64) {}
 +    if e == (Enum::RecordVariant { a: 64, b: 32 }) {}
 +    if e == Enum::UnitVariant {}
 +    if (e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false }) {}
 +
 +    // false
 +
 +    if let 2 | 3 = a {}
 +    if let x @ 2 = a {}
 +    if let Some(3 | 4) = c {}
 +    if let Struct { a, b: false } = d {}
 +    if let Struct { a: 2, b: x } = d {}
 +    if let NotPartialEq::A = f {}
 +    if g == NotStructuralEq::A {}
 +    if let Some(NotPartialEq::A) = Some(f) {}
 +    if Some(g) == Some(NotStructuralEq::A) {}
++
++    macro_rules! m1 {
++        (x) => {
++            "abc"
++        };
++    }
++    if "abc" == m1!(x) {
++        println!("OK");
++    }
 +}
index 12526ca193db6b6fd6b1ca3244070a7faa5c9a20,0000000000000000000000000000000000000000..9a7ab75ef450f528f5ff1543f3bdd361c1881f67
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,78 @@@
 +// run-rustfix
 +
 +#![allow(unused_variables, dead_code)]
 +#![warn(clippy::equatable_if_let)]
 +
 +use std::cmp::Ordering;
 +
 +#[derive(PartialEq)]
 +enum Enum {
 +    TupleVariant(i32, u64),
 +    RecordVariant { a: i64, b: u32 },
 +    UnitVariant,
 +    Recursive(Struct),
 +}
 +
 +#[derive(PartialEq)]
 +struct Struct {
 +    a: i32,
 +    b: bool,
 +}
 +
 +enum NotPartialEq {
 +    A,
 +    B,
 +}
 +
 +enum NotStructuralEq {
 +    A,
 +    B,
 +}
 +
 +impl PartialEq for NotStructuralEq {
 +    fn eq(&self, _: &NotStructuralEq) -> bool {
 +        false
 +    }
 +}
 +
 +fn main() {
 +    let a = 2;
 +    let b = 3;
 +    let c = Some(2);
 +    let d = Struct { a: 2, b: false };
 +    let e = Enum::UnitVariant;
 +    let f = NotPartialEq::A;
 +    let g = NotStructuralEq::A;
 +
 +    // true
 +
 +    if let 2 = a {}
 +    if let Ordering::Greater = a.cmp(&b) {}
 +    if let Some(2) = c {}
 +    if let Struct { a: 2, b: false } = d {}
 +    if let Enum::TupleVariant(32, 64) = e {}
 +    if let Enum::RecordVariant { a: 64, b: 32 } = e {}
 +    if let Enum::UnitVariant = e {}
 +    if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
 +
 +    // false
 +
 +    if let 2 | 3 = a {}
 +    if let x @ 2 = a {}
 +    if let Some(3 | 4) = c {}
 +    if let Struct { a, b: false } = d {}
 +    if let Struct { a: 2, b: x } = d {}
 +    if let NotPartialEq::A = f {}
 +    if let NotStructuralEq::A = g {}
 +    if let Some(NotPartialEq::A) = Some(f) {}
 +    if let Some(NotStructuralEq::A) = Some(g) {}
++
++    macro_rules! m1 {
++        (x) => {
++            "abc"
++        };
++    }
++    if let m1!(x) = "abc" {
++        println!("OK");
++    }
 +}
index 79ef919384df28abc1032e096565ade0147f3d79,0000000000000000000000000000000000000000..760ff88f448f094561f008b0e29be43824306260
mode 100644,000000..100644
--- /dev/null
@@@ -1,64 -1,0 +1,70 @@@
- error: aborting due to 10 previous errors
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:49:8
 +   |
 +LL |     if let 2 = a {}
 +   |        ^^^^^^^^^ help: try: `a == 2`
 +   |
 +   = note: `-D clippy::equatable-if-let` implied by `-D warnings`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:50:8
 +   |
 +LL |     if let Ordering::Greater = a.cmp(&b) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:51:8
 +   |
 +LL |     if let Some(2) = c {}
 +   |        ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:52:8
 +   |
 +LL |     if let Struct { a: 2, b: false } = d {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:53:8
 +   |
 +LL |     if let Enum::TupleVariant(32, 64) = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:54:8
 +   |
 +LL |     if let Enum::RecordVariant { a: 64, b: 32 } = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:55:8
 +   |
 +LL |     if let Enum::UnitVariant = e {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:56:8
 +   |
 +LL |     if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:66:8
 +   |
 +LL |     if let NotStructuralEq::A = g {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
 +
 +error: this pattern matching can be expressed using equality
 +  --> $DIR/equatable_if_let.rs:68:8
 +   |
 +LL |     if let Some(NotStructuralEq::A) = Some(g) {}
 +   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
 +
++error: this pattern matching can be expressed using equality
++  --> $DIR/equatable_if_let.rs:75:8
++   |
++LL |     if let m1!(x) = "abc" {
++   |        ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
++
++error: aborting due to 11 previous errors
 +
index a756d1cf50659473e1c43bbaca97ed675b9c4755,0000000000000000000000000000000000000000..cf923a6a5940c3574216eca71be849f9cd89d6e5
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,95 @@@
 +// run-rustfix
 +
 +#![warn(clippy::expect_fun_call)]
++#![allow(clippy::to_string_in_format_args)]
 +
 +/// Checks implementation of the `EXPECT_FUN_CALL` lint
 +
 +fn main() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Foo
 +        }
 +
 +        fn expect(&self, msg: &str) {
 +            panic!("{}", msg)
 +        }
 +    }
 +
 +    let with_some = Some("value");
 +    with_some.expect("error");
 +
 +    let with_none: Option<i32> = None;
 +    with_none.expect("error");
 +
 +    let error_code = 123_i32;
 +    let with_none_and_format: Option<i32> = None;
 +    with_none_and_format.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
 +
 +    let with_none_and_as_str: Option<i32> = None;
 +    with_none_and_as_str.unwrap_or_else(|| panic!("Error {}: fake error", error_code));
 +
 +    let with_ok: Result<(), ()> = Ok(());
 +    with_ok.expect("error");
 +
 +    let with_err: Result<(), ()> = Err(());
 +    with_err.expect("error");
 +
 +    let error_code = 123_i32;
 +    let with_err_and_format: Result<(), ()> = Err(());
 +    with_err_and_format.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
 +
 +    let with_err_and_as_str: Result<(), ()> = Err(());
 +    with_err_and_as_str.unwrap_or_else(|_| panic!("Error {}: fake error", error_code));
 +
 +    let with_dummy_type = Foo::new();
 +    with_dummy_type.expect("another test string");
 +
 +    let with_dummy_type_and_format = Foo::new();
 +    with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
 +
 +    let with_dummy_type_and_as_str = Foo::new();
 +    with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
 +
 +    //Issue #2937
 +    Some("foo").unwrap_or_else(|| panic!("{} {}", 1, 2));
 +
 +    //Issue #2979 - this should not lint
 +    {
 +        let msg = "bar";
 +        Some("foo").expect(msg);
 +    }
 +
 +    {
 +        fn get_string() -> String {
 +            "foo".to_string()
 +        }
 +
 +        fn get_static_str() -> &'static str {
 +            "foo"
 +        }
 +
 +        fn get_non_static_str(_: &u32) -> &str {
 +            "foo"
 +        }
 +
 +        Some("foo").unwrap_or_else(|| { panic!("{}", get_string()) });
 +        Some("foo").unwrap_or_else(|| { panic!("{}", get_string()) });
 +        Some("foo").unwrap_or_else(|| { panic!("{}", get_string()) });
 +
 +        Some("foo").unwrap_or_else(|| { panic!("{}", get_static_str()) });
 +        Some("foo").unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) });
 +    }
 +
 +    //Issue #3839
 +    Some(true).unwrap_or_else(|| panic!("key {}, {}", 1, 2));
 +
 +    //Issue #4912 - the receiver is a &Option
 +    {
 +        let opt = Some(1);
 +        let opt_ref = &opt;
 +        opt_ref.unwrap_or_else(|| panic!("{:?}", opt_ref));
 +    }
 +}
index 60bbaa89d42825819edae4191b6d3a3cccaa35fe,0000000000000000000000000000000000000000..e6f252259df70427e2da39b9ca9a3c6172f51659
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,95 @@@
 +// run-rustfix
 +
 +#![warn(clippy::expect_fun_call)]
++#![allow(clippy::to_string_in_format_args)]
 +
 +/// Checks implementation of the `EXPECT_FUN_CALL` lint
 +
 +fn main() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Self {
 +            Foo
 +        }
 +
 +        fn expect(&self, msg: &str) {
 +            panic!("{}", msg)
 +        }
 +    }
 +
 +    let with_some = Some("value");
 +    with_some.expect("error");
 +
 +    let with_none: Option<i32> = None;
 +    with_none.expect("error");
 +
 +    let error_code = 123_i32;
 +    let with_none_and_format: Option<i32> = None;
 +    with_none_and_format.expect(&format!("Error {}: fake error", error_code));
 +
 +    let with_none_and_as_str: Option<i32> = None;
 +    with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
 +
 +    let with_ok: Result<(), ()> = Ok(());
 +    with_ok.expect("error");
 +
 +    let with_err: Result<(), ()> = Err(());
 +    with_err.expect("error");
 +
 +    let error_code = 123_i32;
 +    let with_err_and_format: Result<(), ()> = Err(());
 +    with_err_and_format.expect(&format!("Error {}: fake error", error_code));
 +
 +    let with_err_and_as_str: Result<(), ()> = Err(());
 +    with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
 +
 +    let with_dummy_type = Foo::new();
 +    with_dummy_type.expect("another test string");
 +
 +    let with_dummy_type_and_format = Foo::new();
 +    with_dummy_type_and_format.expect(&format!("Error {}: fake error", error_code));
 +
 +    let with_dummy_type_and_as_str = Foo::new();
 +    with_dummy_type_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
 +
 +    //Issue #2937
 +    Some("foo").expect(format!("{} {}", 1, 2).as_ref());
 +
 +    //Issue #2979 - this should not lint
 +    {
 +        let msg = "bar";
 +        Some("foo").expect(msg);
 +    }
 +
 +    {
 +        fn get_string() -> String {
 +            "foo".to_string()
 +        }
 +
 +        fn get_static_str() -> &'static str {
 +            "foo"
 +        }
 +
 +        fn get_non_static_str(_: &u32) -> &str {
 +            "foo"
 +        }
 +
 +        Some("foo").expect(&get_string());
 +        Some("foo").expect(get_string().as_ref());
 +        Some("foo").expect(get_string().as_str());
 +
 +        Some("foo").expect(get_static_str());
 +        Some("foo").expect(get_non_static_str(&0));
 +    }
 +
 +    //Issue #3839
 +    Some(true).expect(&format!("key {}, {}", 1, 2));
 +
 +    //Issue #4912 - the receiver is a &Option
 +    {
 +        let opt = Some(1);
 +        let opt_ref = &opt;
 +        opt_ref.expect(&format!("{:?}", opt_ref));
 +    }
 +}
index 6dc796f5cee37d7e4effecde55e3775344710dc8,0000000000000000000000000000000000000000..ac48a06671cd2e0d572ecc36637c0237bc90426c
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-   --> $DIR/expect_fun_call.rs:28:26
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:31:26
++  --> $DIR/expect_fun_call.rs:29:26
 +   |
 +LL |     with_none_and_format.expect(&format!("Error {}: fake error", error_code));
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
 +   |
 +   = note: `-D clippy::expect-fun-call` implied by `-D warnings`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:41:25
++  --> $DIR/expect_fun_call.rs:32:26
 +   |
 +LL |     with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
 +   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:44:25
++  --> $DIR/expect_fun_call.rs:42:25
 +   |
 +LL |     with_err_and_format.expect(&format!("Error {}: fake error", error_code));
 +   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:56:17
++  --> $DIR/expect_fun_call.rs:45:25
 +   |
 +LL |     with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str());
 +   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:77:21
++  --> $DIR/expect_fun_call.rs:57:17
 +   |
 +LL |     Some("foo").expect(format!("{} {}", 1, 2).as_ref());
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{} {}", 1, 2))`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:78:21
++  --> $DIR/expect_fun_call.rs:78:21
 +   |
 +LL |         Some("foo").expect(&get_string());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:79:21
++  --> $DIR/expect_fun_call.rs:79:21
 +   |
 +LL |         Some("foo").expect(get_string().as_ref());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:81:21
++  --> $DIR/expect_fun_call.rs:80:21
 +   |
 +LL |         Some("foo").expect(get_string().as_str());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_string()) })`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:82:21
++  --> $DIR/expect_fun_call.rs:82:21
 +   |
 +LL |         Some("foo").expect(get_static_str());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_static_str()) })`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:86:16
++  --> $DIR/expect_fun_call.rs:83:21
 +   |
 +LL |         Some("foo").expect(get_non_static_str(&0));
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })`
 +
 +error: use of `expect` followed by a function call
-   --> $DIR/expect_fun_call.rs:92:17
++  --> $DIR/expect_fun_call.rs:87:16
 +   |
 +LL |     Some(true).expect(&format!("key {}, {}", 1, 2));
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))`
 +
 +error: use of `expect` followed by a function call
++  --> $DIR/expect_fun_call.rs:93:17
 +   |
 +LL |         opt_ref.expect(&format!("{:?}", opt_ref));
 +   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| panic!("{:?}", opt_ref))`
 +
 +error: aborting due to 12 previous errors
 +
index 787053fb00064c861a75c37410ae26767ab2322e,0000000000000000000000000000000000000000..7367910eaa126fdc54319aa3d7232765dcca376d
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,249 @@@
 +// aux-build:proc_macro_derive.rs
 +// aux-build:macro_rules.rs
 +
 +#![warn(clippy::field_reassign_with_default)]
 +
 +#[macro_use]
 +extern crate proc_macro_derive;
 +#[macro_use]
 +extern crate macro_rules;
 +
 +// Don't lint on derives that derive `Default`
 +// See https://github.com/rust-lang/rust-clippy/issues/6545
 +#[derive(FieldReassignWithDefault)]
 +struct DerivedStruct;
 +
 +#[derive(Default)]
 +struct A {
 +    i: i32,
 +    j: i64,
 +}
 +
 +struct B {
 +    i: i32,
 +    j: i64,
 +}
 +
 +#[derive(Default)]
 +struct C {
 +    i: Vec<i32>,
 +    j: i64,
 +}
 +
 +#[derive(Default)]
 +struct D {
 +    a: Option<i32>,
 +    b: Option<i32>,
 +}
 +
 +macro_rules! m {
 +    ($key:ident: $value:tt) => {{
 +        let mut data = $crate::D::default();
 +        data.$key = Some($value);
 +        data
 +    }};
 +}
 +
 +/// Implements .next() that returns a different number each time.
 +struct SideEffect(i32);
 +
 +impl SideEffect {
 +    fn new() -> SideEffect {
 +        SideEffect(0)
 +    }
 +    fn next(&mut self) -> i32 {
 +        self.0 += 1;
 +        self.0
 +    }
 +}
 +
 +fn main() {
 +    // wrong, produces first error in stderr
 +    let mut a: A = Default::default();
 +    a.i = 42;
 +
 +    // right
 +    let mut a: A = Default::default();
 +
 +    // right
 +    let a = A {
 +        i: 42,
 +        ..Default::default()
 +    };
 +
 +    // right
 +    let mut a: A = Default::default();
 +    if a.i == 0 {
 +        a.j = 12;
 +    }
 +
 +    // right
 +    let mut a: A = Default::default();
 +    let b = 5;
 +
 +    // right
 +    let mut b = 32;
 +    let mut a: A = Default::default();
 +    b = 2;
 +
 +    // right
 +    let b: B = B { i: 42, j: 24 };
 +
 +    // right
 +    let mut b: B = B { i: 42, j: 24 };
 +    b.i = 52;
 +
 +    // right
 +    let mut b = B { i: 15, j: 16 };
 +    let mut a: A = Default::default();
 +    b.i = 2;
 +
 +    // wrong, produces second error in stderr
 +    let mut a: A = Default::default();
 +    a.j = 43;
 +    a.i = 42;
 +
 +    // wrong, produces third error in stderr
 +    let mut a: A = Default::default();
 +    a.i = 42;
 +    a.j = 43;
 +    a.j = 44;
 +
 +    // wrong, produces fourth error in stderr
 +    let mut a = A::default();
 +    a.i = 42;
 +
 +    // wrong, but does not produce an error in stderr, because we can't produce a correct kind of
 +    // suggestion with current implementation
 +    let mut c: (i32, i32) = Default::default();
 +    c.0 = 42;
 +    c.1 = 21;
 +
 +    // wrong, produces the fifth error in stderr
 +    let mut a: A = Default::default();
 +    a.i = Default::default();
 +
 +    // wrong, produces the sixth error in stderr
 +    let mut a: A = Default::default();
 +    a.i = Default::default();
 +    a.j = 45;
 +
 +    // right, because an assignment refers to another field
 +    let mut x = A::default();
 +    x.i = 42;
 +    x.j = 21 + x.i as i64;
 +
 +    // right, we bail out if there's a reassignment to the same variable, since there is a risk of
 +    // side-effects affecting the outcome
 +    let mut x = A::default();
 +    let mut side_effect = SideEffect::new();
 +    x.i = side_effect.next();
 +    x.j = 2;
 +    x.i = side_effect.next();
 +
 +    // don't lint - some private fields
 +    let mut x = m::F::default();
 +    x.a = 1;
 +
 +    // don't expand macros in the suggestion (#6522)
 +    let mut a: C = C::default();
 +    a.i = vec![1];
 +
 +    // Don't lint in external macros
 +    field_reassign_with_default!();
 +
 +    // be sure suggestion is correct with generics
 +    let mut a: Wrapper<bool> = Default::default();
 +    a.i = true;
 +
 +    let mut a: WrapperMulti<i32, i64> = Default::default();
 +    a.i = 42;
 +
 +    // Don't lint in macros
 +    m! {
 +        a: 42
 +    };
 +}
 +
 +mod m {
 +    #[derive(Default)]
 +    pub struct F {
 +        pub a: u64,
 +        b: u64,
 +    }
 +}
 +
 +#[derive(Default)]
 +struct Wrapper<T> {
 +    i: T,
 +}
 +
 +#[derive(Default)]
 +struct WrapperMulti<T, U> {
 +    i: T,
 +    j: U,
 +}
++
++mod issue6312 {
++    use std::sync::atomic::AtomicBool;
++    use std::sync::Arc;
++
++    // do not lint: type implements `Drop` but not all fields are `Copy`
++    #[derive(Clone, Default)]
++    pub struct ImplDropNotAllCopy {
++        name: String,
++        delay_data_sync: Arc<AtomicBool>,
++    }
++
++    impl Drop for ImplDropNotAllCopy {
++        fn drop(&mut self) {
++            self.close()
++        }
++    }
++
++    impl ImplDropNotAllCopy {
++        fn new(name: &str) -> Self {
++            let mut f = ImplDropNotAllCopy::default();
++            f.name = name.to_owned();
++            f
++        }
++        fn close(&self) {}
++    }
++
++    // lint: type implements `Drop` and all fields are `Copy`
++    #[derive(Clone, Default)]
++    pub struct ImplDropAllCopy {
++        name: usize,
++        delay_data_sync: bool,
++    }
++
++    impl Drop for ImplDropAllCopy {
++        fn drop(&mut self) {
++            self.close()
++        }
++    }
++
++    impl ImplDropAllCopy {
++        fn new(name: &str) -> Self {
++            let mut f = ImplDropAllCopy::default();
++            f.name = name.len();
++            f
++        }
++        fn close(&self) {}
++    }
++
++    // lint: type does not implement `Drop` though all fields are `Copy`
++    #[derive(Clone, Default)]
++    pub struct NoDropAllCopy {
++        name: usize,
++        delay_data_sync: bool,
++    }
++
++    impl NoDropAllCopy {
++        fn new(name: &str) -> Self {
++            let mut f = NoDropAllCopy::default();
++            f.name = name.len();
++            f
++        }
++    }
++}
index b56db08ec8a787f63c3edc31fde93178a0237982,0000000000000000000000000000000000000000..3ce4b91a54869d13c4b72ce7235cc51981a6169e
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,135 @@@
- error: aborting due to 9 previous errors
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:63:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +   = note: `-D clippy::field-reassign-with-default` implied by `-D warnings`
 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:62:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:103:5
 +   |
 +LL |     a.j = 43;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { j: 43, i: 42 }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:102:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:108:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: 42, j: 44 }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:107:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:114:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: 42, ..Default::default() }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:113:5
 +   |
 +LL |     let mut a = A::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:124:5
 +   |
 +LL |     a.i = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: Default::default(), ..Default::default() }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:123:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:128:5
 +   |
 +LL |     a.i = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `main::A { i: Default::default(), j: 45 }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:127:5
 +   |
 +LL |     let mut a: A = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:150:5
 +   |
 +LL |     a.i = vec![1];
 +   |     ^^^^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `C { i: vec![1], ..Default::default() }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:149:5
 +   |
 +LL |     let mut a: C = C::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:157:5
 +   |
 +LL |     a.i = true;
 +   |     ^^^^^^^^^^^
 +   |
 +note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:156:5
 +   |
 +LL |     let mut a: Wrapper<bool> = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: field assignment outside of initializer for an instance created with Default::default()
 +  --> $DIR/field_reassign_with_default.rs:160:5
 +   |
 +LL |     a.i = 42;
 +   |     ^^^^^^^^^
 +   |
 +note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
 +  --> $DIR/field_reassign_with_default.rs:159:5
 +   |
 +LL |     let mut a: WrapperMulti<i32, i64> = Default::default();
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: field assignment outside of initializer for an instance created with Default::default()
++  --> $DIR/field_reassign_with_default.rs:229:13
++   |
++LL |             f.name = name.len();
++   |             ^^^^^^^^^^^^^^^^^^^^
++   |
++note: consider initializing the variable with `issue6312::ImplDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
++  --> $DIR/field_reassign_with_default.rs:228:13
++   |
++LL |             let mut f = ImplDropAllCopy::default();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: field assignment outside of initializer for an instance created with Default::default()
++  --> $DIR/field_reassign_with_default.rs:245:13
++   |
++LL |             f.name = name.len();
++   |             ^^^^^^^^^^^^^^^^^^^^
++   |
++note: consider initializing the variable with `issue6312::NoDropAllCopy { name: name.len(), ..Default::default() }` and removing relevant reassignments
++  --> $DIR/field_reassign_with_default.rs:244:13
++   |
++LL |             let mut f = NoDropAllCopy::default();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 11 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..46704683926b2c8bae4ea25c538af783f1d9cc6d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++#![warn(clippy::fn_to_numeric_cast_any)]
++#![allow(clippy::fn_to_numeric_cast, clippy::fn_to_numeric_cast_with_truncation)]
++
++fn foo() -> u8 {
++    0
++}
++
++fn generic_foo<T>(x: T) -> T {
++    x
++}
++
++trait Trait {
++    fn static_method() -> u32 {
++        2
++    }
++}
++
++struct Struct;
++
++impl Trait for Struct {}
++
++fn fn_pointer_to_integer() {
++    let _ = foo as i8;
++    let _ = foo as i16;
++    let _ = foo as i32;
++    let _ = foo as i64;
++    let _ = foo as i128;
++    let _ = foo as isize;
++
++    let _ = foo as u8;
++    let _ = foo as u16;
++    let _ = foo as u32;
++    let _ = foo as u64;
++    let _ = foo as u128;
++    let _ = foo as usize;
++}
++
++fn static_method_to_integer() {
++    let _ = Struct::static_method as usize;
++}
++
++fn fn_with_fn_arg(f: fn(i32) -> u32) -> usize {
++    f as usize
++}
++
++fn fn_with_generic_static_trait_method<T: Trait>() -> usize {
++    T::static_method as usize
++}
++
++fn closure_to_fn_to_integer() {
++    let clos = |x| x * 2_u32;
++
++    let _ = (clos as fn(u32) -> u32) as usize;
++}
++
++fn fn_to_raw_ptr() {
++    let _ = foo as *const ();
++}
++
++fn cast_fn_to_self() {
++    // Casting to the same function pointer type should be permitted.
++    let _ = foo as fn() -> u8;
++}
++
++fn cast_generic_to_concrete() {
++    // Casting to a more concrete function pointer type should be permitted.
++    let _ = generic_foo as fn(usize) -> usize;
++}
++
++fn cast_closure_to_fn() {
++    // Casting a closure to a function pointer should be permitted.
++    let id = |x| x;
++    let _ = id as fn(usize) -> usize;
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a6c4a77672f86116229fec67ec2d58c97b36689a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++error: casting function pointer `foo` to `i8`
++  --> $DIR/fn_to_numeric_cast_any.rs:23:13
++   |
++LL |     let _ = foo as i8;
++   |             ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8`
++   |
++   = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings`
++
++error: casting function pointer `foo` to `i16`
++  --> $DIR/fn_to_numeric_cast_any.rs:24:13
++   |
++LL |     let _ = foo as i16;
++   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16`
++
++error: casting function pointer `foo` to `i32`
++  --> $DIR/fn_to_numeric_cast_any.rs:25:13
++   |
++LL |     let _ = foo as i32;
++   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32`
++
++error: casting function pointer `foo` to `i64`
++  --> $DIR/fn_to_numeric_cast_any.rs:26:13
++   |
++LL |     let _ = foo as i64;
++   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64`
++
++error: casting function pointer `foo` to `i128`
++  --> $DIR/fn_to_numeric_cast_any.rs:27:13
++   |
++LL |     let _ = foo as i128;
++   |             ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128`
++
++error: casting function pointer `foo` to `isize`
++  --> $DIR/fn_to_numeric_cast_any.rs:28:13
++   |
++LL |     let _ = foo as isize;
++   |             ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize`
++
++error: casting function pointer `foo` to `u8`
++  --> $DIR/fn_to_numeric_cast_any.rs:30:13
++   |
++LL |     let _ = foo as u8;
++   |             ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8`
++
++error: casting function pointer `foo` to `u16`
++  --> $DIR/fn_to_numeric_cast_any.rs:31:13
++   |
++LL |     let _ = foo as u16;
++   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16`
++
++error: casting function pointer `foo` to `u32`
++  --> $DIR/fn_to_numeric_cast_any.rs:32:13
++   |
++LL |     let _ = foo as u32;
++   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32`
++
++error: casting function pointer `foo` to `u64`
++  --> $DIR/fn_to_numeric_cast_any.rs:33:13
++   |
++LL |     let _ = foo as u64;
++   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64`
++
++error: casting function pointer `foo` to `u128`
++  --> $DIR/fn_to_numeric_cast_any.rs:34:13
++   |
++LL |     let _ = foo as u128;
++   |             ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128`
++
++error: casting function pointer `foo` to `usize`
++  --> $DIR/fn_to_numeric_cast_any.rs:35:13
++   |
++LL |     let _ = foo as usize;
++   |             ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize`
++
++error: casting function pointer `Struct::static_method` to `usize`
++  --> $DIR/fn_to_numeric_cast_any.rs:39:13
++   |
++LL |     let _ = Struct::static_method as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize`
++
++error: casting function pointer `f` to `usize`
++  --> $DIR/fn_to_numeric_cast_any.rs:43:5
++   |
++LL |     f as usize
++   |     ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize`
++
++error: casting function pointer `T::static_method` to `usize`
++  --> $DIR/fn_to_numeric_cast_any.rs:47:5
++   |
++LL |     T::static_method as usize
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize`
++
++error: casting function pointer `(clos as fn(u32) -> u32)` to `usize`
++  --> $DIR/fn_to_numeric_cast_any.rs:53:13
++   |
++LL |     let _ = (clos as fn(u32) -> u32) as usize;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize`
++
++error: casting function pointer `foo` to `*const ()`
++  --> $DIR/fn_to_numeric_cast_any.rs:57:13
++   |
++LL |     let _ = foo as *const ();
++   |             ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()`
++
++error: aborting due to 17 previous errors
++
index 5dd64140e81165880f3d6285c60825838d96a0e2,0000000000000000000000000000000000000000..73fc750511c78684cdd8650d03e6ce1dc40582b1
mode 100644,000000..100644
--- /dev/null
@@@ -1,74 -1,0 +1,74 @@@
- #![allow(clippy::print_literal, clippy::redundant_clone)]
 +// run-rustfix
 +
++#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)]
 +#![warn(clippy::useless_format)]
 +
 +struct Foo(pub String);
 +
 +macro_rules! foo {
 +    ($($t:tt)*) => (Foo(format!($($t)*)))
 +}
 +
 +fn main() {
 +    "foo".to_string();
 +    "{}".to_string();
 +    "{} abc {}".to_string();
 +    r##"foo {}
 +" bar"##.to_string();
 +
 +    "foo".to_string();
 +    format!("{:?}", "foo"); // Don't warn about `Debug`.
 +    format!("{:8}", "foo");
 +    format!("{:width$}", "foo", width = 8);
 +    "foo".to_string(); // Warn when the format makes no difference.
 +    "foo".to_string(); // Warn when the format makes no difference.
 +    format!("foo {}", "bar");
 +    format!("{} bar", "foo");
 +
 +    let arg: String = "".to_owned();
 +    arg.to_string();
 +    format!("{:?}", arg); // Don't warn about debug.
 +    format!("{:8}", arg);
 +    format!("{:width$}", arg, width = 8);
 +    arg.to_string(); // Warn when the format makes no difference.
 +    arg.to_string(); // Warn when the format makes no difference.
 +    format!("foo {}", arg);
 +    format!("{} bar", arg);
 +
 +    // We don’t want to warn for non-string args; see issue #697.
 +    format!("{}", 42);
 +    format!("{:?}", 42);
 +    format!("{:+}", 42);
 +    format!("foo {}", 42);
 +    format!("{} bar", 42);
 +
 +    // We only want to warn about `format!` itself.
 +    println!("foo");
 +    println!("{}", "foo");
 +    println!("foo {}", "foo");
 +    println!("{}", 42);
 +    println!("foo {}", 42);
 +
 +    // A `format!` inside a macro should not trigger a warning.
 +    foo!("should not warn");
 +
 +    // Precision on string means slicing without panicking on size.
 +    format!("{:.1}", "foo"); // Could be `"foo"[..1]`
 +    format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
 +    format!("{:.prec$}", "foo", prec = 1);
 +    format!("{:.prec$}", "foo", prec = 10);
 +
 +    42.to_string();
 +    let x = std::path::PathBuf::from("/bar/foo/qux");
 +    x.display().to_string();
 +
 +    // False positive
 +    let a = "foo".to_string();
 +    let _ = Some(a + "bar");
 +
 +    // Wrap it with braces
 +    let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
 +    let _s: String = (&*v.join("\n")).to_string();
 +
 +    format!("prepend {:+}", "s");
 +}
index 4599fb5207ea85c280f07b863d39f798f6262eaa,0000000000000000000000000000000000000000..2f4595650cbf3c8d3cdd716e3ea32a0e24113b58
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
- #![allow(clippy::print_literal, clippy::redundant_clone)]
 +// run-rustfix
 +
++#![allow(clippy::print_literal, clippy::redundant_clone, clippy::to_string_in_format_args)]
 +#![warn(clippy::useless_format)]
 +
 +struct Foo(pub String);
 +
 +macro_rules! foo {
 +    ($($t:tt)*) => (Foo(format!($($t)*)))
 +}
 +
 +fn main() {
 +    format!("foo");
 +    format!("{{}}");
 +    format!("{{}} abc {{}}");
 +    format!(
 +        r##"foo {{}}
 +" bar"##
 +    );
 +
 +    format!("{}", "foo");
 +    format!("{:?}", "foo"); // Don't warn about `Debug`.
 +    format!("{:8}", "foo");
 +    format!("{:width$}", "foo", width = 8);
 +    format!("{:+}", "foo"); // Warn when the format makes no difference.
 +    format!("{:<}", "foo"); // Warn when the format makes no difference.
 +    format!("foo {}", "bar");
 +    format!("{} bar", "foo");
 +
 +    let arg: String = "".to_owned();
 +    format!("{}", arg);
 +    format!("{:?}", arg); // Don't warn about debug.
 +    format!("{:8}", arg);
 +    format!("{:width$}", arg, width = 8);
 +    format!("{:+}", arg); // Warn when the format makes no difference.
 +    format!("{:<}", arg); // Warn when the format makes no difference.
 +    format!("foo {}", arg);
 +    format!("{} bar", arg);
 +
 +    // We don’t want to warn for non-string args; see issue #697.
 +    format!("{}", 42);
 +    format!("{:?}", 42);
 +    format!("{:+}", 42);
 +    format!("foo {}", 42);
 +    format!("{} bar", 42);
 +
 +    // We only want to warn about `format!` itself.
 +    println!("foo");
 +    println!("{}", "foo");
 +    println!("foo {}", "foo");
 +    println!("{}", 42);
 +    println!("foo {}", 42);
 +
 +    // A `format!` inside a macro should not trigger a warning.
 +    foo!("should not warn");
 +
 +    // Precision on string means slicing without panicking on size.
 +    format!("{:.1}", "foo"); // Could be `"foo"[..1]`
 +    format!("{:.10}", "foo"); // Could not be `"foo"[..10]`
 +    format!("{:.prec$}", "foo", prec = 1);
 +    format!("{:.prec$}", "foo", prec = 10);
 +
 +    format!("{}", 42.to_string());
 +    let x = std::path::PathBuf::from("/bar/foo/qux");
 +    format!("{}", x.display().to_string());
 +
 +    // False positive
 +    let a = "foo".to_string();
 +    let _ = Some(format!("{}", a + "bar"));
 +
 +    // Wrap it with braces
 +    let v: Vec<String> = vec!["foo".to_string(), "bar".to_string()];
 +    let _s: String = format!("{}", &*v.join("\n"));
 +
 +    format!("prepend {:+}", "s");
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8376566c4d62d57fcfa1c51198500c4ac517e904
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,105 @@@
++// run-rustfix
++
++#![allow(unreachable_code)]
++#![allow(unused_macros)]
++#![allow(unused_variables)]
++#![allow(clippy::assertions_on_constants)]
++#![allow(clippy::eq_op)]
++#![warn(clippy::to_string_in_format_args)]
++
++use std::io::{stdout, Write};
++use std::ops::Deref;
++use std::panic::Location;
++
++struct Somewhere;
++
++impl ToString for Somewhere {
++    fn to_string(&self) -> String {
++        String::from("somewhere")
++    }
++}
++
++struct X(u32);
++
++impl Deref for X {
++    type Target = u32;
++
++    fn deref(&self) -> &u32 {
++        &self.0
++    }
++}
++
++struct Y<'a>(&'a X);
++
++impl<'a> Deref for Y<'a> {
++    type Target = &'a X;
++
++    fn deref(&self) -> &Self::Target {
++        &self.0
++    }
++}
++
++struct Z(u32);
++
++impl Deref for Z {
++    type Target = u32;
++
++    fn deref(&self) -> &u32 {
++        &self.0
++    }
++}
++
++impl std::fmt::Display for Z {
++    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
++        write!(f, "Z")
++    }
++}
++
++macro_rules! my_macro {
++    () => {
++        // here be dragons, do not enter (or lint)
++        println!("error: something failed at {}", Location::caller().to_string());
++    };
++}
++
++macro_rules! my_other_macro {
++    () => {
++        Location::caller().to_string()
++    };
++}
++
++fn main() {
++    let x = &X(1);
++    let x_ref = &x;
++
++    let _ = format!("error: something failed at {}", Location::caller());
++    let _ = write!(
++        stdout(),
++        "error: something failed at {}",
++        Location::caller()
++    );
++    let _ = writeln!(
++        stdout(),
++        "error: something failed at {}",
++        Location::caller()
++    );
++    print!("error: something failed at {}", Location::caller());
++    println!("error: something failed at {}", Location::caller());
++    eprint!("error: something failed at {}", Location::caller());
++    eprintln!("error: something failed at {}", Location::caller());
++    let _ = format_args!("error: something failed at {}", Location::caller());
++    assert!(true, "error: something failed at {}", Location::caller());
++    assert_eq!(0, 0, "error: something failed at {}", Location::caller());
++    assert_ne!(0, 0, "error: something failed at {}", Location::caller());
++    panic!("error: something failed at {}", Location::caller());
++    println!("{}", *X(1));
++    println!("{}", ***Y(&X(1)));
++    println!("{}", Z(1));
++    println!("{}", **x);
++    println!("{}", ***x_ref);
++
++    println!("error: something failed at {}", Somewhere.to_string());
++    println!("{} and again {0}", x.to_string());
++    my_macro!();
++    println!("error: something failed at {}", my_other_macro!());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..164cc07066dc3b892022b616b34f4130083c12ce
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,105 @@@
++// run-rustfix
++
++#![allow(unreachable_code)]
++#![allow(unused_macros)]
++#![allow(unused_variables)]
++#![allow(clippy::assertions_on_constants)]
++#![allow(clippy::eq_op)]
++#![warn(clippy::to_string_in_format_args)]
++
++use std::io::{stdout, Write};
++use std::ops::Deref;
++use std::panic::Location;
++
++struct Somewhere;
++
++impl ToString for Somewhere {
++    fn to_string(&self) -> String {
++        String::from("somewhere")
++    }
++}
++
++struct X(u32);
++
++impl Deref for X {
++    type Target = u32;
++
++    fn deref(&self) -> &u32 {
++        &self.0
++    }
++}
++
++struct Y<'a>(&'a X);
++
++impl<'a> Deref for Y<'a> {
++    type Target = &'a X;
++
++    fn deref(&self) -> &Self::Target {
++        &self.0
++    }
++}
++
++struct Z(u32);
++
++impl Deref for Z {
++    type Target = u32;
++
++    fn deref(&self) -> &u32 {
++        &self.0
++    }
++}
++
++impl std::fmt::Display for Z {
++    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
++        write!(f, "Z")
++    }
++}
++
++macro_rules! my_macro {
++    () => {
++        // here be dragons, do not enter (or lint)
++        println!("error: something failed at {}", Location::caller().to_string());
++    };
++}
++
++macro_rules! my_other_macro {
++    () => {
++        Location::caller().to_string()
++    };
++}
++
++fn main() {
++    let x = &X(1);
++    let x_ref = &x;
++
++    let _ = format!("error: something failed at {}", Location::caller().to_string());
++    let _ = write!(
++        stdout(),
++        "error: something failed at {}",
++        Location::caller().to_string()
++    );
++    let _ = writeln!(
++        stdout(),
++        "error: something failed at {}",
++        Location::caller().to_string()
++    );
++    print!("error: something failed at {}", Location::caller().to_string());
++    println!("error: something failed at {}", Location::caller().to_string());
++    eprint!("error: something failed at {}", Location::caller().to_string());
++    eprintln!("error: something failed at {}", Location::caller().to_string());
++    let _ = format_args!("error: something failed at {}", Location::caller().to_string());
++    assert!(true, "error: something failed at {}", Location::caller().to_string());
++    assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
++    assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
++    panic!("error: something failed at {}", Location::caller().to_string());
++    println!("{}", X(1).to_string());
++    println!("{}", Y(&X(1)).to_string());
++    println!("{}", Z(1).to_string());
++    println!("{}", x.to_string());
++    println!("{}", x_ref.to_string());
++
++    println!("error: something failed at {}", Somewhere.to_string());
++    println!("{} and again {0}", x.to_string());
++    my_macro!();
++    println!("error: something failed at {}", my_other_macro!());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9cfc97edeafb8215fabae9014bc1b543840a650c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,106 @@@
++error: `to_string` applied to a type that implements `Display` in `format!` args
++  --> $DIR/format_args.rs:75:72
++   |
++LL |     let _ = format!("error: something failed at {}", Location::caller().to_string());
++   |                                                                        ^^^^^^^^^^^^ help: remove this
++   |
++   = note: `-D clippy::to-string-in-format-args` implied by `-D warnings`
++
++error: `to_string` applied to a type that implements `Display` in `write!` args
++  --> $DIR/format_args.rs:79:27
++   |
++LL |         Location::caller().to_string()
++   |                           ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `writeln!` args
++  --> $DIR/format_args.rs:84:27
++   |
++LL |         Location::caller().to_string()
++   |                           ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `print!` args
++  --> $DIR/format_args.rs:86:63
++   |
++LL |     print!("error: something failed at {}", Location::caller().to_string());
++   |                                                               ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:87:65
++   |
++LL |     println!("error: something failed at {}", Location::caller().to_string());
++   |                                                                 ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `eprint!` args
++  --> $DIR/format_args.rs:88:64
++   |
++LL |     eprint!("error: something failed at {}", Location::caller().to_string());
++   |                                                                ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `eprintln!` args
++  --> $DIR/format_args.rs:89:66
++   |
++LL |     eprintln!("error: something failed at {}", Location::caller().to_string());
++   |                                                                  ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `format_args!` args
++  --> $DIR/format_args.rs:90:77
++   |
++LL |     let _ = format_args!("error: something failed at {}", Location::caller().to_string());
++   |                                                                             ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `assert!` args
++  --> $DIR/format_args.rs:91:70
++   |
++LL |     assert!(true, "error: something failed at {}", Location::caller().to_string());
++   |                                                                      ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
++  --> $DIR/format_args.rs:92:73
++   |
++LL |     assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
++   |                                                                         ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
++  --> $DIR/format_args.rs:93:73
++   |
++LL |     assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
++   |                                                                         ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `panic!` args
++  --> $DIR/format_args.rs:94:63
++   |
++LL |     panic!("error: something failed at {}", Location::caller().to_string());
++   |                                                               ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:95:20
++   |
++LL |     println!("{}", X(1).to_string());
++   |                    ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:96:20
++   |
++LL |     println!("{}", Y(&X(1)).to_string());
++   |                    ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:97:24
++   |
++LL |     println!("{}", Z(1).to_string());
++   |                        ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:98:20
++   |
++LL |     println!("{}", x.to_string());
++   |                    ^^^^^^^^^^^^^ help: use this: `**x`
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:99:20
++   |
++LL |     println!("{}", x_ref.to_string());
++   |                    ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
++
++error: aborting due to 17 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a8c06c2bde6645e418bd8d3f76599e24be4d9c2e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,60 @@@
++#![allow(clippy::assertions_on_constants)]
++#![allow(clippy::eq_op)]
++#![warn(clippy::format_in_format_args)]
++#![warn(clippy::to_string_in_format_args)]
++
++use std::io::{stdout, Error, ErrorKind, Write};
++use std::ops::Deref;
++use std::panic::Location;
++
++macro_rules! my_macro {
++    () => {
++        // here be dragons, do not enter (or lint)
++        println!("error: {}", format!("something failed at {}", Location::caller()));
++    };
++}
++
++macro_rules! my_other_macro {
++    () => {
++        format!("something failed at {}", Location::caller())
++    };
++}
++
++fn main() {
++    let error = Error::new(ErrorKind::Other, "bad thing");
++    let x = 'x';
++
++    println!("error: {}", format!("something failed at {}", Location::caller()));
++    println!("{}: {}", error, format!("something failed at {}", Location::caller()));
++    println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
++    println!("{{}}: {}", format!("something failed at {}", Location::caller()));
++    println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
++    println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
++    println!("error: {}", format!("something failed at {} {0}", Location::caller()));
++    let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
++    let _ = write!(
++        stdout(),
++        "error: {}",
++        format!("something failed at {}", Location::caller())
++    );
++    let _ = writeln!(
++        stdout(),
++        "error: {}",
++        format!("something failed at {}", Location::caller())
++    );
++    print!("error: {}", format!("something failed at {}", Location::caller()));
++    eprint!("error: {}", format!("something failed at {}", Location::caller()));
++    eprintln!("error: {}", format!("something failed at {}", Location::caller()));
++    let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
++    assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
++    assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
++    assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
++    panic!("error: {}", format!("something failed at {}", Location::caller()));
++
++    println!("error: {}", format_args!("something failed at {}", Location::caller()));
++    println!("error: {:>70}", format!("something failed at {}", Location::caller()));
++    println!("error: {} {0}", format!("something failed at {}", Location::caller()));
++    println!("{} and again {0}", format!("hi {}", x));
++    my_macro!();
++    println!("error: {}", my_other_macro!());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4476218ad58e91a3d0a4e0b5c7a23b5d1c0e43b4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,175 @@@
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:27:5
++   |
++LL |     println!("error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::format-in-format-args` implied by `-D warnings`
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:28:5
++   |
++LL |     println!("{}: {}", error, format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:29:5
++   |
++LL |     println!("{:?}: {}", error, format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:30:5
++   |
++LL |     println!("{{}}: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:31:5
++   |
++LL |     println!(r#"error: "{}""#, format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:32:5
++   |
++LL |     println!("error: {}", format!(r#"something failed at "{}""#, Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `println!` args
++  --> $DIR/format_args_unfixable.rs:33:5
++   |
++LL |     println!("error: {}", format!("something failed at {} {0}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `println!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `format!` args
++  --> $DIR/format_args_unfixable.rs:34:13
++   |
++LL |     let _ = format!("error: {}", format!("something failed at {}", Location::caller()));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `format!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `write!` args
++  --> $DIR/format_args_unfixable.rs:35:13
++   |
++LL |       let _ = write!(
++   |  _____________^
++LL | |         stdout(),
++LL | |         "error: {}",
++LL | |         format!("something failed at {}", Location::caller())
++LL | |     );
++   | |_____^
++   |
++   = help: combine the `format!(..)` arguments with the outer `write!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `writeln!` args
++  --> $DIR/format_args_unfixable.rs:40:13
++   |
++LL |       let _ = writeln!(
++   |  _____________^
++LL | |         stdout(),
++LL | |         "error: {}",
++LL | |         format!("something failed at {}", Location::caller())
++LL | |     );
++   | |_____^
++   |
++   = help: combine the `format!(..)` arguments with the outer `writeln!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `print!` args
++  --> $DIR/format_args_unfixable.rs:45:5
++   |
++LL |     print!("error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `print!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `eprint!` args
++  --> $DIR/format_args_unfixable.rs:46:5
++   |
++LL |     eprint!("error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `eprint!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `eprintln!` args
++  --> $DIR/format_args_unfixable.rs:47:5
++   |
++LL |     eprintln!("error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `eprintln!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `format_args!` args
++  --> $DIR/format_args_unfixable.rs:48:13
++   |
++LL |     let _ = format_args!("error: {}", format!("something failed at {}", Location::caller()));
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `format_args!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `assert!` args
++  --> $DIR/format_args_unfixable.rs:49:5
++   |
++LL |     assert!(true, "error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `assert!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `assert_eq!` args
++  --> $DIR/format_args_unfixable.rs:50:5
++   |
++LL |     assert_eq!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `assert_eq!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `assert_ne!` args
++  --> $DIR/format_args_unfixable.rs:51:5
++   |
++LL |     assert_ne!(0, 0, "error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `assert_ne!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: `format!` in `panic!` args
++  --> $DIR/format_args_unfixable.rs:52:5
++   |
++LL |     panic!("error: {}", format!("something failed at {}", Location::caller()));
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: combine the `format!(..)` arguments with the outer `panic!(..)` call
++   = help: or consider changing `format!` to `format_args!`
++
++error: aborting due to 18 previous errors
++
index 859765d08a70bbb83d313b53f608cd23d07aff4b,0000000000000000000000000000000000000000..e6f57e9267eac93091e423180bbaef8f9d97315b
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,168 @@@
 +// run-rustfix
 +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
 +#![warn(clippy::implicit_saturating_sub)]
 +
 +fn main() {
 +    // Tests for unsigned integers
 +
 +    let end_8: u8 = 10;
 +    let start_8: u8 = 5;
 +    let mut u_8: u8 = end_8 - start_8;
 +
 +    // Lint
 +    u_8 = u_8.saturating_sub(1);
 +
 +    match end_8 {
 +        10 => {
 +            // Lint
 +            u_8 = u_8.saturating_sub(1);
 +        },
 +        11 => u_8 += 1,
 +        _ => u_8 = 0,
 +    }
 +
 +    let end_16: u16 = 40;
 +    let start_16: u16 = 35;
 +
 +    let mut u_16: u16 = end_16 - start_16;
 +
 +    // Lint
 +    u_16 = u_16.saturating_sub(1);
 +
 +    let mut end_32: u32 = 7010;
 +    let mut start_32: u32 = 7000;
 +
 +    let mut u_32: u32 = end_32 - start_32;
 +
 +    // Lint
 +    u_32 = u_32.saturating_sub(1);
 +
 +    // No Lint
 +    if u_32 > 0 {
 +        u_16 += 1;
 +    }
 +
 +    // No Lint
 +    if u_32 != 0 {
 +        end_32 -= 1;
 +        start_32 += 1;
 +    }
 +
 +    let mut end_64: u64 = 75001;
 +    let mut start_64: u64 = 75000;
 +
 +    let mut u_64: u64 = end_64 - start_64;
 +
 +    // Lint
 +    u_64 = u_64.saturating_sub(1);
 +
 +    // Lint
 +    u_64 = u_64.saturating_sub(1);
 +
 +    // Lint
 +    u_64 = u_64.saturating_sub(1);
 +
 +    // No Lint
 +    if u_64 >= 1 {
 +        u_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_64 > 0 {
 +        end_64 -= 1;
 +    }
 +
 +    // Tests for usize
 +    let end_usize: usize = 8054;
 +    let start_usize: usize = 8050;
 +
 +    let mut u_usize: usize = end_usize - start_usize;
 +
 +    // Lint
 +    u_usize = u_usize.saturating_sub(1);
 +
 +    // Tests for signed integers
 +
 +    let endi_8: i8 = 10;
 +    let starti_8: i8 = 50;
 +
 +    let mut i_8: i8 = endi_8 - starti_8;
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    // Lint
 +    i_8 = i_8.saturating_sub(1);
 +
 +    let endi_16: i16 = 45;
 +    let starti_16: i16 = 44;
 +
 +    let mut i_16: i16 = endi_16 - starti_16;
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    // Lint
 +    i_16 = i_16.saturating_sub(1);
 +
 +    let endi_32: i32 = 45;
 +    let starti_32: i32 = 44;
 +
 +    let mut i_32: i32 = endi_32 - starti_32;
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    // Lint
 +    i_32 = i_32.saturating_sub(1);
 +
 +    let endi_64: i64 = 45;
 +    let starti_64: i64 = 44;
 +
 +    let mut i_64: i64 = endi_64 - starti_64;
 +
 +    // Lint
 +    i_64 = i_64.saturating_sub(1);
 +
 +    // Lint
 +    i_64 = i_64.saturating_sub(1);
 +
 +    // Lint
 +    i_64 = i_64.saturating_sub(1);
 +
 +    // No Lint
 +    if i_64 > 0 {
 +        i_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if i_64 != 0 {
 +        i_64 -= 1;
 +    }
++
++    // issue #7831
++    // No Lint
++    if u_32 > 0 {
++        u_32 -= 1;
++    } else {
++        println!("side effect");
++    }
 +}
index 2f32a7b1578219a2a5c951b9099994a22681bed5,0000000000000000000000000000000000000000..8bb28d149c62895c210a5e4ba68082daf6ff9813
mode 100644,000000..100644
--- /dev/null
@@@ -1,206 -1,0 +1,214 @@@
 +// run-rustfix
 +#![allow(unused_assignments, unused_mut, clippy::assign_op_pattern)]
 +#![warn(clippy::implicit_saturating_sub)]
 +
 +fn main() {
 +    // Tests for unsigned integers
 +
 +    let end_8: u8 = 10;
 +    let start_8: u8 = 5;
 +    let mut u_8: u8 = end_8 - start_8;
 +
 +    // Lint
 +    if u_8 > 0 {
 +        u_8 = u_8 - 1;
 +    }
 +
 +    match end_8 {
 +        10 => {
 +            // Lint
 +            if u_8 > 0 {
 +                u_8 -= 1;
 +            }
 +        },
 +        11 => u_8 += 1,
 +        _ => u_8 = 0,
 +    }
 +
 +    let end_16: u16 = 40;
 +    let start_16: u16 = 35;
 +
 +    let mut u_16: u16 = end_16 - start_16;
 +
 +    // Lint
 +    if u_16 > 0 {
 +        u_16 -= 1;
 +    }
 +
 +    let mut end_32: u32 = 7010;
 +    let mut start_32: u32 = 7000;
 +
 +    let mut u_32: u32 = end_32 - start_32;
 +
 +    // Lint
 +    if u_32 != 0 {
 +        u_32 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_32 > 0 {
 +        u_16 += 1;
 +    }
 +
 +    // No Lint
 +    if u_32 != 0 {
 +        end_32 -= 1;
 +        start_32 += 1;
 +    }
 +
 +    let mut end_64: u64 = 75001;
 +    let mut start_64: u64 = 75000;
 +
 +    let mut u_64: u64 = end_64 - start_64;
 +
 +    // Lint
 +    if u_64 > 0 {
 +        u_64 -= 1;
 +    }
 +
 +    // Lint
 +    if 0 < u_64 {
 +        u_64 -= 1;
 +    }
 +
 +    // Lint
 +    if 0 != u_64 {
 +        u_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_64 >= 1 {
 +        u_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if u_64 > 0 {
 +        end_64 -= 1;
 +    }
 +
 +    // Tests for usize
 +    let end_usize: usize = 8054;
 +    let start_usize: usize = 8050;
 +
 +    let mut u_usize: usize = end_usize - start_usize;
 +
 +    // Lint
 +    if u_usize > 0 {
 +        u_usize -= 1;
 +    }
 +
 +    // Tests for signed integers
 +
 +    let endi_8: i8 = 10;
 +    let starti_8: i8 = 50;
 +
 +    let mut i_8: i8 = endi_8 - starti_8;
 +
 +    // Lint
 +    if i_8 > i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    // Lint
 +    if i_8 > i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    // Lint
 +    if i_8 != i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    // Lint
 +    if i_8 != i8::MIN {
 +        i_8 -= 1;
 +    }
 +
 +    let endi_16: i16 = 45;
 +    let starti_16: i16 = 44;
 +
 +    let mut i_16: i16 = endi_16 - starti_16;
 +
 +    // Lint
 +    if i_16 > i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    // Lint
 +    if i_16 > i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    // Lint
 +    if i_16 != i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    // Lint
 +    if i_16 != i16::MIN {
 +        i_16 -= 1;
 +    }
 +
 +    let endi_32: i32 = 45;
 +    let starti_32: i32 = 44;
 +
 +    let mut i_32: i32 = endi_32 - starti_32;
 +
 +    // Lint
 +    if i_32 > i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    // Lint
 +    if i_32 > i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    // Lint
 +    if i_32 != i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    // Lint
 +    if i_32 != i32::MIN {
 +        i_32 -= 1;
 +    }
 +
 +    let endi_64: i64 = 45;
 +    let starti_64: i64 = 44;
 +
 +    let mut i_64: i64 = endi_64 - starti_64;
 +
 +    // Lint
 +    if i64::MIN < i_64 {
 +        i_64 -= 1;
 +    }
 +
 +    // Lint
 +    if i64::MIN != i_64 {
 +        i_64 -= 1;
 +    }
 +
 +    // Lint
 +    if i64::MIN < i_64 {
 +        i_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if i_64 > 0 {
 +        i_64 -= 1;
 +    }
 +
 +    // No Lint
 +    if i_64 != 0 {
 +        i_64 -= 1;
 +    }
++
++    // issue #7831
++    // No Lint
++    if u_32 > 0 {
++        u_32 -= 1;
++    } else {
++        println!("side effect");
++    }
 +}
index 366ef36c367bfb3f031f42968dc77f170c3c1ec5,0000000000000000000000000000000000000000..d7cedf9f9f1596dfabb659099e2922281b410f93
mode 100644,000000..100644
--- /dev/null
@@@ -1,157 -1,0 +1,124 @@@
- error: you don't need to add `&` to both the expression and the patterns
-   --> $DIR/match_expr_like_matches_macro.rs:166:20
-    |
- LL |           let _res = match &val {
-    |  ____________________^
- LL | |             &Some(ref _a) => true,
- LL | |             _ => false,
- LL | |         };
-    | |_________^
-    |
-    = note: `-D clippy::match-ref-pats` implied by `-D warnings`
- help: try
-    |
- LL ~         let _res = match val {
- LL ~             Some(ref _a) => true,
-    |
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:10:14
 +   |
 +LL |       let _y = match x {
 +   |  ______________^
 +LL | |         Some(0) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(0))`
 +   |
 +   = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:16:14
 +   |
 +LL |       let _w = match x {
 +   |  ______________^
 +LL | |         Some(_) => true,
 +LL | |         _ => false,
 +LL | |     };
 +   | |_____^ help: try this: `matches!(x, Some(_))`
 +
 +error: redundant pattern matching, consider using `is_none()`
 +  --> $DIR/match_expr_like_matches_macro.rs:22:14
 +   |
 +LL |       let _z = match x {
 +   |  ______________^
 +LL | |         Some(_) => false,
 +LL | |         None => true,
 +LL | |     };
 +   | |_____^ help: try this: `x.is_none()`
 +   |
 +   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:28:15
 +   |
 +LL |       let _zz = match x {
 +   |  _______________^
 +LL | |         Some(r) if r == 0 => false,
 +LL | |         _ => true,
 +LL | |     };
 +   | |_____^ help: try this: `!matches!(x, Some(r) if r == 0)`
 +
 +error: if let .. else expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:34:16
 +   |
 +LL |     let _zzz = if let Some(5) = x { true } else { false };
 +   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:58:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::A(_) => true,
 +LL | |             E::B(_) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:66:20
 +   |
 +LL |           let _ans = match x {
 +   |  ____________________^
 +LL | |             E::B(_) => false,
 +LL | |             E::C => false,
 +LL | |             _ => true,
 +LL | |         };
 +   | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:126:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(z, Some(3))`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:135:18
 +   |
 +LL |           let _z = match &z {
 +   |  __________________^
 +LL | |             Some(3) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&z, Some(3))`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:152:21
 +   |
 +LL |               let _ = match &z {
 +   |  _____________________^
 +LL | |                 AnEnum::X => true,
 +LL | |                 _ => false,
 +LL | |             };
 +   | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
 +
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:166:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
- error: you don't need to add `&` to both the expression and the patterns
-   --> $DIR/match_expr_like_matches_macro.rs:178:20
-    |
- LL |           let _res = match &val {
-    |  ____________________^
- LL | |             &Some(ref _a) => true,
- LL | |             _ => false,
- LL | |         };
-    | |_________^
-    |
- help: try
-    |
- LL ~         let _res = match val {
- LL ~             Some(ref _a) => true,
-    |
- error: aborting due to 14 previous errors
 +error: match expression looks like `matches!` macro
 +  --> $DIR/match_expr_like_matches_macro.rs:178:20
 +   |
 +LL |           let _res = match &val {
 +   |  ____________________^
 +LL | |             &Some(ref _a) => true,
 +LL | |             _ => false,
 +LL | |         };
 +   | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 +
++error: aborting due to 12 previous errors
 +
index 846d665d1d864d86dd9396dc23a379271252cb8e,0000000000000000000000000000000000000000..ff91c4498ec62afd43c12497579a6a66925fd42a
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,110 @@@
-         0..=10 => println!("0 ... 10"),
-         0..=11 => println!("0 ... 11"),
 +#![feature(exclusive_range_pattern)]
 +#![feature(half_open_range_patterns)]
 +#![warn(clippy::match_overlapping_arm)]
 +#![allow(clippy::redundant_pattern_matching)]
 +#![allow(clippy::if_same_then_else, clippy::equatable_if_let)]
 +
 +/// Tests for match_overlapping_arm
 +
 +fn overlapping() {
 +    const FOO: u64 = 2;
 +
 +    match 42 {
-         0..=5 => println!("0 ... 5"),
-         6..=7 => println!("6 ... 7"),
-         FOO..=11 => println!("0 ... 11"),
++        0..=10 => println!("0..=10"),
++        0..=11 => println!("0..=11"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         0..=5 => println!("0 ... 5"),
++        0..=5 => println!("0..=5"),
++        6..=7 => println!("6..=7"),
++        FOO..=11 => println!("FOO..=11"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        2 => println!("2"),
-         0..=2 => println!("0 ... 2"),
++        0..=5 => println!("0..=5"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        2 => println!("2"),
-         0..=10 => println!("0 ... 10"),
-         11..=50 => println!("11 ... 50"),
++        0..=2 => println!("0..=2"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         0..2 => println!("0 .. 2"),
++        0..=10 => println!("0..=10"),
++        11..=50 => println!("11..=50"),
 +        _ => (),
 +    }
 +
 +    match 42 {
 +        2 => println!("2"),
-         0..10 => println!("0 .. 10"),
-         10..50 => println!("10 .. 50"),
++        0..2 => println!("0..2"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         0..11 => println!("0 .. 11"),
-         0..=11 => println!("0 ... 11"),
++        0..10 => println!("0..10"),
++        10..50 => println!("10..50"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         5..7 => println!("5 .. 7"),
-         0..10 => println!("0 .. 10"),
++        0..11 => println!("0..11"),
++        0..=11 => println!("0..=11"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         5..10 => println!("5 .. 10"),
-         0..=10 => println!("0 ... 10"),
++        5..7 => println!("5..7"),
++        0..10 => println!("0..10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         0..14 => println!("0 .. 14"),
-         5..10 => println!("5 .. 10"),
++        5..10 => println!("5..10"),
++        0..=10 => println!("0..=10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         5..14 => println!("5 .. 14"),
-         0..=10 => println!("0 ... 10"),
++        0..14 => println!("0..14"),
++        5..10 => println!("5..10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-         0..7 => println!("0 .. 7"),
-         0..=10 => println!("0 ... 10"),
++        5..14 => println!("5..14"),
++        0..=10 => println!("0..=10"),
 +        _ => (),
 +    }
 +
 +    match 42 {
-     /*
-     // FIXME(JohnTitor): uncomment this once rustfmt knows half-open patterns
++        0..7 => println!("0..7"),
++        0..=10 => println!("0..=10"),
 +        _ => (),
 +    }
 +
-         0.. => println!("0 .. 42"),
-         3.. => println!("3 .. 42"),
 +    match 42 {
-         ..=23 => println!("0 ... 23"),
-         ..26 => println!("0 .. 26"),
++        3.. => println!("3.."),
++        0.. => println!("0.."),
 +        _ => (),
 +    }
 +
 +    match 42 {
-     */
++        ..=23 => println!("..=23"),
++        ..26 => println!("..26"),
 +        _ => (),
 +    }
 +
 +    if let None = Some(42) {
 +        // nothing
 +    } else if let None = Some(42) {
 +        // another nothing :-)
 +    }
 +}
 +
 +fn main() {}
index 359fa49f51be73735e65231454cc6bda7fbbbe89,0000000000000000000000000000000000000000..c2b3f173c2b80bc7102261d8db7d7862e643d875
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,75 @@@
- LL |         0..=10 => println!("0 ... 10"),
 +error: some ranges overlap
 +  --> $DIR/match_overlapping_arm.rs:13:9
 +   |
- LL |         0..=11 => println!("0 ... 11"),
++LL |         0..=10 => println!("0..=10"),
 +   |         ^^^^^^
 +   |
 +   = note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
 +note: overlaps with this
 +  --> $DIR/match_overlapping_arm.rs:14:9
 +   |
- LL |         0..=5 => println!("0 ... 5"),
++LL |         0..=11 => println!("0..=11"),
 +   |         ^^^^^^
 +
 +error: some ranges overlap
 +  --> $DIR/match_overlapping_arm.rs:19:9
 +   |
- LL |         FOO..=11 => println!("0 ... 11"),
++LL |         0..=5 => println!("0..=5"),
 +   |         ^^^^^
 +   |
 +note: overlaps with this
 +  --> $DIR/match_overlapping_arm.rs:21:9
 +   |
- LL |         0..11 => println!("0 .. 11"),
++LL |         FOO..=11 => println!("FOO..=11"),
 +   |         ^^^^^^^^
 +
 +error: some ranges overlap
 +  --> $DIR/match_overlapping_arm.rs:56:9
 +   |
- LL |         0..=11 => println!("0 ... 11"),
++LL |         0..11 => println!("0..11"),
 +   |         ^^^^^
 +   |
 +note: overlaps with this
 +  --> $DIR/match_overlapping_arm.rs:57:9
 +   |
- LL |         0..=10 => println!("0 ... 10"),
++LL |         0..=11 => println!("0..=11"),
 +   |         ^^^^^^
 +
 +error: some ranges overlap
 +  --> $DIR/match_overlapping_arm.rs:81:9
 +   |
- LL |         5..14 => println!("5 .. 14"),
++LL |         0..=10 => println!("0..=10"),
 +   |         ^^^^^^
 +   |
 +note: overlaps with this
 +  --> $DIR/match_overlapping_arm.rs:80:9
 +   |
- LL |         0..7 => println!("0 .. 7"),
++LL |         5..14 => println!("5..14"),
 +   |         ^^^^^
 +
 +error: some ranges overlap
 +  --> $DIR/match_overlapping_arm.rs:86:9
 +   |
- LL |         0..=10 => println!("0 ... 10"),
++LL |         0..7 => println!("0..7"),
 +   |         ^^^^
 +   |
 +note: overlaps with this
 +  --> $DIR/match_overlapping_arm.rs:87:9
 +   |
- error: aborting due to 5 previous errors
++LL |         0..=10 => println!("0..=10"),
 +   |         ^^^^^^
 +
++error: some ranges overlap
++  --> $DIR/match_overlapping_arm.rs:98:9
++   |
++LL |         ..=23 => println!("..=23"),
++   |         ^^^^^
++   |
++note: overlaps with this
++  --> $DIR/match_overlapping_arm.rs:99:9
++   |
++LL |         ..26 => println!("..26"),
++   |         ^^^^
++
++error: aborting due to 6 previous errors
 +
index 6cbb4d32b0d71287c8e35a8e648d9436cbb0aef5,0000000000000000000000000000000000000000..50246486bb6fc9371206f17adfe284b5e722a566
mode 100644,000000..100644
--- /dev/null
@@@ -1,75 -1,0 +1,117 @@@
 +#![warn(clippy::match_ref_pats)]
 +#![allow(clippy::equatable_if_let)]
 +
 +fn ref_pats() {
 +    {
 +        let v = &Some(0);
 +        match v {
 +            &Some(v) => println!("{:?}", v),
 +            &None => println!("none"),
 +        }
 +        match v {
 +            // This doesn't trigger; we have a different pattern.
 +            &Some(v) => println!("some"),
 +            other => println!("other"),
 +        }
 +    }
 +    let tup = &(1, 2);
 +    match tup {
 +        &(v, 1) => println!("{}", v),
 +        _ => println!("none"),
 +    }
 +    // Special case: using `&` both in expr and pats.
 +    let w = Some(0);
 +    match &w {
 +        &Some(v) => println!("{:?}", v),
 +        &None => println!("none"),
 +    }
 +    // False positive: only wildcard pattern.
 +    let w = Some(0);
 +    #[allow(clippy::match_single_binding)]
 +    match w {
 +        _ => println!("none"),
 +    }
 +
 +    let a = &Some(0);
 +    if let &None = a {
 +        println!("none");
 +    }
 +
 +    let b = Some(0);
 +    if let &None = &b {
 +        println!("none");
 +    }
 +}
 +
 +mod ice_3719 {
 +    macro_rules! foo_variant(
 +        ($idx:expr) => (Foo::get($idx).unwrap())
 +    );
 +
 +    enum Foo {
 +        A,
 +        B,
 +    }
 +
 +    impl Foo {
 +        fn get(idx: u8) -> Option<&'static Self> {
 +            match idx {
 +                0 => Some(&Foo::A),
 +                1 => Some(&Foo::B),
 +                _ => None,
 +            }
 +        }
 +    }
 +
 +    fn ice_3719() {
 +        // ICE #3719
 +        match foo_variant!(0) {
 +            &Foo::A => println!("A"),
 +            _ => println!("Wild"),
 +        }
 +    }
 +}
 +
++mod issue_7740 {
++    macro_rules! foobar_variant(
++        ($idx:expr) => (FooBar::get($idx).unwrap())
++    );
++
++    enum FooBar {
++        Foo,
++        Bar,
++        FooBar,
++        BarFoo,
++    }
++
++    impl FooBar {
++        fn get(idx: u8) -> Option<&'static Self> {
++            match idx {
++                0 => Some(&FooBar::Foo),
++                1 => Some(&FooBar::Bar),
++                2 => Some(&FooBar::FooBar),
++                3 => Some(&FooBar::BarFoo),
++                _ => None,
++            }
++        }
++    }
++
++    fn issue_7740() {
++        // Issue #7740
++        match foobar_variant!(0) {
++            &FooBar::Foo => println!("Foo"),
++            &FooBar::Bar => println!("Bar"),
++            &FooBar::FooBar => println!("FooBar"),
++            _ => println!("Wild"),
++        }
++
++        // This shouldn't trigger
++        if let &FooBar::BarFoo = foobar_variant!(3) {
++            println!("BarFoo");
++        } else {
++            println!("Wild");
++        }
++    }
++}
++
 +fn main() {}
index 072aff445e97f2b330a4fdc56e58bff33fa5a1b1,0000000000000000000000000000000000000000..901820077e20e974b651c9a68ef26df9b364cf59
mode 100644,000000..100644
--- /dev/null
@@@ -1,105 -1,0 +1,68 @@@
- error: you don't need to add `&` to all patterns
-   --> $DIR/match_ref_pats.rs:18:5
-    |
- LL | /     match tup {
- LL | |         &(v, 1) => println!("{}", v),
- LL | |         _ => println!("none"),
- LL | |     }
-    | |_____^
-    |
- help: instead of prefixing all patterns with `&`, you can dereference the expression
-    |
- LL ~     match *tup {
- LL ~         (v, 1) => println!("{}", v),
-    |
 +error: you don't need to add `&` to all patterns
 +  --> $DIR/match_ref_pats.rs:7:9
 +   |
 +LL | /         match v {
 +LL | |             &Some(v) => println!("{:?}", v),
 +LL | |             &None => println!("none"),
 +LL | |         }
 +   | |_________^
 +   |
 +   = note: `-D clippy::match-ref-pats` implied by `-D warnings`
 +help: instead of prefixing all patterns with `&`, you can dereference the expression
 +   |
 +LL ~         match *v {
 +LL ~             Some(v) => println!("{:?}", v),
 +LL ~             None => println!("none"),
 +   |
 +
- error: you don't need to add `&` to all patterns
-   --> $DIR/match_ref_pats.rs:36:5
-    |
- LL | /     if let &None = a {
- LL | |         println!("none");
- LL | |     }
-    | |_____^
-    |
- help: instead of prefixing all patterns with `&`, you can dereference the expression
-    |
- LL |     if let None = *a {
-    |            ~~~~   ~~
 +error: you don't need to add `&` to both the expression and the patterns
 +  --> $DIR/match_ref_pats.rs:24:5
 +   |
 +LL | /     match &w {
 +LL | |         &Some(v) => println!("{:?}", v),
 +LL | |         &None => println!("none"),
 +LL | |     }
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     match w {
 +LL ~         Some(v) => println!("{:?}", v),
 +LL ~         None => println!("none"),
 +   |
 +
 +error: redundant pattern matching, consider using `is_none()`
 +  --> $DIR/match_ref_pats.rs:36:12
 +   |
 +LL |     if let &None = a {
 +   |     -------^^^^^---- help: try this: `if a.is_none()`
 +   |
 +   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
 +
- error: you don't need to add `&` to both the expression and the patterns
-   --> $DIR/match_ref_pats.rs:41:5
-    |
- LL | /     if let &None = &b {
- LL | |         println!("none");
- LL | |     }
-    | |_____^
-    |
- help: try
-    |
- LL |     if let None = b {
-    |            ~~~~   ~
 +error: redundant pattern matching, consider using `is_none()`
 +  --> $DIR/match_ref_pats.rs:41:12
 +   |
 +LL |     if let &None = &b {
 +   |     -------^^^^^----- help: try this: `if b.is_none()`
 +
-   --> $DIR/match_ref_pats.rs:68:9
 +error: you don't need to add `&` to all patterns
- LL | /         match foo_variant!(0) {
- LL | |             &Foo::A => println!("A"),
++  --> $DIR/match_ref_pats.rs:101:9
 +   |
- LL ~         match *foo_variant!(0) {
- LL ~             Foo::A => println!("A"),
++LL | /         match foobar_variant!(0) {
++LL | |             &FooBar::Foo => println!("Foo"),
++LL | |             &FooBar::Bar => println!("Bar"),
++LL | |             &FooBar::FooBar => println!("FooBar"),
 +LL | |             _ => println!("Wild"),
 +LL | |         }
 +   | |_________^
 +   |
 +help: instead of prefixing all patterns with `&`, you can dereference the expression
 +   |
- error: aborting due to 8 previous errors
++LL ~         match *foobar_variant!(0) {
++LL ~             FooBar::Foo => println!("Foo"),
++LL ~             FooBar::Bar => println!("Bar"),
++LL ~             FooBar::FooBar => println!("FooBar"),
 +   |
 +
++error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..208a4bba3d23c40d5cf2cbe4a2f650c18ba584b5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++#![warn(clippy::match_str_case_mismatch)]
++
++// Valid
++
++fn as_str_match() {
++    let var = "BAR";
++
++    match var.to_ascii_lowercase().as_str() {
++        "foo" => {},
++        "bar" => {},
++        _ => {},
++    }
++}
++
++fn addrof_unary_match() {
++    let var = "BAR";
++
++    match &*var.to_ascii_lowercase() {
++        "foo" => {},
++        "bar" => {},
++        _ => {},
++    }
++}
++
++fn alternating_chain() {
++    let var = "BAR";
++
++    match &*var
++        .to_ascii_lowercase()
++        .to_uppercase()
++        .to_lowercase()
++        .to_ascii_uppercase()
++    {
++        "FOO" => {},
++        "BAR" => {},
++        _ => {},
++    }
++}
++
++fn unrelated_method() {
++    struct Item {
++        a: String,
++    }
++
++    impl Item {
++        #[allow(clippy::wrong_self_convention)]
++        fn to_lowercase(self) -> String {
++            self.a
++        }
++    }
++
++    let item = Item { a: String::from("BAR") };
++
++    match &*item.to_lowercase() {
++        "FOO" => {},
++        "BAR" => {},
++        _ => {},
++    }
++}
++
++// Invalid
++
++fn as_str_match_mismatch() {
++    let var = "BAR";
++
++    match var.to_ascii_lowercase().as_str() {
++        "foo" => {},
++        "Bar" => {},
++        _ => {},
++    }
++}
++
++fn addrof_unary_match_mismatch() {
++    let var = "BAR";
++
++    match &*var.to_ascii_lowercase() {
++        "foo" => {},
++        "Bar" => {},
++        _ => {},
++    }
++}
++
++fn alternating_chain_mismatch() {
++    let var = "BAR";
++
++    match &*var
++        .to_ascii_lowercase()
++        .to_uppercase()
++        .to_lowercase()
++        .to_ascii_uppercase()
++    {
++        "FOO" => {},
++        "bAR" => {},
++        _ => {},
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fa023477a9c334d20c7a934e93091b270bd41bf0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++error: this `match` arm has a differing case than its expression
++  --> $DIR/match_str_case_mismatch.rs:68:9
++   |
++LL |         "Bar" => {},
++   |         ^^^^^
++   |
++   = note: `-D clippy::match-str-case-mismatch` implied by `-D warnings`
++help: consider changing the case of this arm to respect `to_ascii_lowercase`
++   |
++LL |         "bar" => {},
++   |         ~~~~~
++
++error: this `match` arm has a differing case than its expression
++  --> $DIR/match_str_case_mismatch.rs:78:9
++   |
++LL |         "Bar" => {},
++   |         ^^^^^
++   |
++help: consider changing the case of this arm to respect `to_ascii_lowercase`
++   |
++LL |         "bar" => {},
++   |         ~~~~~
++
++error: this `match` arm has a differing case than its expression
++  --> $DIR/match_str_case_mismatch.rs:93:9
++   |
++LL |         "bAR" => {},
++   |         ^^^^^
++   |
++help: consider changing the case of this arm to respect `to_ascii_uppercase`
++   |
++LL |         "BAR" => {},
++   |         ~~~~~
++
++error: aborting due to 3 previous errors
++
index 8965cef66deddc899b5e1779a7f8814ebf61849d,0000000000000000000000000000000000000000..be854d94183329d547df961f1690f32d10d01139
mode 100644,000000..100644
--- /dev/null
@@@ -1,49 -1,0 +1,59 @@@
++// aux-build:macro_rules.rs
++
 +#![allow(unused, clippy::no_effect, clippy::unnecessary_operation)]
 +#![warn(clippy::mut_mut)]
 +
++#[macro_use]
++extern crate macro_rules;
++
 +fn fun(x: &mut &mut u32) -> bool {
 +    **x > 0
 +}
 +
 +fn less_fun(x: *mut *mut u32) {
 +    let y = x;
 +}
 +
 +macro_rules! mut_ptr {
 +    ($p:expr) => {
 +        &mut $p
 +    };
 +}
 +
 +#[allow(unused_mut, unused_variables)]
 +fn main() {
 +    let mut x = &mut &mut 1u32;
 +    {
 +        let mut y = &mut x;
 +    }
 +
 +    if fun(x) {
 +        let y: &mut &mut u32 = &mut &mut 2;
 +        **y + **x;
 +    }
 +
 +    if fun(x) {
 +        let y: &mut &mut &mut u32 = &mut &mut &mut 2;
 +        ***y + **x;
 +    }
 +
 +    let mut z = mut_ptr!(&mut 3u32);
 +}
 +
 +fn issue939() {
 +    let array = [5, 6, 7, 8, 9];
 +    let mut args = array.iter().skip(2);
 +    for &arg in &mut args {
 +        println!("{}", arg);
 +    }
 +
 +    let args = &mut args;
 +    for arg in args {
 +        println!(":{}", arg);
 +    }
 +}
++
++fn issue6922() {
++    // do not lint from an external macro
++    mut_mut!();
++}
index 0fed6953cb85c295fc022a50e9d13a97c2772ded,0000000000000000000000000000000000000000..6820a85aa54337f2cad40936ed7d0f6bad95358b
mode 100644,000000..100644
--- /dev/null
@@@ -1,63 -1,0 +1,63 @@@
-   --> $DIR/mut_mut.rs:4:11
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:20:17
++  --> $DIR/mut_mut.rs:9:11
 +   |
 +LL | fn fun(x: &mut &mut u32) -> bool {
 +   |           ^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::mut-mut` implied by `-D warnings`
 +
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:14:9
++  --> $DIR/mut_mut.rs:25:17
 +   |
 +LL |     let mut x = &mut &mut 1u32;
 +   |                 ^^^^^^^^^^^^^^
 +
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:22:21
++  --> $DIR/mut_mut.rs:19:9
 +   |
 +LL |         &mut $p
 +   |         ^^^^^^^
 +...
 +LL |     let mut z = mut_ptr!(&mut 3u32);
 +   |                 ------------------- in this macro invocation
 +   |
 +   = note: this error originates in the macro `mut_ptr` (in Nightly builds, run with -Z macro-backtrace for more info)
 +
 +error: this expression mutably borrows a mutable reference. Consider reborrowing
-   --> $DIR/mut_mut.rs:26:32
++  --> $DIR/mut_mut.rs:27:21
 +   |
 +LL |         let mut y = &mut x;
 +   |                     ^^^^^^
 +
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:26:16
++  --> $DIR/mut_mut.rs:31:32
 +   |
 +LL |         let y: &mut &mut u32 = &mut &mut 2;
 +   |                                ^^^^^^^^^^^
 +
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:31:37
++  --> $DIR/mut_mut.rs:31:16
 +   |
 +LL |         let y: &mut &mut u32 = &mut &mut 2;
 +   |                ^^^^^^^^^^^^^
 +
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:31:16
++  --> $DIR/mut_mut.rs:36:37
 +   |
 +LL |         let y: &mut &mut &mut u32 = &mut &mut &mut 2;
 +   |                                     ^^^^^^^^^^^^^^^^
 +
 +error: generally you want to avoid `&mut &mut _` if possible
-   --> $DIR/mut_mut.rs:31:21
++  --> $DIR/mut_mut.rs:36:16
 +   |
 +LL |         let y: &mut &mut &mut u32 = &mut &mut &mut 2;
 +   |                ^^^^^^^^^^^^^^^^^^
 +
 +error: generally you want to avoid `&mut &mut _` if possible
++  --> $DIR/mut_mut.rs:36:21
 +   |
 +LL |         let y: &mut &mut &mut u32 = &mut &mut &mut 2;
 +   |                     ^^^^^^^^^^^^^
 +
 +error: aborting due to 9 previous errors
 +
index 7ec845adfaacf6d290e4d4be2011f3b0917d2621,0000000000000000000000000000000000000000..7bcc4cad0d363a78153e36456b0e128eaeef8e45
mode 100644,000000..100644
--- /dev/null
@@@ -1,105 -1,0 +1,111 @@@
- #![warn(clippy::no_effect)]
 +#![feature(box_syntax)]
++#![warn(clippy::no_effect_underscore_binding)]
 +#![allow(dead_code)]
 +#![allow(path_statements)]
 +#![allow(clippy::deref_addrof)]
 +#![allow(clippy::redundant_field_names)]
 +#![feature(untagged_unions)]
 +
 +struct Unit;
 +struct Tuple(i32);
 +struct Struct {
 +    field: i32,
 +}
 +enum Enum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +struct DropUnit;
 +impl Drop for DropUnit {
 +    fn drop(&mut self) {}
 +}
 +struct DropStruct {
 +    field: i32,
 +}
 +impl Drop for DropStruct {
 +    fn drop(&mut self) {}
 +}
 +struct DropTuple(i32);
 +impl Drop for DropTuple {
 +    fn drop(&mut self) {}
 +}
 +enum DropEnum {
 +    Tuple(i32),
 +    Struct { field: i32 },
 +}
 +impl Drop for DropEnum {
 +    fn drop(&mut self) {}
 +}
 +struct FooString {
 +    s: String,
 +}
 +union Union {
 +    a: u8,
 +    b: f64,
 +}
 +
 +fn get_number() -> i32 {
 +    0
 +}
 +fn get_struct() -> Struct {
 +    Struct { field: 0 }
 +}
 +fn get_drop_struct() -> DropStruct {
 +    DropStruct { field: 0 }
 +}
 +
 +unsafe fn unsafe_fn() -> i32 {
 +    0
 +}
 +
 +fn main() {
 +    let s = get_struct();
 +    let s2 = get_struct();
 +
 +    0;
 +    s2;
 +    Unit;
 +    Tuple(0);
 +    Struct { field: 0 };
 +    Struct { ..s };
 +    Union { a: 0 };
 +    Enum::Tuple(0);
 +    Enum::Struct { field: 0 };
 +    5 + 6;
 +    *&42;
 +    &6;
 +    (5, 6, 7);
 +    box 42;
 +    ..;
 +    5..;
 +    ..5;
 +    5..6;
 +    5..=6;
 +    [42, 55];
 +    [42, 55][1];
 +    (42, 55).1;
 +    [42; 55];
 +    [42; 55][13];
 +    let mut x = 0;
 +    || x += 5;
 +    let s: String = "foo".into();
 +    FooString { s: s };
++    let _unused = 1;
++    let _penguin = || println!("Some helpful closure");
++    let _duck = Struct { field: 0 };
++    let _cat = [2, 4, 6, 8][2];
 +
 +    #[allow(clippy::no_effect)]
 +    0;
 +
 +    // Do not warn
 +    get_number();
 +    unsafe { unsafe_fn() };
++    let _used = get_struct();
++    let _x = vec![1];
 +    DropUnit;
 +    DropStruct { field: 0 };
 +    DropTuple(0);
 +    DropEnum::Tuple(0);
 +    DropEnum::Struct { field: 0 };
 +}
index 6b24675ac2d42a8a934a85989394ca2920c44895,0000000000000000000000000000000000000000..a5dbc9fef455a4e71028acbfcfa78c4577dda5e5
mode 100644,000000..100644
--- /dev/null
@@@ -1,160 -1,0 +1,186 @@@
- error: aborting due to 26 previous errors
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:65:5
 +   |
 +LL |     0;
 +   |     ^^
 +   |
 +   = note: `-D clippy::no-effect` implied by `-D warnings`
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:66:5
 +   |
 +LL |     s2;
 +   |     ^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:67:5
 +   |
 +LL |     Unit;
 +   |     ^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:68:5
 +   |
 +LL |     Tuple(0);
 +   |     ^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:69:5
 +   |
 +LL |     Struct { field: 0 };
 +   |     ^^^^^^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:70:5
 +   |
 +LL |     Struct { ..s };
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:71:5
 +   |
 +LL |     Union { a: 0 };
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:72:5
 +   |
 +LL |     Enum::Tuple(0);
 +   |     ^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:73:5
 +   |
 +LL |     Enum::Struct { field: 0 };
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:74:5
 +   |
 +LL |     5 + 6;
 +   |     ^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:75:5
 +   |
 +LL |     *&42;
 +   |     ^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:76:5
 +   |
 +LL |     &6;
 +   |     ^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:77:5
 +   |
 +LL |     (5, 6, 7);
 +   |     ^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:78:5
 +   |
 +LL |     box 42;
 +   |     ^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:79:5
 +   |
 +LL |     ..;
 +   |     ^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:80:5
 +   |
 +LL |     5..;
 +   |     ^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:81:5
 +   |
 +LL |     ..5;
 +   |     ^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:82:5
 +   |
 +LL |     5..6;
 +   |     ^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:83:5
 +   |
 +LL |     5..=6;
 +   |     ^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:84:5
 +   |
 +LL |     [42, 55];
 +   |     ^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:85:5
 +   |
 +LL |     [42, 55][1];
 +   |     ^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:86:5
 +   |
 +LL |     (42, 55).1;
 +   |     ^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:87:5
 +   |
 +LL |     [42; 55];
 +   |     ^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:88:5
 +   |
 +LL |     [42; 55][13];
 +   |     ^^^^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:90:5
 +   |
 +LL |     || x += 5;
 +   |     ^^^^^^^^^^
 +
 +error: statement with no effect
 +  --> $DIR/no_effect.rs:92:5
 +   |
 +LL |     FooString { s: s };
 +   |     ^^^^^^^^^^^^^^^^^^^
 +
++error: binding to `_` prefixed variable with no side-effect
++  --> $DIR/no_effect.rs:93:5
++   |
++LL |     let _unused = 1;
++   |     ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
++
++error: binding to `_` prefixed variable with no side-effect
++  --> $DIR/no_effect.rs:94:5
++   |
++LL |     let _penguin = || println!("Some helpful closure");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: binding to `_` prefixed variable with no side-effect
++  --> $DIR/no_effect.rs:95:5
++   |
++LL |     let _duck = Struct { field: 0 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: binding to `_` prefixed variable with no side-effect
++  --> $DIR/no_effect.rs:96:5
++   |
++LL |     let _cat = [2, 4, 6, 8][2];
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: aborting due to 30 previous errors
 +
index a3ebe5d0703846bd09a131d94a0008700eba1b9c,0000000000000000000000000000000000000000..4077f1920a3837758a6f02f6f5b5ea8c6a2b078e
mode 100644,000000..100644
--- /dev/null
@@@ -1,151 -1,0 +1,150 @@@
- #![allow(clippy::redundant_closure)]
- #![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
 +// edition:2018
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
++#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    string.map_or((false, "hello"), |x| (true, x))
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = string.map_or(0, |s| s.len());
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.map_or(0, |mut s| {
 +        s += 1;
 +        s
 +    });
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    arg.map_or(13, |x| {
 +        let y = x * x;
 +        y * y
 +    })
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = arg.map_or_else(|| side_effect(), |x| x);
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = arg.map_or_else(|| {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    }, |x| x * x * x * x);
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = optional.map_or(5, |x| x + 2);
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
 +
 +    let _ = Some(0).map_or(0, |x| loop {
 +            if x == 0 {
 +                break x;
 +            }
 +        });
 +
 +    // #7576
 +    const fn _f(x: Option<u32>) -> u32 {
 +        // Don't lint, `map_or` isn't const
 +        if let Some(x) = x { x } else { 10 }
 +    }
 +
 +    // #5822
 +    let s = String::new();
 +    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        s.len()
 +    };
 +
 +    let s = String::new();
 +    // Lint, both branches immutably borrow `s`.
 +    let _ = Some(0).map_or_else(|| s.len(), |x| s.len() + x);
 +
 +    let s = String::new();
 +    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
 +    let _ = Some(0).map_or(1, |x| {
 +        let s = s;
 +        s.len() + x
 +    });
 +
 +    let s = Some(String::new());
 +    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
 +    let _ = if let Some(x) = &s {
 +        x.len()
 +    } else {
 +        let _s = s;
 +        10
 +    };
 +
 +    let mut s = Some(String::new());
 +    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
 +    let _ = if let Some(x) = &mut s {
 +        x.push_str("test");
 +        x.len()
 +    } else {
 +        let _s = &s;
 +        10
 +    };
 +
 +    async fn _f1(x: u32) -> u32 {
 +        x
 +    }
 +
 +    async fn _f2() {
 +        // Don't lint. `await` can't be moved into a closure.
 +        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
 +    }
 +}
index b11df3db60f57edfa56913f4af239b343259ecfa,0000000000000000000000000000000000000000..2f414e129d5a77c9952b88bd8e9efa5d0acc4249
mode 100644,000000..100644
--- /dev/null
@@@ -1,176 -1,0 +1,175 @@@
- #![allow(clippy::redundant_closure)]
- #![allow(clippy::ref_option_ref, clippy::equatable_if_let)]
 +// edition:2018
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
++#![allow(clippy::redundant_closure, clippy::ref_option_ref, clippy::equatable_if_let)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    if let Some(x) = string {
 +        (true, x)
 +    } else {
 +        (false, "hello")
 +    }
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = if let Some(s) = *string { s.len() } else { 0 };
 +    let _ = if let Some(s) = &num { s } else { &0 };
 +    let _ = if let Some(s) = &mut num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +    let _ = if let Some(ref s) = num { s } else { &0 };
 +    let _ = if let Some(mut s) = num {
 +        s += 1;
 +        s
 +    } else {
 +        0
 +    };
 +    let _ = if let Some(ref mut s) = num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    if let Some(x) = arg {
 +        let y = x * x;
 +        y * y
 +    } else {
 +        13
 +    }
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = if let Some(x) = arg {
 +        x
 +    } else {
 +        // map_or_else must be suggested
 +        side_effect()
 +    };
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = if let Some(x) = arg {
 +        x * x * x * x
 +    } else {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    };
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = if let Some(x) = optional { x + 2 } else { 5 };
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
 +
 +    let _ = if let Some(x) = Some(0) {
 +        loop {
 +            if x == 0 {
 +                break x;
 +            }
 +        }
 +    } else {
 +        0
 +    };
 +
 +    // #7576
 +    const fn _f(x: Option<u32>) -> u32 {
 +        // Don't lint, `map_or` isn't const
 +        if let Some(x) = x { x } else { 10 }
 +    }
 +
 +    // #5822
 +    let s = String::new();
 +    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        s.len()
 +    };
 +
 +    let s = String::new();
 +    // Lint, both branches immutably borrow `s`.
 +    let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
 +
 +    let s = String::new();
 +    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        1
 +    };
 +
 +    let s = Some(String::new());
 +    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
 +    let _ = if let Some(x) = &s {
 +        x.len()
 +    } else {
 +        let _s = s;
 +        10
 +    };
 +
 +    let mut s = Some(String::new());
 +    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
 +    let _ = if let Some(x) = &mut s {
 +        x.push_str("test");
 +        x.len()
 +    } else {
 +        let _s = &s;
 +        10
 +    };
 +
 +    async fn _f1(x: u32) -> u32 {
 +        x
 +    }
 +
 +    async fn _f2() {
 +        // Don't lint. `await` can't be moved into a closure.
 +        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
 +    }
 +}
index ed748ee8b39e4422c034f9a96982316dbb256745,0000000000000000000000000000000000000000..803d941c36df8b2d8aa80ca7fa381ee8c35c780a
mode 100644,000000..100644
--- /dev/null
@@@ -1,200 -1,0 +1,200 @@@
-   --> $DIR/option_if_let_else.rs:8:5
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:26:13
++  --> $DIR/option_if_let_else.rs:7:5
 +   |
 +LL | /     if let Some(x) = string {
 +LL | |         (true, x)
 +LL | |     } else {
 +LL | |         (false, "hello")
 +LL | |     }
 +   | |_____^ help: try: `string.map_or((false, "hello"), |x| (true, x))`
 +   |
 +   = note: `-D clippy::option-if-let-else` implied by `-D warnings`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:27:13
++  --> $DIR/option_if_let_else.rs:25:13
 +   |
 +LL |     let _ = if let Some(s) = *string { s.len() } else { 0 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.map_or(0, |s| s.len())`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:28:13
++  --> $DIR/option_if_let_else.rs:26:13
 +   |
 +LL |     let _ = if let Some(s) = &num { s } else { &0 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:34:13
++  --> $DIR/option_if_let_else.rs:27:13
 +   |
 +LL |       let _ = if let Some(s) = &mut num {
 +   |  _____________^
 +LL | |         *s += 1;
 +LL | |         s
 +LL | |     } else {
 +LL | |         &mut 0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = num.as_mut().map_or(&mut 0, |s| {
 +LL +         *s += 1;
 +LL +         s
 +LL ~     });
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:35:13
++  --> $DIR/option_if_let_else.rs:33:13
 +   |
 +LL |     let _ = if let Some(ref s) = num { s } else { &0 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `num.as_ref().map_or(&0, |s| s)`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:41:13
++  --> $DIR/option_if_let_else.rs:34:13
 +   |
 +LL |       let _ = if let Some(mut s) = num {
 +   |  _____________^
 +LL | |         s += 1;
 +LL | |         s
 +LL | |     } else {
 +LL | |         0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = num.map_or(0, |mut s| {
 +LL +         s += 1;
 +LL +         s
 +LL ~     });
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:50:5
++  --> $DIR/option_if_let_else.rs:40:13
 +   |
 +LL |       let _ = if let Some(ref mut s) = num {
 +   |  _____________^
 +LL | |         *s += 1;
 +LL | |         s
 +LL | |     } else {
 +LL | |         &mut 0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = num.as_mut().map_or(&mut 0, |s| {
 +LL +         *s += 1;
 +LL +         s
 +LL ~     });
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:63:13
++  --> $DIR/option_if_let_else.rs:49:5
 +   |
 +LL | /     if let Some(x) = arg {
 +LL | |         let y = x * x;
 +LL | |         y * y
 +LL | |     } else {
 +LL | |         13
 +LL | |     }
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     arg.map_or(13, |x| {
 +LL +         let y = x * x;
 +LL +         y * y
 +LL +     })
 +   |
 +
 +error: use Option::map_or_else instead of an if let/else
-   --> $DIR/option_if_let_else.rs:72:13
++  --> $DIR/option_if_let_else.rs:62:13
 +   |
 +LL |       let _ = if let Some(x) = arg {
 +   |  _____________^
 +LL | |         x
 +LL | |     } else {
 +LL | |         // map_or_else must be suggested
 +LL | |         side_effect()
 +LL | |     };
 +   | |_____^ help: try: `arg.map_or_else(|| side_effect(), |x| x)`
 +
 +error: use Option::map_or_else instead of an if let/else
-   --> $DIR/option_if_let_else.rs:101:13
++  --> $DIR/option_if_let_else.rs:71:13
 +   |
 +LL |       let _ = if let Some(x) = arg {
 +   |  _____________^
 +LL | |         x * x * x * x
 +LL | |     } else {
 +LL | |         let mut y = 1;
 +...  |
 +LL | |         y
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = arg.map_or_else(|| {
 +LL +         let mut y = 1;
 +LL +         y = (y + 2 / y) / 2;
 +LL +         y = (y + 2 / y) / 2;
 +LL +         y
 +LL ~     }, |x| x * x * x * x);
 +   |
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:110:13
++  --> $DIR/option_if_let_else.rs:100:13
 +   |
 +LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 +
 +error: use Option::map_or instead of an if let/else
-   --> $DIR/option_if_let_else.rs:138:13
++  --> $DIR/option_if_let_else.rs:109:13
 +   |
 +LL |       let _ = if let Some(x) = Some(0) {
 +   |  _____________^
 +LL | |         loop {
 +LL | |             if x == 0 {
 +LL | |                 break x;
 +...  |
 +LL | |         0
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = Some(0).map_or(0, |x| loop {
 +LL +             if x == 0 {
 +LL +                 break x;
 +LL +             }
 +LL ~         });
 +   |
 +
 +error: use Option::map_or_else instead of an if let/else
-   --> $DIR/option_if_let_else.rs:142:13
++  --> $DIR/option_if_let_else.rs:137:13
 +   |
 +LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or_else(|| s.len(), |x| s.len() + x)`
 +
 +error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:141:13
 +   |
 +LL |       let _ = if let Some(x) = Some(0) {
 +   |  _____________^
 +LL | |         let s = s;
 +LL | |         s.len() + x
 +LL | |     } else {
 +LL | |         1
 +LL | |     };
 +   | |_____^
 +   |
 +help: try
 +   |
 +LL ~     let _ = Some(0).map_or(1, |x| {
 +LL +         let s = s;
 +LL +         s.len() + x
 +LL ~     });
 +   |
 +
 +error: aborting due to 14 previous errors
 +
index 0b5746cb52270ed7e59d4126b7664151d1d39e95,0000000000000000000000000000000000000000..ccb2e5a302e91590feb0ba2af50ae467e00e8115
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,143 @@@
 +// run-rustfix
 +#![allow(unreachable_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    a?;
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        (self.opt)?;
 +
 +        self.opt?;
 +
 +        let _ = Some(self.opt?);
 +
 +        let _ = self.opt?;
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = self.opt.as_ref()?;
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = self.opt?;
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    f()?;
 +
 +    Some(0)
 +}
 +
++fn result_func(x: Result<i32, &str>) -> Result<i32, &str> {
++    let _ = x?;
++
++    x?;
++
++    // No warning
++    let y = if let Ok(x) = x {
++        x
++    } else {
++        return Err("some error");
++    };
++
++    Ok(y)
++}
++
 +fn main() {
 +    some_func(Some(42));
 +    some_func(None);
 +    some_other_func(Some(42));
 +
 +    let copy_struct = CopyStruct { opt: Some(54) };
 +    copy_struct.func();
 +
 +    let move_struct = MoveStruct {
 +        opt: Some(vec![42, 1337]),
 +    };
 +    move_struct.ref_func();
 +    move_struct.clone().mov_func_reuse();
 +    move_struct.mov_func_no_use();
 +
 +    let so = SeemsOption::Some(45);
 +    returns_something_similar_to_option(so);
 +
 +    func();
++
++    let _ = result_func(Ok(42));
 +}
index 0f0825c9334679d185e9bc711dc86f05df0466d8,0000000000000000000000000000000000000000..ca3722371f524b53b27ca94f15fd1a46e7d867ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,156 -1,0 +1,175 @@@
 +// run-rustfix
 +#![allow(unreachable_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    }
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        if (self.opt).is_none() {
 +            return None;
 +        }
 +
 +        if self.opt.is_none() {
 +            return None
 +        }
 +
 +        let _ = if self.opt.is_none() {
 +            return None;
 +        } else {
 +            self.opt
 +        };
 +
 +        let _ = if let Some(x) = self.opt {
 +            x
 +        } else {
 +            return None;
 +        };
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = if let Some(ref v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = if let Some(v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    if f().is_none() {
 +        return None;
 +    }
 +
 +    Some(0)
 +}
 +
++fn result_func(x: Result<i32, &str>) -> Result<i32, &str> {
++    let _ = if let Ok(x) = x { x } else { return x };
++
++    if x.is_err() {
++        return x;
++    }
++
++    // No warning
++    let y = if let Ok(x) = x {
++        x
++    } else {
++        return Err("some error");
++    };
++
++    Ok(y)
++}
++
 +fn main() {
 +    some_func(Some(42));
 +    some_func(None);
 +    some_other_func(Some(42));
 +
 +    let copy_struct = CopyStruct { opt: Some(54) };
 +    copy_struct.func();
 +
 +    let move_struct = MoveStruct {
 +        opt: Some(vec![42, 1337]),
 +    };
 +    move_struct.ref_func();
 +    move_struct.clone().mov_func_reuse();
 +    move_struct.mov_func_no_use();
 +
 +    let so = SeemsOption::Some(45);
 +    returns_something_similar_to_option(so);
 +
 +    func();
++
++    let _ = result_func(Ok(42));
 +}
index 6f330cfa385dddeaab2c9473fb9c684f381aea27,0000000000000000000000000000000000000000..161588cb73cba06797561917e4ee5fe6fa3710d9
mode 100644,000000..100644
--- /dev/null
@@@ -1,104 -1,0 +1,118 @@@
- error: aborting due to 11 previous errors
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:6:5
 +   |
 +LL | /     if a.is_none() {
 +LL | |         return None;
 +LL | |     }
 +   | |_____^ help: replace it with: `a?;`
 +   |
 +   = note: `-D clippy::question-mark` implied by `-D warnings`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:51:9
 +   |
 +LL | /         if (self.opt).is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `(self.opt)?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:55:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:59:17
 +   |
 +LL |           let _ = if self.opt.is_none() {
 +   |  _________________^
 +LL | |             return None;
 +LL | |         } else {
 +LL | |             self.opt
 +LL | |         };
 +   | |_________^ help: replace it with: `Some(self.opt?)`
 +
 +error: this if-let-else may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:65:17
 +   |
 +LL |           let _ = if let Some(x) = self.opt {
 +   |  _________________^
 +LL | |             x
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt?`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:82:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:90:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:98:9
 +   |
 +LL | /         if self.opt.is_none() {
 +LL | |             return None;
 +LL | |         }
 +   | |_________^ help: replace it with: `self.opt.as_ref()?;`
 +
 +error: this if-let-else may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:105:26
 +   |
 +LL |           let v: &Vec<_> = if let Some(ref v) = self.opt {
 +   |  __________________________^
 +LL | |             v
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt.as_ref()?`
 +
 +error: this if-let-else may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:115:17
 +   |
 +LL |           let v = if let Some(v) = self.opt {
 +   |  _________________^
 +LL | |             v
 +LL | |         } else {
 +LL | |             return None;
 +LL | |         };
 +   | |_________^ help: replace it with: `self.opt?`
 +
 +error: this block may be rewritten with the `?` operator
 +  --> $DIR/question_mark.rs:130:5
 +   |
 +LL | /     if f().is_none() {
 +LL | |         return None;
 +LL | |     }
 +   | |_____^ help: replace it with: `f()?;`
 +
++error: this if-let-else may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:138:13
++   |
++LL |     let _ = if let Ok(x) = x { x } else { return x };
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
++
++error: this block may be rewritten with the `?` operator
++  --> $DIR/question_mark.rs:140:5
++   |
++LL | /     if x.is_err() {
++LL | |         return x;
++LL | |     }
++   | |_____^ help: replace it with: `x?;`
++
++error: aborting due to 13 previous errors
 +
index 9644a23296831bf1a67092feecba921180aa3ba7,0000000000000000000000000000000000000000..7a45f1b18d4af2c8d920676b4f96b755083867d1
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,112 @@@
 +#![warn(clippy::semicolon_if_nothing_returned)]
 +#![allow(clippy::redundant_closure)]
 +#![feature(label_break_value)]
 +
 +fn get_unit() {}
 +
 +// the functions below trigger the lint
 +fn main() {
 +    println!("Hello")
 +}
 +
 +fn hello() {
 +    get_unit()
 +}
 +
 +fn basic101(x: i32) {
 +    let y: i32;
 +    y = x + 1
 +}
 +
 +#[rustfmt::skip]
 +fn closure_error() {
 +    let _d = || {
 +        hello()
 +    };
 +}
 +
 +#[rustfmt::skip]
 +fn unsafe_checks_error() {
 +    use std::mem::MaybeUninit;
 +    use std::ptr;
 +
 +    let mut s = MaybeUninit::<String>::uninit();
 +    let _d = || unsafe {
 +        ptr::drop_in_place(s.as_mut_ptr())
 +    };
 +}
 +
 +// this is fine
 +fn print_sum(a: i32, b: i32) {
 +    println!("{}", a + b);
 +    assert_eq!(true, false);
 +}
 +
 +fn foo(x: i32) {
 +    let y: i32;
 +    if x < 1 {
 +        y = 4;
 +    } else {
 +        y = 5;
 +    }
 +}
 +
 +fn bar(x: i32) {
 +    let y: i32;
 +    match x {
 +        1 => y = 4,
 +        _ => y = 32,
 +    }
 +}
 +
 +fn foobar(x: i32) {
 +    let y: i32;
 +    'label: {
 +        y = x + 1;
 +    }
 +}
 +
 +fn loop_test(x: i32) {
 +    let y: i32;
 +    for &ext in &["stdout", "stderr", "fixed"] {
 +        println!("{}", ext);
 +    }
 +}
 +
 +fn closure() {
 +    let _d = || hello();
 +}
 +
 +#[rustfmt::skip]
 +fn closure_block() {
 +    let _d = || { hello() };
 +}
 +
 +unsafe fn some_unsafe_op() {}
 +unsafe fn some_other_unsafe_fn() {}
 +
 +fn do_something() {
 +    unsafe { some_unsafe_op() };
 +
 +    unsafe { some_other_unsafe_fn() };
 +}
 +
 +fn unsafe_checks() {
 +    use std::mem::MaybeUninit;
 +    use std::ptr;
 +
 +    let mut s = MaybeUninit::<String>::uninit();
 +    let _d = || unsafe { ptr::drop_in_place(s.as_mut_ptr()) };
 +}
++
++// Issue #7768
++#[rustfmt::skip]
++fn macro_with_semicolon() {
++    macro_rules! repro {
++        () => {
++            while false {
++            }
++        };
++    }
++    repro!();
++}
index 02e838456d0b574559f34404e79f22ffa158100e,0000000000000000000000000000000000000000..55caef59f7f683f5509412a2065391fb211f4c03
mode 100644,000000..100644
--- /dev/null
@@@ -1,77 -1,0 +1,82 @@@
 +#![warn(clippy::shadow_same, clippy::shadow_reuse, clippy::shadow_unrelated)]
 +
 +fn shadow_same() {
 +    let x = 1;
 +    let x = x;
 +    let mut x = &x;
 +    let x = &mut x;
 +    let x = *x;
 +}
 +
 +fn shadow_reuse() -> Option<()> {
 +    let x = ([[0]], ());
 +    let x = x.0;
 +    let x = x[0];
 +    let [x] = x;
 +    let x = Some(x);
 +    let x = foo(x);
 +    let x = || x;
 +    let x = Some(1).map(|_| x)?;
++    let y = 1;
++    let y = match y {
++        1 => 2,
++        _ => 3,
++    };
 +    None
 +}
 +
 +fn shadow_unrelated() {
 +    let x = 1;
 +    let x = 2;
 +}
 +
 +fn syntax() {
 +    fn f(x: u32) {
 +        let x = 1;
 +    }
 +    let x = 1;
 +    match Some(1) {
 +        Some(1) => {},
 +        Some(x) => {
 +            let x = 1;
 +        },
 +        _ => {},
 +    }
 +    if let Some(x) = Some(1) {}
 +    while let Some(x) = Some(1) {}
 +    let _ = |[x]: [u32; 1]| {
 +        let x = 1;
 +    };
 +}
 +
 +fn negative() {
 +    match Some(1) {
 +        Some(x) if x == 1 => {},
 +        Some(x) => {},
 +        None => {},
 +    }
 +    match [None, Some(1)] {
 +        [Some(x), None] | [None, Some(x)] => {},
 +        _ => {},
 +    }
 +    if let Some(x) = Some(1) {
 +        let y = 1;
 +    } else {
 +        let x = 1;
 +        let y = 1;
 +    }
 +    let x = 1;
 +    #[allow(clippy::shadow_unrelated)]
 +    let x = 1;
 +}
 +
 +fn foo<T>(_: T) {}
 +
 +fn question_mark() -> Option<()> {
 +    let val = 1;
 +    // `?` expands with a `val` binding
 +    None?;
 +    None
 +}
 +
 +fn main() {}
index 8b60e072c9342c9c763dc6b8de1740cef3f99dac,0000000000000000000000000000000000000000..feed6e1ba8b8660ff2f73dc71a956fb135c62235
mode 100644,000000..100644
--- /dev/null
@@@ -1,233 -1,0 +1,245 @@@
- error: `x` is shadowed by `x.0` which reuses the original value
 +error: `x` is shadowed by itself in `x`
 +  --> $DIR/shadow.rs:5:9
 +   |
 +LL |     let x = x;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-same` implied by `-D warnings`
 +note: previous binding is here
 +  --> $DIR/shadow.rs:4:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `mut x` is shadowed by itself in `&x`
 +  --> $DIR/shadow.rs:6:13
 +   |
 +LL |     let mut x = &x;
 +   |             ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:5:9
 +   |
 +LL |     let x = x;
 +   |         ^
 +
 +error: `x` is shadowed by itself in `&mut x`
 +  --> $DIR/shadow.rs:7:9
 +   |
 +LL |     let x = &mut x;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:6:9
 +   |
 +LL |     let mut x = &x;
 +   |         ^^^^^
 +
 +error: `x` is shadowed by itself in `*x`
 +  --> $DIR/shadow.rs:8:9
 +   |
 +LL |     let x = *x;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:7:9
 +   |
 +LL |     let x = &mut x;
 +   |         ^
 +
- error: `x` is shadowed by `x[0]` which reuses the original value
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:13:9
 +   |
 +LL |     let x = x.0;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-reuse` implied by `-D warnings`
 +note: previous binding is here
 +  --> $DIR/shadow.rs:12:9
 +   |
 +LL |     let x = ([[0]], ());
 +   |         ^
 +
- error: `x` is shadowed by `x` which reuses the original value
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:14:9
 +   |
 +LL |     let x = x[0];
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:13:9
 +   |
 +LL |     let x = x.0;
 +   |         ^
 +
- error: `x` is shadowed by `Some(x)` which reuses the original value
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:15:10
 +   |
 +LL |     let [x] = x;
 +   |          ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:14:9
 +   |
 +LL |     let x = x[0];
 +   |         ^
 +
- error: `x` is shadowed by `foo(x)` which reuses the original value
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:16:9
 +   |
 +LL |     let x = Some(x);
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:15:10
 +   |
 +LL |     let [x] = x;
 +   |          ^
 +
- error: `x` is shadowed by `|| x` which reuses the original value
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:17:9
 +   |
 +LL |     let x = foo(x);
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:16:9
 +   |
 +LL |     let x = Some(x);
 +   |         ^
 +
- error: `x` is shadowed by `Some(1).map(|_| x)?` which reuses the original value
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:18:9
 +   |
 +LL |     let x = || x;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:17:9
 +   |
 +LL |     let x = foo(x);
 +   |         ^
 +
-   --> $DIR/shadow.rs:25:9
++error: `x` is shadowed
 +  --> $DIR/shadow.rs:19:9
 +   |
 +LL |     let x = Some(1).map(|_| x)?;
 +   |         ^
 +   |
 +note: previous binding is here
 +  --> $DIR/shadow.rs:18:9
 +   |
 +LL |     let x = || x;
 +   |         ^
 +
++error: `y` is shadowed
++  --> $DIR/shadow.rs:21:9
++   |
++LL |     let y = match y {
++   |         ^
++   |
++note: previous binding is here
++  --> $DIR/shadow.rs:20:9
++   |
++LL |     let y = 1;
++   |         ^
++
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:24:9
++  --> $DIR/shadow.rs:30:9
 +   |
 +LL |     let x = 2;
 +   |         ^
 +   |
 +   = note: `-D clippy::shadow-unrelated` implied by `-D warnings`
 +note: previous binding is here
-   --> $DIR/shadow.rs:30:13
++  --> $DIR/shadow.rs:29:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:29:10
++  --> $DIR/shadow.rs:35:13
 +   |
 +LL |         let x = 1;
 +   |             ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:35:14
++  --> $DIR/shadow.rs:34:10
 +   |
 +LL |     fn f(x: u32) {
 +   |          ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:32:9
++  --> $DIR/shadow.rs:40:14
 +   |
 +LL |         Some(x) => {
 +   |              ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:36:17
++  --> $DIR/shadow.rs:37:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:35:14
++  --> $DIR/shadow.rs:41:17
 +   |
 +LL |             let x = 1;
 +   |                 ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:40:17
++  --> $DIR/shadow.rs:40:14
 +   |
 +LL |         Some(x) => {
 +   |              ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:32:9
++  --> $DIR/shadow.rs:45:17
 +   |
 +LL |     if let Some(x) = Some(1) {}
 +   |                 ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:41:20
++  --> $DIR/shadow.rs:37:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:32:9
++  --> $DIR/shadow.rs:46:20
 +   |
 +LL |     while let Some(x) = Some(1) {}
 +   |                    ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:42:15
++  --> $DIR/shadow.rs:37:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:32:9
++  --> $DIR/shadow.rs:47:15
 +   |
 +LL |     let _ = |[x]: [u32; 1]| {
 +   |               ^
 +   |
 +note: previous binding is here
-   --> $DIR/shadow.rs:43:13
++  --> $DIR/shadow.rs:37:9
 +   |
 +LL |     let x = 1;
 +   |         ^
 +
 +error: `x` shadows a previous, unrelated binding
-   --> $DIR/shadow.rs:42:15
++  --> $DIR/shadow.rs:48:13
 +   |
 +LL |         let x = 1;
 +   |             ^
 +   |
 +note: previous binding is here
- error: aborting due to 19 previous errors
++  --> $DIR/shadow.rs:47:15
 +   |
 +LL |     let _ = |[x]: [u32; 1]| {
 +   |               ^
 +
++error: aborting due to 20 previous errors
 +
index eb8105c6b6da0f28a50ee521f4550e065a96de55,0000000000000000000000000000000000000000..3ccdcd1117b5a5d47dcf87f95b30acbdba631e13
mode 100644,000000..100644
--- /dev/null
@@@ -1,69 -1,0 +1,69 @@@
- #![allow(clippy::inherent_to_string_shadow_display)]
 +#![warn(clippy::to_string_in_display)]
++#![allow(clippy::inherent_to_string_shadow_display, clippy::to_string_in_format_args)]
 +
 +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 {
 +    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"),
 +        }
 +    }
 +}
 +
 +fn main() {
 +    let a = A;
 +    a.to_string();
 +    a.fmt();
 +    fmt(a);
 +
 +    let c = C;
 +    c.to_string();
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..501c9eb7651f8320b7e0d914de982b879ab382a3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,186 @@@
++#![warn(clippy::trailing_empty_array)]
++#![feature(const_generics_defaults)]
++
++// Do lint:
++
++struct RarelyUseful {
++    field: i32,
++    last: [usize; 0],
++}
++
++struct OnlyField {
++    first_and_last: [usize; 0],
++}
++
++struct GenericArrayType<T> {
++    field: i32,
++    last: [T; 0],
++}
++
++#[must_use]
++struct OnlyAnotherAttribute {
++    field: i32,
++    last: [usize; 0],
++}
++
++#[derive(Debug)]
++struct OnlyADeriveAttribute {
++    field: i32,
++    last: [usize; 0],
++}
++
++const ZERO: usize = 0;
++struct ZeroSizedWithConst {
++    field: i32,
++    last: [usize; ZERO],
++}
++
++#[allow(clippy::eq_op)]
++const fn compute_zero() -> usize {
++    (4 + 6) - (2 * 5)
++}
++struct ZeroSizedWithConstFunction {
++    field: i32,
++    last: [usize; compute_zero()],
++}
++
++const fn compute_zero_from_arg(x: usize) -> usize {
++    x - 1
++}
++struct ZeroSizedWithConstFunction2 {
++    field: i32,
++    last: [usize; compute_zero_from_arg(1)],
++}
++
++struct ZeroSizedArrayWrapper([usize; 0]);
++
++struct TupleStruct(i32, [usize; 0]);
++
++struct LotsOfFields {
++    f1: u32,
++    f2: u32,
++    f3: u32,
++    f4: u32,
++    f5: u32,
++    f6: u32,
++    f7: u32,
++    f8: u32,
++    f9: u32,
++    f10: u32,
++    f11: u32,
++    f12: u32,
++    f13: u32,
++    f14: u32,
++    f15: u32,
++    f16: u32,
++    last: [usize; 0],
++}
++
++// Don't lint
++
++#[repr(C)]
++struct GoodReason {
++    field: i32,
++    last: [usize; 0],
++}
++
++#[repr(C)]
++struct OnlyFieldWithReprC {
++    first_and_last: [usize; 0],
++}
++
++struct NonZeroSizedArray {
++    field: i32,
++    last: [usize; 1],
++}
++
++struct NotLastField {
++    f1: u32,
++    zero_sized: [usize; 0],
++    last: i32,
++}
++
++const ONE: usize = 1;
++struct NonZeroSizedWithConst {
++    field: i32,
++    last: [usize; ONE],
++}
++
++#[derive(Debug)]
++#[repr(C)]
++struct AlsoADeriveAttribute {
++    field: i32,
++    last: [usize; 0],
++}
++
++#[must_use]
++#[repr(C)]
++struct AlsoAnotherAttribute {
++    field: i32,
++    last: [usize; 0],
++}
++
++#[repr(packed)]
++struct ReprPacked {
++    field: i32,
++    last: [usize; 0],
++}
++
++#[repr(C, packed)]
++struct ReprCPacked {
++    field: i32,
++    last: [usize; 0],
++}
++
++#[repr(align(64))]
++struct ReprAlign {
++    field: i32,
++    last: [usize; 0],
++}
++#[repr(C, align(64))]
++struct ReprCAlign {
++    field: i32,
++    last: [usize; 0],
++}
++
++// NOTE: because of https://doc.rust-lang.org/stable/reference/type-layout.html#primitive-representation-of-enums-with-fields and I'm not sure when in the compilation pipeline that would happen
++#[repr(C)]
++enum DontLintAnonymousStructsFromDesuraging {
++    A(u32),
++    B(f32, [u64; 0]),
++    C { x: u32, y: [u64; 0] },
++}
++
++#[repr(C)]
++struct TupleStructReprC(i32, [usize; 0]);
++
++type NamedTuple = (i32, [usize; 0]);
++
++#[rustfmt::skip] // [rustfmt#4995](https://github.com/rust-lang/rustfmt/issues/4995)
++struct ConstParamZeroDefault<const N: usize = 0> {
++    field: i32,
++    last: [usize; N],
++}
++
++struct ConstParamNoDefault<const N: usize> {
++    field: i32,
++    last: [usize; N],
++}
++
++#[rustfmt::skip] 
++struct ConstParamNonZeroDefault<const N: usize = 1> {
++    field: i32,
++    last: [usize; N],
++}
++
++struct TwoGenericParams<T, const N: usize> {
++    field: i32,
++    last: [T; N],
++}
++
++type A = ConstParamZeroDefault;
++type B = ConstParamZeroDefault<0>;
++type C = ConstParamNoDefault<0>;
++type D = ConstParamNonZeroDefault<0>;
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d88aa0504b537b39f2176d4346a261aa0be1e9f4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,120 @@@
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:6:1
++   |
++LL | / struct RarelyUseful {
++LL | |     field: i32,
++LL | |     last: [usize; 0],
++LL | | }
++   | |_^
++   |
++   = note: `-D clippy::trailing-empty-array` implied by `-D warnings`
++   = help: consider annotating `RarelyUseful` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:11:1
++   |
++LL | / struct OnlyField {
++LL | |     first_and_last: [usize; 0],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `OnlyField` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:15:1
++   |
++LL | / struct GenericArrayType<T> {
++LL | |     field: i32,
++LL | |     last: [T; 0],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `GenericArrayType` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:21:1
++   |
++LL | / struct OnlyAnotherAttribute {
++LL | |     field: i32,
++LL | |     last: [usize; 0],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `OnlyAnotherAttribute` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:27:1
++   |
++LL | / struct OnlyADeriveAttribute {
++LL | |     field: i32,
++LL | |     last: [usize; 0],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `OnlyADeriveAttribute` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:33:1
++   |
++LL | / struct ZeroSizedWithConst {
++LL | |     field: i32,
++LL | |     last: [usize; ZERO],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `ZeroSizedWithConst` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:42:1
++   |
++LL | / struct ZeroSizedWithConstFunction {
++LL | |     field: i32,
++LL | |     last: [usize; compute_zero()],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `ZeroSizedWithConstFunction` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:50:1
++   |
++LL | / struct ZeroSizedWithConstFunction2 {
++LL | |     field: i32,
++LL | |     last: [usize; compute_zero_from_arg(1)],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `ZeroSizedWithConstFunction2` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:55:1
++   |
++LL | struct ZeroSizedArrayWrapper([usize; 0]);
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider annotating `ZeroSizedArrayWrapper` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:57:1
++   |
++LL | struct TupleStruct(i32, [usize; 0]);
++   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider annotating `TupleStruct` with `#[repr(C)]` or another `repr` attribute
++
++error: trailing zero-sized array in a struct which is not marked with a `repr` attribute
++  --> $DIR/trailing_empty_array.rs:59:1
++   |
++LL | / struct LotsOfFields {
++LL | |     f1: u32,
++LL | |     f2: u32,
++LL | |     f3: u32,
++...  |
++LL | |     last: [usize; 0],
++LL | | }
++   | |_^
++   |
++   = help: consider annotating `LotsOfFields` with `#[repr(C)]` or another `repr` attribute
++
++error: aborting due to 11 previous errors
++
index bce4c81b78aa104709dd0a238f923a2f44de6451,0000000000000000000000000000000000000000..6a7037d8f3826e962594836a03ee0ed9ffce9a7f
mode 100644,000000..100644
--- /dev/null
@@@ -1,111 -1,0 +1,138 @@@
 +#![allow(dead_code)]
 +
 +extern crate core;
 +
 +use std::mem::transmute as my_transmute;
 +use std::vec::Vec as MyVec;
 +
 +fn my_int() -> Usize {
 +    Usize(42)
 +}
 +
 +fn my_vec() -> MyVec<i32> {
 +    vec![]
 +}
 +
 +#[allow(clippy::needless_lifetimes, clippy::transmute_ptr_to_ptr)]
 +#[warn(clippy::useless_transmute)]
 +unsafe fn _generic<'a, T, U: 'a>(t: &'a T) {
 +    let _: &'a T = core::intrinsics::transmute(t);
 +
 +    let _: &'a U = core::intrinsics::transmute(t);
 +
 +    let _: *const T = core::intrinsics::transmute(t);
 +
 +    let _: *mut T = core::intrinsics::transmute(t);
 +
 +    let _: *const U = core::intrinsics::transmute(t);
 +}
 +
 +#[warn(clippy::useless_transmute)]
 +fn useless() {
 +    unsafe {
 +        let _: Vec<i32> = core::intrinsics::transmute(my_vec());
 +
 +        let _: Vec<i32> = core::mem::transmute(my_vec());
 +
 +        let _: Vec<i32> = std::intrinsics::transmute(my_vec());
 +
 +        let _: Vec<i32> = std::mem::transmute(my_vec());
 +
 +        let _: Vec<i32> = my_transmute(my_vec());
 +
 +        let _: *const usize = std::mem::transmute(5_isize);
 +
 +        let _ = 5_isize as *const usize;
 +
 +        let _: *const usize = std::mem::transmute(1 + 1usize);
 +
 +        let _ = (1 + 1_usize) as *const usize;
 +    }
 +}
 +
 +struct Usize(usize);
 +
 +#[warn(clippy::crosspointer_transmute)]
 +fn crosspointer() {
 +    let mut int: Usize = Usize(0);
 +    let int_const_ptr: *const Usize = &int as *const Usize;
 +    let int_mut_ptr: *mut Usize = &mut int as *mut Usize;
 +
 +    unsafe {
 +        let _: Usize = core::intrinsics::transmute(int_const_ptr);
 +
 +        let _: Usize = core::intrinsics::transmute(int_mut_ptr);
 +
 +        let _: *const Usize = core::intrinsics::transmute(my_int());
 +
 +        let _: *mut Usize = core::intrinsics::transmute(my_int());
 +    }
 +}
 +
 +#[warn(clippy::transmute_int_to_char)]
 +fn int_to_char() {
 +    let _: char = unsafe { std::mem::transmute(0_u32) };
 +    let _: char = unsafe { std::mem::transmute(0_i32) };
 +}
 +
 +#[warn(clippy::transmute_int_to_bool)]
 +fn int_to_bool() {
 +    let _: bool = unsafe { std::mem::transmute(0_u8) };
 +}
 +
 +#[warn(clippy::transmute_int_to_float)]
 +mod int_to_float {
 +    fn test() {
 +        let _: f32 = unsafe { std::mem::transmute(0_u32) };
 +        let _: f32 = unsafe { std::mem::transmute(0_i32) };
 +        let _: f64 = unsafe { std::mem::transmute(0_u64) };
 +        let _: f64 = unsafe { std::mem::transmute(0_i64) };
 +    }
 +
 +    mod issue_5747 {
 +        const VALUE32: f32 = unsafe { std::mem::transmute(0_u32) };
 +        const VALUE64: f64 = unsafe { std::mem::transmute(0_i64) };
 +
 +        const fn from_bits_32(v: i32) -> f32 {
 +            unsafe { std::mem::transmute(v) }
 +        }
 +
 +        const fn from_bits_64(v: u64) -> f64 {
 +            unsafe { std::mem::transmute(v) }
 +        }
 +    }
 +}
 +
++mod num_to_bytes {
++    fn test() {
++        unsafe {
++            let _: [u8; 1] = std::mem::transmute(0u8);
++            let _: [u8; 4] = std::mem::transmute(0u32);
++            let _: [u8; 16] = std::mem::transmute(0u128);
++            let _: [u8; 1] = std::mem::transmute(0i8);
++            let _: [u8; 4] = std::mem::transmute(0i32);
++            let _: [u8; 16] = std::mem::transmute(0i128);
++            let _: [u8; 4] = std::mem::transmute(0.0f32);
++            let _: [u8; 8] = std::mem::transmute(0.0f64);
++        }
++    }
++    const fn test_const() {
++        unsafe {
++            let _: [u8; 1] = std::mem::transmute(0u8);
++            let _: [u8; 4] = std::mem::transmute(0u32);
++            let _: [u8; 16] = std::mem::transmute(0u128);
++            let _: [u8; 1] = std::mem::transmute(0i8);
++            let _: [u8; 4] = std::mem::transmute(0i32);
++            let _: [u8; 16] = std::mem::transmute(0i128);
++            let _: [u8; 4] = std::mem::transmute(0.0f32);
++            let _: [u8; 8] = std::mem::transmute(0.0f64);
++        }
++    }
++}
++
 +fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
 +    let _: &str = unsafe { std::mem::transmute(b) };
 +    let _: &mut str = unsafe { std::mem::transmute(mb) };
 +}
 +
 +fn main() {}
index e31accb982af39e3d502d9cdb94f94d2633bccee,0000000000000000000000000000000000000000..86537153e322897de816064f7b80041c88418c0d
mode 100644,000000..100644
--- /dev/null
@@@ -1,158 -1,0 +1,244 @@@
-   --> $DIR/transmute.rs:107:28
 +error: transmute from a type (`&T`) to itself
 +  --> $DIR/transmute.rs:19:20
 +   |
 +LL |     let _: &'a T = core::intrinsics::transmute(t);
 +   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::useless-transmute` implied by `-D warnings`
 +
 +error: transmute from a reference to a pointer
 +  --> $DIR/transmute.rs:23:23
 +   |
 +LL |     let _: *const T = core::intrinsics::transmute(t);
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
 +
 +error: transmute from a reference to a pointer
 +  --> $DIR/transmute.rs:25:21
 +   |
 +LL |     let _: *mut T = core::intrinsics::transmute(t);
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
 +
 +error: transmute from a reference to a pointer
 +  --> $DIR/transmute.rs:27:23
 +   |
 +LL |     let _: *const U = core::intrinsics::transmute(t);
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
 +  --> $DIR/transmute.rs:33:27
 +   |
 +LL |         let _: Vec<i32> = core::intrinsics::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
 +  --> $DIR/transmute.rs:35:27
 +   |
 +LL |         let _: Vec<i32> = core::mem::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
 +  --> $DIR/transmute.rs:37:27
 +   |
 +LL |         let _: Vec<i32> = std::intrinsics::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
 +  --> $DIR/transmute.rs:39:27
 +   |
 +LL |         let _: Vec<i32> = std::mem::transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`std::vec::Vec<i32>`) to itself
 +  --> $DIR/transmute.rs:41:27
 +   |
 +LL |         let _: Vec<i32> = my_transmute(my_vec());
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from an integer to a pointer
 +  --> $DIR/transmute.rs:43:31
 +   |
 +LL |         let _: *const usize = std::mem::transmute(5_isize);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
 +
 +error: transmute from an integer to a pointer
 +  --> $DIR/transmute.rs:47:31
 +   |
 +LL |         let _: *const usize = std::mem::transmute(1 + 1usize);
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
 +
 +error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
 +  --> $DIR/transmute.rs:62:24
 +   |
 +LL |         let _: Usize = core::intrinsics::transmute(int_const_ptr);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::crosspointer-transmute` implied by `-D warnings`
 +
 +error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
 +  --> $DIR/transmute.rs:64:24
 +   |
 +LL |         let _: Usize = core::intrinsics::transmute(int_mut_ptr);
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
 +  --> $DIR/transmute.rs:66:31
 +   |
 +LL |         let _: *const Usize = core::intrinsics::transmute(my_int());
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
 +  --> $DIR/transmute.rs:68:29
 +   |
 +LL |         let _: *mut Usize = core::intrinsics::transmute(my_int());
 +   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from a `u32` to a `char`
 +  --> $DIR/transmute.rs:74:28
 +   |
 +LL |     let _: char = unsafe { std::mem::transmute(0_u32) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_u32).unwrap()`
 +   |
 +   = note: `-D clippy::transmute-int-to-char` implied by `-D warnings`
 +
 +error: transmute from a `i32` to a `char`
 +  --> $DIR/transmute.rs:75:28
 +   |
 +LL |     let _: char = unsafe { std::mem::transmute(0_i32) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
 +
 +error: transmute from a `u8` to a `bool`
 +  --> $DIR/transmute.rs:80:28
 +   |
 +LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
 +   |
 +   = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
 +
 +error: transmute from a `u32` to a `f32`
 +  --> $DIR/transmute.rs:86:31
 +   |
 +LL |         let _: f32 = unsafe { std::mem::transmute(0_u32) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
 +   |
 +   = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
 +
 +error: transmute from a `i32` to a `f32`
 +  --> $DIR/transmute.rs:87:31
 +   |
 +LL |         let _: f32 = unsafe { std::mem::transmute(0_i32) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
 +
 +error: transmute from a `u64` to a `f64`
 +  --> $DIR/transmute.rs:88:31
 +   |
 +LL |         let _: f64 = unsafe { std::mem::transmute(0_u64) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
 +
 +error: transmute from a `i64` to a `f64`
 +  --> $DIR/transmute.rs:89:31
 +   |
 +LL |         let _: f64 = unsafe { std::mem::transmute(0_i64) };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
 +
++error: transmute from a `u8` to a `[u8; 1]`
++  --> $DIR/transmute.rs:109:30
++   |
++LL |             let _: [u8; 1] = std::mem::transmute(0u8);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
++   |
++   = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings`
++
++error: transmute from a `u32` to a `[u8; 4]`
++  --> $DIR/transmute.rs:110:30
++   |
++LL |             let _: [u8; 4] = std::mem::transmute(0u32);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
++
++error: transmute from a `u128` to a `[u8; 16]`
++  --> $DIR/transmute.rs:111:31
++   |
++LL |             let _: [u8; 16] = std::mem::transmute(0u128);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
++
++error: transmute from a `i8` to a `[u8; 1]`
++  --> $DIR/transmute.rs:112:30
++   |
++LL |             let _: [u8; 1] = std::mem::transmute(0i8);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
++
++error: transmute from a `i32` to a `[u8; 4]`
++  --> $DIR/transmute.rs:113:30
++   |
++LL |             let _: [u8; 4] = std::mem::transmute(0i32);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
++
++error: transmute from a `i128` to a `[u8; 16]`
++  --> $DIR/transmute.rs:114:31
++   |
++LL |             let _: [u8; 16] = std::mem::transmute(0i128);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
++
++error: transmute from a `f32` to a `[u8; 4]`
++  --> $DIR/transmute.rs:115:30
++   |
++LL |             let _: [u8; 4] = std::mem::transmute(0.0f32);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()`
++
++error: transmute from a `f64` to a `[u8; 8]`
++  --> $DIR/transmute.rs:116:30
++   |
++LL |             let _: [u8; 8] = std::mem::transmute(0.0f64);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()`
++
++error: transmute from a `u8` to a `[u8; 1]`
++  --> $DIR/transmute.rs:121:30
++   |
++LL |             let _: [u8; 1] = std::mem::transmute(0u8);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
++
++error: transmute from a `u32` to a `[u8; 4]`
++  --> $DIR/transmute.rs:122:30
++   |
++LL |             let _: [u8; 4] = std::mem::transmute(0u32);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
++
++error: transmute from a `u128` to a `[u8; 16]`
++  --> $DIR/transmute.rs:123:31
++   |
++LL |             let _: [u8; 16] = std::mem::transmute(0u128);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
++
++error: transmute from a `i8` to a `[u8; 1]`
++  --> $DIR/transmute.rs:124:30
++   |
++LL |             let _: [u8; 1] = std::mem::transmute(0i8);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
++
++error: transmute from a `i32` to a `[u8; 4]`
++  --> $DIR/transmute.rs:125:30
++   |
++LL |             let _: [u8; 4] = std::mem::transmute(0i32);
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
++
++error: transmute from a `i128` to a `[u8; 16]`
++  --> $DIR/transmute.rs:126:31
++   |
++LL |             let _: [u8; 16] = std::mem::transmute(0i128);
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
++
 +error: transmute from a `&[u8]` to a `&str`
-   --> $DIR/transmute.rs:108:32
++  --> $DIR/transmute.rs:134:28
 +   |
 +LL |     let _: &str = unsafe { std::mem::transmute(b) };
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
 +   |
 +   = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
 +
 +error: transmute from a `&mut [u8]` to a `&mut str`
- error: aborting due to 24 previous errors
++  --> $DIR/transmute.rs:135:32
 +   |
 +LL |     let _: &mut str = unsafe { std::mem::transmute(mb) };
 +   |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
 +
++error: aborting due to 38 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..52577323a5837255f37698f6ff8076a941bd205c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,287 @@@
++#![warn(clippy::undocumented_unsafe_blocks)]
++
++// Valid comments
++
++fn nested_local() {
++    let _ = {
++        let _ = {
++            // Safety:
++            let _ = unsafe {};
++        };
++    };
++}
++
++fn deep_nest() {
++    let _ = {
++        let _ = {
++            // Safety:
++            let _ = unsafe {};
++
++            // Safety:
++            unsafe {};
++
++            let _ = {
++                let _ = {
++                    let _ = {
++                        let _ = {
++                            let _ = {
++                                // Safety:
++                                let _ = unsafe {};
++
++                                // Safety:
++                                unsafe {};
++                            };
++                        };
++                    };
++
++                    // Safety:
++                    unsafe {};
++                };
++            };
++        };
++
++        // Safety:
++        unsafe {};
++    };
++
++    // Safety:
++    unsafe {};
++}
++
++fn local_tuple_expression() {
++    // Safety:
++    let _ = (42, unsafe {});
++}
++
++fn line_comment() {
++    // Safety:
++    unsafe {}
++}
++
++fn line_comment_newlines() {
++    // Safety:
++
++    unsafe {}
++}
++
++fn line_comment_empty() {
++    // Safety:
++    //
++    //
++    //
++    unsafe {}
++}
++
++fn line_comment_with_extras() {
++    // This is a description
++    // Safety:
++    unsafe {}
++}
++
++fn block_comment() {
++    /* Safety: */
++    unsafe {}
++}
++
++fn block_comment_newlines() {
++    /* Safety: */
++
++    unsafe {}
++}
++
++#[rustfmt::skip]
++fn inline_block_comment() {
++    /* Safety: */unsafe {}
++}
++
++fn block_comment_with_extras() {
++    /* This is a description
++     * Safety:
++     */
++    unsafe {}
++}
++
++fn block_comment_terminator_same_line() {
++    /* This is a description
++     * Safety: */
++    unsafe {}
++}
++
++fn buried_safety() {
++    // Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor
++    // incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation
++    // ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
++    // reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
++    // occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
++    // laborum. Safety:
++    // Tellus elementum sagittis vitae et leo duis ut diam quam. Sit amet nulla facilisi
++    // morbi tempus iaculis urna. Amet luctus venenatis lectus magna. At quis risus sed vulputate odio
++    // ut. Luctus venenatis lectus magna fringilla urna. Tortor id aliquet lectus proin nibh nisl
++    // condimentum id venenatis. Vulputate dignissim suspendisse in est ante in nibh mauris cursus.
++    unsafe {}
++}
++
++fn safety_with_prepended_text() {
++    // This is a test. Safety:
++    unsafe {}
++}
++
++fn local_line_comment() {
++    // Safety:
++    let _ = unsafe {};
++}
++
++fn local_block_comment() {
++    /* Safety: */
++    let _ = unsafe {};
++}
++
++fn comment_array() {
++    // Safety:
++    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
++}
++
++fn comment_tuple() {
++    // Safety:
++    let _ = (42, unsafe {}, "test", unsafe {});
++}
++
++fn comment_unary() {
++    // Safety:
++    let _ = *unsafe { &42 };
++}
++
++#[allow(clippy::match_single_binding)]
++fn comment_match() {
++    // Safety:
++    let _ = match unsafe {} {
++        _ => {},
++    };
++}
++
++fn comment_addr_of() {
++    // Safety:
++    let _ = &unsafe {};
++}
++
++fn comment_repeat() {
++    // Safety:
++    let _ = [unsafe {}; 5];
++}
++
++fn comment_macro_call() {
++    macro_rules! t {
++        ($b:expr) => {
++            $b
++        };
++    }
++
++    t!(
++        // Safety:
++        unsafe {}
++    );
++}
++
++fn comment_macro_def() {
++    macro_rules! t {
++        () => {
++            // Safety:
++            unsafe {}
++        };
++    }
++
++    t!();
++}
++
++fn non_ascii_comment() {
++    // ॐ᧻໒ Safety: ௵∰
++    unsafe {};
++}
++
++fn local_commented_block() {
++    let _ =
++        // Safety:
++        unsafe {};
++}
++
++fn local_nest() {
++    // Safety:
++    let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
++}
++
++// Invalid comments
++
++fn no_comment() {
++    unsafe {}
++}
++
++fn no_comment_array() {
++    let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
++}
++
++fn no_comment_tuple() {
++    let _ = (42, unsafe {}, "test", unsafe {});
++}
++
++fn no_comment_unary() {
++    let _ = *unsafe { &42 };
++}
++
++#[allow(clippy::match_single_binding)]
++fn no_comment_match() {
++    let _ = match unsafe {} {
++        _ => {},
++    };
++}
++
++fn no_comment_addr_of() {
++    let _ = &unsafe {};
++}
++
++fn no_comment_repeat() {
++    let _ = [unsafe {}; 5];
++}
++
++fn local_no_comment() {
++    let _ = unsafe {};
++}
++
++fn no_comment_macro_call() {
++    macro_rules! t {
++        ($b:expr) => {
++            $b
++        };
++    }
++
++    t!(unsafe {});
++}
++
++fn no_comment_macro_def() {
++    macro_rules! t {
++        () => {
++            unsafe {}
++        };
++    }
++
++    t!();
++}
++
++fn trailing_comment() {
++    unsafe {} // Safety:
++}
++
++fn internal_comment() {
++    unsafe {
++        // Safety:
++    }
++}
++
++fn interference() {
++    // Safety
++
++    let _ = 42;
++
++    unsafe {};
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..613e9ffca456559a9352d85166f44711d7c7b787
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,159 @@@
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:215:5
++   |
++LL |     unsafe {}
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     unsafe {}
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:219:5
++   |
++LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:223:5
++   |
++LL |     let _ = (42, unsafe {}, "test", unsafe {});
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = (42, unsafe {}, "test", unsafe {});
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:227:5
++   |
++LL |     let _ = *unsafe { &42 };
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = *unsafe { &42 };
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:232:5
++   |
++LL |     let _ = match unsafe {} {
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = match unsafe {} {
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:238:5
++   |
++LL |     let _ = &unsafe {};
++   |     ^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = &unsafe {};
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:242:5
++   |
++LL |     let _ = [unsafe {}; 5];
++   |     ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = [unsafe {}; 5];
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:246:5
++   |
++LL |     let _ = unsafe {};
++   |     ^^^^^^^^^^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     let _ = unsafe {};
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:256:8
++   |
++LL |     t!(unsafe {});
++   |        ^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     t!(// Safety: ...
++LL ~     unsafe {});
++   |
++
++error: unsafe block in macro expansion missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:262:13
++   |
++LL |             unsafe {}
++   |             ^^^^^^^^^
++...
++LL |     t!();
++   |     ---- in this macro invocation
++   |
++   = help: consider adding a safety comment in the macro definition
++   = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:270:5
++   |
++LL |     unsafe {} // Safety:
++   |     ^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL ~     unsafe {} // Safety:
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:274:5
++   |
++LL |     unsafe {
++   |     ^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL +     unsafe {
++   |
++
++error: unsafe block missing a safety comment
++  --> $DIR/undocumented_unsafe_blocks.rs:284:5
++   |
++LL |     unsafe {};
++   |     ^^^^^^^^^
++   |
++help: consider adding a safety comment
++   |
++LL ~     // Safety: ...
++LL ~     unsafe {};
++   |
++
++error: aborting due to 13 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..dc150cf28f2cce48b79d9df965539d5482cc445f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,94 @@@
++#![warn(clippy::uninit_vec)]
++
++use std::mem::MaybeUninit;
++
++#[derive(Default)]
++struct MyVec {
++    vec: Vec<u8>,
++}
++
++fn main() {
++    // with_capacity() -> set_len() should be detected
++    let mut vec: Vec<u8> = Vec::with_capacity(1000);
++    unsafe {
++        vec.set_len(200);
++    }
++
++    // reserve() -> set_len() should be detected
++    vec.reserve(1000);
++    unsafe {
++        vec.set_len(200);
++    }
++
++    // new() -> set_len() should be detected
++    let mut vec: Vec<u8> = Vec::new();
++    unsafe {
++        vec.set_len(200);
++    }
++
++    // default() -> set_len() should be detected
++    let mut vec: Vec<u8> = Default::default();
++    unsafe {
++        vec.set_len(200);
++    }
++
++    let mut vec: Vec<u8> = Vec::default();
++    unsafe {
++        vec.set_len(200);
++    }
++
++    // test when both calls are enclosed in the same unsafe block
++    unsafe {
++        let mut vec: Vec<u8> = Vec::with_capacity(1000);
++        vec.set_len(200);
++
++        vec.reserve(1000);
++        vec.set_len(200);
++    }
++
++    let mut vec: Vec<u8> = Vec::with_capacity(1000);
++    unsafe {
++        // test the case where there are other statements in the following unsafe block
++        vec.set_len(200);
++        assert!(vec.len() == 200);
++    }
++
++    // handle vec stored in the field of a struct
++    let mut my_vec = MyVec::default();
++    my_vec.vec.reserve(1000);
++    unsafe {
++        my_vec.vec.set_len(200);
++    }
++
++    my_vec.vec = Vec::with_capacity(1000);
++    unsafe {
++        my_vec.vec.set_len(200);
++    }
++
++    // Test `#[allow(...)]` attributes on inner unsafe block (shouldn't trigger)
++    let mut vec: Vec<u8> = Vec::with_capacity(1000);
++    #[allow(clippy::uninit_vec)]
++    unsafe {
++        vec.set_len(200);
++    }
++
++    // MaybeUninit-wrapped types should not be detected
++    unsafe {
++        let mut vec: Vec<MaybeUninit<u8>> = Vec::with_capacity(1000);
++        vec.set_len(200);
++
++        let mut vec: Vec<(MaybeUninit<u8>, MaybeUninit<bool>)> = Vec::with_capacity(1000);
++        vec.set_len(200);
++
++        let mut vec: Vec<(MaybeUninit<u8>, [MaybeUninit<bool>; 2])> = Vec::with_capacity(1000);
++        vec.set_len(200);
++    }
++
++    // known false negative
++    let mut vec1: Vec<u8> = Vec::with_capacity(1000);
++    let mut vec2: Vec<u8> = Vec::with_capacity(1000);
++    unsafe {
++        vec1.set_len(200);
++        vec2.set_len(200);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..520bfb26b62e1b7c23c521cb859113fb31aa540e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,105 @@@
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:12:5
++   |
++LL |     let mut vec: Vec<u8> = Vec::with_capacity(1000);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = note: `-D clippy::uninit-vec` implied by `-D warnings`
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:18:5
++   |
++LL |     vec.reserve(1000);
++   |     ^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: calling `set_len()` on empty `Vec` creates out-of-bound values
++  --> $DIR/uninit_vec.rs:24:5
++   |
++LL |     let mut vec: Vec<u8> = Vec::new();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++
++error: calling `set_len()` on empty `Vec` creates out-of-bound values
++  --> $DIR/uninit_vec.rs:30:5
++   |
++LL |     let mut vec: Vec<u8> = Default::default();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++
++error: calling `set_len()` on empty `Vec` creates out-of-bound values
++  --> $DIR/uninit_vec.rs:35:5
++   |
++LL |     let mut vec: Vec<u8> = Vec::default();
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:49:5
++   |
++LL |     let mut vec: Vec<u8> = Vec::with_capacity(1000);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++...
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:58:5
++   |
++LL |     my_vec.vec.reserve(1000);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         my_vec.vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:63:5
++   |
++LL |     my_vec.vec = Vec::with_capacity(1000);
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |     unsafe {
++LL |         my_vec.vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:42:9
++   |
++LL |         let mut vec: Vec<u8> = Vec::with_capacity(1000);
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: calling `set_len()` immediately after reserving a buffer creates uninitialized values
++  --> $DIR/uninit_vec.rs:45:9
++   |
++LL |         vec.reserve(1000);
++   |         ^^^^^^^^^^^^^^^^^^
++LL |         vec.set_len(200);
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = help: initialize the buffer or wrap the content in `MaybeUninit`
++
++error: aborting due to 10 previous errors
++
index b45b27d8f23b1a92bc006bc64104b97302beda7c,0000000000000000000000000000000000000000..d806d620b176a3efd702bef0b00b0fb4755ce1dd
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,105 @@@
 +// run-rustfix
 +
 +#![allow(clippy::stable_sort_primitive)]
 +
++use std::cell::Ref;
 +use std::cmp::Reverse;
 +
 +fn unnecessary_sort_by() {
 +    fn id(x: isize) -> isize {
 +        x
 +    }
 +
 +    let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
 +    // Forward examples
 +    vec.sort();
 +    vec.sort_unstable();
 +    vec.sort_by_key(|a| (a + 5).abs());
 +    vec.sort_unstable_by_key(|a| id(-a));
 +    // Reverse examples
 +    vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow
 +    vec.sort_by_key(|b| Reverse((b + 5).abs()));
 +    vec.sort_unstable_by_key(|b| Reverse(id(-b)));
 +    // Negative examples (shouldn't be changed)
 +    let c = &7;
 +    vec.sort_by(|a, b| (b - a).cmp(&(a - b)));
 +    vec.sort_by(|_, b| b.cmp(&5));
 +    vec.sort_by(|_, b| b.cmp(c));
 +    vec.sort_unstable_by(|a, _| a.cmp(c));
 +
 +    // Vectors of references are fine as long as the resulting key does not borrow
 +    let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5];
 +    vec.sort_by_key(|a| (***a).abs());
 +    vec.sort_unstable_by_key(|a| (***a).abs());
 +    // `Reverse(b)` would borrow in the following cases, don't lint
 +    vec.sort_by(|a, b| b.cmp(a));
 +    vec.sort_unstable_by(|a, b| b.cmp(a));
++
++    // No warning if element does not implement `Ord`
++    let mut vec: Vec<Ref<usize>> = Vec::new();
++    vec.sort_unstable_by(|a, b| a.cmp(b));
 +}
 +
 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key`
 +mod issue_5754 {
 +    #[derive(Clone, Copy)]
 +    struct Test(usize);
 +
 +    #[derive(PartialOrd, Ord, PartialEq, Eq)]
 +    struct Wrapper<'a>(&'a usize);
 +
 +    impl Test {
 +        fn name(&self) -> &usize {
 +            &self.0
 +        }
 +
 +        fn wrapped(&self) -> Wrapper<'_> {
 +            Wrapper(&self.0)
 +        }
 +    }
 +
 +    pub fn test() {
 +        let mut args: Vec<Test> = vec![];
 +
 +        // Forward
 +        args.sort_by(|a, b| a.name().cmp(b.name()));
 +        args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
 +        args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
 +        args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
 +        // Reverse
 +        args.sort_by(|a, b| b.name().cmp(a.name()));
 +        args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
 +        args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
 +        args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
 +    }
 +}
 +
 +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted
 +mod issue_6001 {
 +    use super::*;
 +    struct Test(String);
 +
 +    impl Test {
 +        // Return an owned type so that we don't hit the fix for 5754
 +        fn name(&self) -> String {
 +            self.0.clone()
 +        }
 +    }
 +
 +    pub fn test() {
 +        let mut args: Vec<Test> = vec![];
 +
 +        // Forward
 +        args.sort_by_key(|a| a.name());
 +        args.sort_unstable_by_key(|a| a.name());
 +        // Reverse
 +        args.sort_by_key(|b| Reverse(b.name()));
 +        args.sort_unstable_by_key(|b| Reverse(b.name()));
 +    }
 +}
 +
 +fn main() {
 +    unnecessary_sort_by();
 +    issue_5754::test();
 +    issue_6001::test();
 +}
index be2abe7f7014d785b59ccae8875d550829a3ca23,0000000000000000000000000000000000000000..6ee9c3af455dfbd39c98264d2f9f386b5bc8a85b
mode 100644,000000..100644
--- /dev/null
@@@ -1,100 -1,0 +1,105 @@@
 +// run-rustfix
 +
 +#![allow(clippy::stable_sort_primitive)]
 +
++use std::cell::Ref;
 +use std::cmp::Reverse;
 +
 +fn unnecessary_sort_by() {
 +    fn id(x: isize) -> isize {
 +        x
 +    }
 +
 +    let mut vec: Vec<isize> = vec![3, 6, 1, 2, 5];
 +    // Forward examples
 +    vec.sort_by(|a, b| a.cmp(b));
 +    vec.sort_unstable_by(|a, b| a.cmp(b));
 +    vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
 +    vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
 +    // Reverse examples
 +    vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow
 +    vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
 +    vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
 +    // Negative examples (shouldn't be changed)
 +    let c = &7;
 +    vec.sort_by(|a, b| (b - a).cmp(&(a - b)));
 +    vec.sort_by(|_, b| b.cmp(&5));
 +    vec.sort_by(|_, b| b.cmp(c));
 +    vec.sort_unstable_by(|a, _| a.cmp(c));
 +
 +    // Vectors of references are fine as long as the resulting key does not borrow
 +    let mut vec: Vec<&&&isize> = vec![&&&3, &&&6, &&&1, &&&2, &&&5];
 +    vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
 +    vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
 +    // `Reverse(b)` would borrow in the following cases, don't lint
 +    vec.sort_by(|a, b| b.cmp(a));
 +    vec.sort_unstable_by(|a, b| b.cmp(a));
++
++    // No warning if element does not implement `Ord`
++    let mut vec: Vec<Ref<usize>> = Vec::new();
++    vec.sort_unstable_by(|a, b| a.cmp(b));
 +}
 +
 +// Do not suggest returning a reference to the closure parameter of `Vec::sort_by_key`
 +mod issue_5754 {
 +    #[derive(Clone, Copy)]
 +    struct Test(usize);
 +
 +    #[derive(PartialOrd, Ord, PartialEq, Eq)]
 +    struct Wrapper<'a>(&'a usize);
 +
 +    impl Test {
 +        fn name(&self) -> &usize {
 +            &self.0
 +        }
 +
 +        fn wrapped(&self) -> Wrapper<'_> {
 +            Wrapper(&self.0)
 +        }
 +    }
 +
 +    pub fn test() {
 +        let mut args: Vec<Test> = vec![];
 +
 +        // Forward
 +        args.sort_by(|a, b| a.name().cmp(b.name()));
 +        args.sort_by(|a, b| a.wrapped().cmp(&b.wrapped()));
 +        args.sort_unstable_by(|a, b| a.name().cmp(b.name()));
 +        args.sort_unstable_by(|a, b| a.wrapped().cmp(&b.wrapped()));
 +        // Reverse
 +        args.sort_by(|a, b| b.name().cmp(a.name()));
 +        args.sort_by(|a, b| b.wrapped().cmp(&a.wrapped()));
 +        args.sort_unstable_by(|a, b| b.name().cmp(a.name()));
 +        args.sort_unstable_by(|a, b| b.wrapped().cmp(&a.wrapped()));
 +    }
 +}
 +
 +// The closure parameter is not dereferenced anymore, so non-Copy types can be linted
 +mod issue_6001 {
 +    use super::*;
 +    struct Test(String);
 +
 +    impl Test {
 +        // Return an owned type so that we don't hit the fix for 5754
 +        fn name(&self) -> String {
 +            self.0.clone()
 +        }
 +    }
 +
 +    pub fn test() {
 +        let mut args: Vec<Test> = vec![];
 +
 +        // Forward
 +        args.sort_by(|a, b| a.name().cmp(&b.name()));
 +        args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
 +        // Reverse
 +        args.sort_by(|a, b| b.name().cmp(&a.name()));
 +        args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
 +    }
 +}
 +
 +fn main() {
 +    unnecessary_sort_by();
 +    issue_5754::test();
 +    issue_6001::test();
 +}
index 50607933e18f7b5ed5dc012a362eb03f9d910240,0000000000000000000000000000000000000000..ca9641e880316533eda97e7f6904efccc7297380
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,76 @@@
-   --> $DIR/unnecessary_sort_by.rs:14:5
 +error: use Vec::sort here instead
-   --> $DIR/unnecessary_sort_by.rs:15:5
++  --> $DIR/unnecessary_sort_by.rs:15:5
 +   |
 +LL |     vec.sort_by(|a, b| a.cmp(b));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort()`
 +   |
 +   = note: `-D clippy::unnecessary-sort-by` implied by `-D warnings`
 +
 +error: use Vec::sort here instead
-   --> $DIR/unnecessary_sort_by.rs:16:5
++  --> $DIR/unnecessary_sort_by.rs:16:5
 +   |
 +LL |     vec.sort_unstable_by(|a, b| a.cmp(b));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable()`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:17:5
++  --> $DIR/unnecessary_sort_by.rs:17:5
 +   |
 +LL |     vec.sort_by(|a, b| (a + 5).abs().cmp(&(b + 5).abs()));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (a + 5).abs())`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:20:5
++  --> $DIR/unnecessary_sort_by.rs:18:5
 +   |
 +LL |     vec.sort_unstable_by(|a, b| id(-a).cmp(&id(-b)));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| id(-a))`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:21:5
++  --> $DIR/unnecessary_sort_by.rs:21:5
 +   |
 +LL |     vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs()));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|b| Reverse((b + 5).abs()))`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:31:5
++  --> $DIR/unnecessary_sort_by.rs:22:5
 +   |
 +LL |     vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a)));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|b| Reverse(id(-b)))`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:32:5
++  --> $DIR/unnecessary_sort_by.rs:32:5
 +   |
 +LL |     vec.sort_by(|a, b| (***a).abs().cmp(&(***b).abs()));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_by_key(|a| (***a).abs())`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:88:9
++  --> $DIR/unnecessary_sort_by.rs:33:5
 +   |
 +LL |     vec.sort_unstable_by(|a, b| (***a).abs().cmp(&(***b).abs()));
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec.sort_unstable_by_key(|a| (***a).abs())`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:89:9
++  --> $DIR/unnecessary_sort_by.rs:93:9
 +   |
 +LL |         args.sort_by(|a, b| a.name().cmp(&b.name()));
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|a| a.name())`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:91:9
++  --> $DIR/unnecessary_sort_by.rs:94:9
 +   |
 +LL |         args.sort_unstable_by(|a, b| a.name().cmp(&b.name()));
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|a| a.name())`
 +
 +error: use Vec::sort_by_key here instead
-   --> $DIR/unnecessary_sort_by.rs:92:9
++  --> $DIR/unnecessary_sort_by.rs:96:9
 +   |
 +LL |         args.sort_by(|a, b| b.name().cmp(&a.name()));
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_by_key(|b| Reverse(b.name()))`
 +
 +error: use Vec::sort_by_key here instead
++  --> $DIR/unnecessary_sort_by.rs:97:9
 +   |
 +LL |         args.sort_unstable_by(|a, b| b.name().cmp(&a.name()));
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `args.sort_unstable_by_key(|b| Reverse(b.name()))`
 +
 +error: aborting due to 12 previous errors
 +
index ee9c9045fff55940b42177b9eecef25bebe2b780,0000000000000000000000000000000000000000..98bc1e80731ff3934643d2c565f70ad241ddd7f1
mode 100644,000000..100644
--- /dev/null
@@@ -1,233 -1,0 +1,241 @@@
 +// run-rustfix
 +// aux-build:wildcard_imports_helper.rs
 +
 +#![warn(clippy::wildcard_imports)]
 +//#![allow(clippy::redundant_pub_crate)]
 +#![allow(unused)]
 +#![allow(clippy::unnecessary_wraps)]
 +#![warn(unused_imports)]
 +
 +extern crate wildcard_imports_helper;
 +
 +use crate::fn_mod::foo;
 +use crate::mod_mod::inner_mod;
 +use crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod};
 +#[macro_use]
 +use crate::struct_mod::{A, inner_struct_mod};
 +
 +#[allow(unused_imports)]
 +use wildcard_imports_helper::inner::inner_for_self_import;
 +use wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar;
 +use wildcard_imports_helper::{ExternA, extern_foo};
 +
 +use std::io::prelude::*;
 +use wildcard_imports_helper::prelude::v1::*;
 +
 +struct ReadFoo;
 +
 +impl Read for ReadFoo {
 +    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
 +        Ok(0)
 +    }
 +}
 +
 +mod fn_mod {
 +    pub fn foo() {}
 +}
 +
 +mod mod_mod {
 +    pub mod inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod multi_fn_mod {
 +    pub fn multi_foo() {}
 +    pub fn multi_bar() {}
 +    pub fn multi_baz() {}
 +    pub mod multi_inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod struct_mod {
 +    pub struct A;
 +    pub struct B;
 +    pub mod inner_struct_mod {
 +        pub struct C;
 +    }
 +
 +    #[macro_export]
 +    macro_rules! double_struct_import_test {
 +        () => {
 +            let _ = A;
 +        };
 +    }
 +}
 +
 +fn main() {
 +    foo();
 +    multi_foo();
 +    multi_bar();
 +    multi_inner_mod::foo();
 +    inner_mod::foo();
 +    extern_foo();
 +    inner_extern_bar();
 +
 +    let _ = A;
 +    let _ = inner_struct_mod::C;
 +    let _ = ExternA;
 +    let _ = PreludeModAnywhere;
 +
 +    double_struct_import_test!();
 +    double_struct_import_test!();
 +}
 +
 +mod in_fn_test {
 +    pub use self::inner_exported::*;
 +    #[allow(unused_imports)]
 +    pub(crate) use self::inner_exported2::*;
 +
 +    fn test_intern() {
 +        use crate::fn_mod::foo;
 +
 +        foo();
 +    }
 +
 +    fn test_extern() {
 +        use wildcard_imports_helper::inner::inner_for_self_import::{self, inner_extern_foo};
 +        use wildcard_imports_helper::{ExternA, extern_foo};
 +
 +        inner_for_self_import::inner_extern_foo();
 +        inner_extern_foo();
 +
 +        extern_foo();
 +
 +        let _ = ExternA;
 +    }
 +
 +    fn test_inner_nested() {
 +        use self::{inner::inner_foo, inner2::inner_bar};
 +
 +        inner_foo();
 +        inner_bar();
 +    }
 +
 +    fn test_extern_reexported() {
 +        use wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported};
 +
 +        extern_exported();
 +        let _ = ExternExportedStruct;
 +        let _ = ExternExportedEnum::A;
 +    }
 +
 +    mod inner_exported {
 +        pub fn exported() {}
 +        pub struct ExportedStruct;
 +        pub enum ExportedEnum {
 +            A,
 +        }
 +    }
 +
 +    mod inner_exported2 {
 +        pub(crate) fn exported2() {}
 +    }
 +
 +    mod inner {
 +        pub fn inner_foo() {}
 +    }
 +
 +    mod inner2 {
 +        pub fn inner_bar() {}
 +    }
 +}
 +
 +fn test_reexported() {
 +    use crate::in_fn_test::{ExportedEnum, ExportedStruct, exported};
 +
 +    exported();
 +    let _ = ExportedStruct;
 +    let _ = ExportedEnum::A;
 +}
 +
 +#[rustfmt::skip]
 +fn test_weird_formatting() {
 +    use crate:: in_fn_test::exported;
 +    use crate:: fn_mod::foo;
 +
 +    exported();
 +    foo();
 +}
 +
 +mod super_imports {
 +    fn foofoo() {}
 +
 +    mod should_be_replaced {
 +        use super::foofoo;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_inside_function {
 +        fn with_super_inside_function() {
 +            use super::*;
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod should_be_replaced_futher_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::insidefoo;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod use_explicit_should_be_replaced {
 +        use super_imports::foofoo;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod use_double_super_should_be_replaced {
 +        mod inner {
 +            use super::super::foofoo;
 +
 +            fn with_double_super() {
 +                let _ = foofoo();
 +            }
 +        }
 +    }
 +
 +    mod use_super_explicit_should_be_replaced {
 +        use super::super::super_imports::foofoo;
 +
 +        fn with_super_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
++
++    mod attestation_should_be_replaced {
++        use super::foofoo;
++
++        fn with_explicit() {
++            let _ = foofoo();
++        }
++    }
 +}
index efaa8f9ef664186b0ba5e70a6d9cd4143bb31460,0000000000000000000000000000000000000000..4ef61f9245b58f9420daded6c79a1d84e201edb9
mode 100644,000000..100644
--- /dev/null
@@@ -1,234 -1,0 +1,242 @@@
 +// run-rustfix
 +// aux-build:wildcard_imports_helper.rs
 +
 +#![warn(clippy::wildcard_imports)]
 +//#![allow(clippy::redundant_pub_crate)]
 +#![allow(unused)]
 +#![allow(clippy::unnecessary_wraps)]
 +#![warn(unused_imports)]
 +
 +extern crate wildcard_imports_helper;
 +
 +use crate::fn_mod::*;
 +use crate::mod_mod::*;
 +use crate::multi_fn_mod::*;
 +#[macro_use]
 +use crate::struct_mod::*;
 +
 +#[allow(unused_imports)]
 +use wildcard_imports_helper::inner::inner_for_self_import;
 +use wildcard_imports_helper::inner::inner_for_self_import::*;
 +use wildcard_imports_helper::*;
 +
 +use std::io::prelude::*;
 +use wildcard_imports_helper::prelude::v1::*;
 +
 +struct ReadFoo;
 +
 +impl Read for ReadFoo {
 +    fn read(&mut self, _buf: &mut [u8]) -> std::io::Result<usize> {
 +        Ok(0)
 +    }
 +}
 +
 +mod fn_mod {
 +    pub fn foo() {}
 +}
 +
 +mod mod_mod {
 +    pub mod inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod multi_fn_mod {
 +    pub fn multi_foo() {}
 +    pub fn multi_bar() {}
 +    pub fn multi_baz() {}
 +    pub mod multi_inner_mod {
 +        pub fn foo() {}
 +    }
 +}
 +
 +mod struct_mod {
 +    pub struct A;
 +    pub struct B;
 +    pub mod inner_struct_mod {
 +        pub struct C;
 +    }
 +
 +    #[macro_export]
 +    macro_rules! double_struct_import_test {
 +        () => {
 +            let _ = A;
 +        };
 +    }
 +}
 +
 +fn main() {
 +    foo();
 +    multi_foo();
 +    multi_bar();
 +    multi_inner_mod::foo();
 +    inner_mod::foo();
 +    extern_foo();
 +    inner_extern_bar();
 +
 +    let _ = A;
 +    let _ = inner_struct_mod::C;
 +    let _ = ExternA;
 +    let _ = PreludeModAnywhere;
 +
 +    double_struct_import_test!();
 +    double_struct_import_test!();
 +}
 +
 +mod in_fn_test {
 +    pub use self::inner_exported::*;
 +    #[allow(unused_imports)]
 +    pub(crate) use self::inner_exported2::*;
 +
 +    fn test_intern() {
 +        use crate::fn_mod::*;
 +
 +        foo();
 +    }
 +
 +    fn test_extern() {
 +        use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
 +        use wildcard_imports_helper::*;
 +
 +        inner_for_self_import::inner_extern_foo();
 +        inner_extern_foo();
 +
 +        extern_foo();
 +
 +        let _ = ExternA;
 +    }
 +
 +    fn test_inner_nested() {
 +        use self::{inner::*, inner2::*};
 +
 +        inner_foo();
 +        inner_bar();
 +    }
 +
 +    fn test_extern_reexported() {
 +        use wildcard_imports_helper::*;
 +
 +        extern_exported();
 +        let _ = ExternExportedStruct;
 +        let _ = ExternExportedEnum::A;
 +    }
 +
 +    mod inner_exported {
 +        pub fn exported() {}
 +        pub struct ExportedStruct;
 +        pub enum ExportedEnum {
 +            A,
 +        }
 +    }
 +
 +    mod inner_exported2 {
 +        pub(crate) fn exported2() {}
 +    }
 +
 +    mod inner {
 +        pub fn inner_foo() {}
 +    }
 +
 +    mod inner2 {
 +        pub fn inner_bar() {}
 +    }
 +}
 +
 +fn test_reexported() {
 +    use crate::in_fn_test::*;
 +
 +    exported();
 +    let _ = ExportedStruct;
 +    let _ = ExportedEnum::A;
 +}
 +
 +#[rustfmt::skip]
 +fn test_weird_formatting() {
 +    use crate:: in_fn_test::  * ;
 +    use crate:: fn_mod::
 +        *;
 +
 +    exported();
 +    foo();
 +}
 +
 +mod super_imports {
 +    fn foofoo() {}
 +
 +    mod should_be_replaced {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass {
 +        use super::*;
 +
 +        fn with_super() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_inside_function {
 +        fn with_super_inside_function() {
 +            use super::*;
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod test_should_pass_further_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod should_be_replaced_futher_inside {
 +        fn insidefoo() {}
 +        mod inner {
 +            use super::*;
 +            fn with_super() {
 +                let _ = insidefoo();
 +            }
 +        }
 +    }
 +
 +    mod use_explicit_should_be_replaced {
 +        use super_imports::*;
 +
 +        fn with_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
 +
 +    mod use_double_super_should_be_replaced {
 +        mod inner {
 +            use super::super::*;
 +
 +            fn with_double_super() {
 +                let _ = foofoo();
 +            }
 +        }
 +    }
 +
 +    mod use_super_explicit_should_be_replaced {
 +        use super::super::super_imports::*;
 +
 +        fn with_super_explicit() {
 +            let _ = foofoo();
 +        }
 +    }
++
++    mod attestation_should_be_replaced {
++        use super::*;
++
++        fn with_explicit() {
++            let _ = foofoo();
++        }
++    }
 +}
index 66267dd27b84fcf4da55e265ed164709a6e6647e,0000000000000000000000000000000000000000..d7af0c046e88694f34a1ff399931fe346cc8904c
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,132 @@@
- error: aborting due to 20 previous errors
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:12:5
 +   |
 +LL | use crate::fn_mod::*;
 +   |     ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
 +   |
 +   = note: `-D clippy::wildcard-imports` implied by `-D warnings`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:13:5
 +   |
 +LL | use crate::mod_mod::*;
 +   |     ^^^^^^^^^^^^^^^^^ help: try: `crate::mod_mod::inner_mod`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:14:5
 +   |
 +LL | use crate::multi_fn_mod::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::multi_fn_mod::{multi_bar, multi_foo, multi_inner_mod}`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:16:5
 +   |
 +LL | use crate::struct_mod::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::struct_mod::{A, inner_struct_mod}`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:20:5
 +   |
 +LL | use wildcard_imports_helper::inner::inner_for_self_import::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::inner::inner_for_self_import::inner_extern_bar`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:21:5
 +   |
 +LL | use wildcard_imports_helper::*;
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:92:13
 +   |
 +LL |         use crate::fn_mod::*;
 +   |             ^^^^^^^^^^^^^^^^ help: try: `crate::fn_mod::foo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:98:75
 +   |
 +LL |         use wildcard_imports_helper::inner::inner_for_self_import::{self, *};
 +   |                                                                           ^ help: try: `inner_extern_foo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:99:13
 +   |
 +LL |         use wildcard_imports_helper::*;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternA, extern_foo}`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:110:20
 +   |
 +LL |         use self::{inner::*, inner2::*};
 +   |                    ^^^^^^^^ help: try: `inner::inner_foo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:110:30
 +   |
 +LL |         use self::{inner::*, inner2::*};
 +   |                              ^^^^^^^^^ help: try: `inner2::inner_bar`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:117:13
 +   |
 +LL |         use wildcard_imports_helper::*;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `wildcard_imports_helper::{ExternExportedEnum, ExternExportedStruct, extern_exported}`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:146:9
 +   |
 +LL |     use crate::in_fn_test::*;
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: try: `crate::in_fn_test::{ExportedEnum, ExportedStruct, exported}`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:155:9
 +   |
 +LL |     use crate:: in_fn_test::  * ;
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate:: in_fn_test::exported`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:156:9
 +   |
 +LL |       use crate:: fn_mod::
 +   |  _________^
 +LL | |         *;
 +   | |_________^ help: try: `crate:: fn_mod::foo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:167:13
 +   |
 +LL |         use super::*;
 +   |             ^^^^^^^^ help: try: `super::foofoo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:202:17
 +   |
 +LL |             use super::*;
 +   |                 ^^^^^^^^ help: try: `super::insidefoo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:210:13
 +   |
 +LL |         use super_imports::*;
 +   |             ^^^^^^^^^^^^^^^^ help: try: `super_imports::foofoo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:219:17
 +   |
 +LL |             use super::super::*;
 +   |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 +
 +error: usage of wildcard import
 +  --> $DIR/wildcard_imports.rs:228:13
 +   |
 +LL |         use super::super::super_imports::*;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
 +
++error: usage of wildcard import
++  --> $DIR/wildcard_imports.rs:236:13
++   |
++LL |         use super::*;
++   |             ^^^^^^^^ help: try: `super::foofoo`
++
++error: aborting due to 21 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f2f5f1e588ed408e8a252148e7c9f48b48001bda
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,15 @@@
++#[warn(clippy::eq_op)]
++#[test]
++fn eq_op_shouldnt_trigger_in_tests() {
++    let a = 1;
++    let result = a + 1 == 1 + a;
++    assert!(result);
++}
++
++#[test]
++fn eq_op_macros_shouldnt_trigger_in_tests() {
++    let a = 1;
++    let b = 2;
++    assert_eq!(a, a);
++    assert_eq!(a + b, b + a);
++}