]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit 'f51aade56f93175dde89177a92e3669ebd8e7592' into clippyup
authorJason Newcomb <jsnewcomb@pm.me>
Wed, 31 Aug 2022 13:24:45 +0000 (09:24 -0400)
committerJason Newcomb <jsnewcomb@pm.me>
Wed, 31 Aug 2022 13:24:45 +0000 (09:24 -0400)
227 files changed:
1  2 
Cargo.lock
src/tools/clippy/.github/workflows/clippy.yml
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/clippy_dev/src/bless.rs
src/tools/clippy/clippy_dev/src/fmt.rs
src/tools/clippy/clippy_dev/src/new_lint.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/doc_link_with_quotes.rs
src/tools/clippy/clippy_lints/src/duplicate_mod.rs
src/tools/clippy/clippy_lints/src/escape.rs
src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
src/tools/clippy/clippy_lints/src/format.rs
src/tools/clippy/clippy_lints/src/format_args.rs
src/tools/clippy/clippy_lints/src/format_impl.rs
src/tools/clippy/clippy_lints/src/functions/mod.rs
src/tools/clippy/clippy_lints/src/functions/result.rs
src/tools/clippy/clippy_lints/src/if_let_mutex.rs
src/tools/clippy/clippy_lints/src/if_then_some_else_none.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.register_style.rs
src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/needless_collect.rs
src/tools/clippy/clippy_lints/src/manual_async_fn.rs
src/tools/clippy/clippy_lints/src/manual_string_new.rs
src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
src/tools/clippy/clippy_lints/src/matches/needless_match.rs
src/tools/clippy/clippy_lints/src/methods/bytecount.rs
src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs
src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs
src/tools/clippy/clippy_lints/src/methods/expect_used.rs
src/tools/clippy/clippy_lints/src/methods/get_first.rs
src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
src/tools/clippy/clippy_lints/src/methods/map_clone.rs
src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
src/tools/clippy/clippy_lints/src/methods/open_options.rs
src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
src/tools/clippy/clippy_lints/src/methods/path_buf_push_overwrite.rs
src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs
src/tools/clippy/clippy_lints/src/methods/repeat_once.rs
src/tools/clippy/clippy_lints/src/methods/stable_sort_primitive.rs
src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
src/tools/clippy/clippy_lints/src/methods/uninit_assumed_init.rs
src/tools/clippy/clippy_lints/src/methods/unit_hash.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs
src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs
src/tools/clippy/clippy_lints/src/methods/verbose_file_reads.rs
src/tools/clippy/clippy_lints/src/misc_early/unneeded_wildcard_pattern.rs
src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
src/tools/clippy/clippy_lints/src/multi_assignments.rs
src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/partialeq_to_none.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/ranges.rs
src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs
src/tools/clippy/clippy_lints/src/redundant_slicing.rs
src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
src/tools/clippy/clippy_lints/src/returns.rs
src/tools/clippy/clippy_lints/src/self_named_constructors.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/transmute/mod.rs
src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
src/tools/clippy/clippy_lints/src/transmute/transmuting_null.rs
src/tools/clippy/clippy_lints/src/unicode.rs
src/tools/clippy/clippy_lints/src/uninit_vec.rs
src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
src/tools/clippy/clippy_lints/src/unused_peekable.rs
src/tools/clippy/clippy_lints/src/unused_rounding.rs
src/tools/clippy/clippy_lints/src/utils/conf.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/Cargo.toml
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/macros.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/rustc_tools_util/src/lib.rs
src/tools/clippy/tests/check-fmt.rs
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/integration.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed
src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs
src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr
src/tools/clippy/tests/ui/collapsible_str_replace.fixed
src/tools/clippy/tests/ui/collapsible_str_replace.rs
src/tools/clippy/tests/ui/collapsible_str_replace.stderr
src/tools/clippy/tests/ui/expect.rs
src/tools/clippy/tests/ui/expect.stderr
src/tools/clippy/tests/ui/floating_point_exp.fixed
src/tools/clippy/tests/ui/floating_point_exp.rs
src/tools/clippy/tests/ui/floating_point_exp.stderr
src/tools/clippy/tests/ui/floating_point_log.fixed
src/tools/clippy/tests/ui/floating_point_log.rs
src/tools/clippy/tests/ui/floating_point_log.stderr
src/tools/clippy/tests/ui/floating_point_logbase.fixed
src/tools/clippy/tests/ui/floating_point_logbase.rs
src/tools/clippy/tests/ui/floating_point_logbase.stderr
src/tools/clippy/tests/ui/floating_point_powf.fixed
src/tools/clippy/tests/ui/floating_point_powf.rs
src/tools/clippy/tests/ui/floating_point_powf.stderr
src/tools/clippy/tests/ui/floating_point_powi.fixed
src/tools/clippy/tests/ui/floating_point_powi.rs
src/tools/clippy/tests/ui/floating_point_powi.stderr
src/tools/clippy/tests/ui/floating_point_rad.fixed
src/tools/clippy/tests/ui/floating_point_rad.rs
src/tools/clippy/tests/ui/floating_point_rad.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/identity_op.fixed
src/tools/clippy/tests/ui/identity_op.rs
src/tools/clippy/tests/ui/if_let_mutex.rs
src/tools/clippy/tests/ui/if_let_mutex.stderr
src/tools/clippy/tests/ui/if_then_some_else_none.stderr
src/tools/clippy/tests/ui/iter_on_empty_collections.fixed
src/tools/clippy/tests/ui/iter_on_empty_collections.rs
src/tools/clippy/tests/ui/iter_on_empty_collections.stderr
src/tools/clippy/tests/ui/iter_on_single_items.fixed
src/tools/clippy/tests/ui/iter_on_single_items.rs
src/tools/clippy/tests/ui/iter_on_single_items.stderr
src/tools/clippy/tests/ui/manual_string_new.fixed
src/tools/clippy/tests/ui/manual_string_new.rs
src/tools/clippy/tests/ui/manual_string_new.stderr
src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
src/tools/clippy/tests/ui/multi_assignments.rs
src/tools/clippy/tests/ui/multi_assignments.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_borrow.stderr
src/tools/clippy/tests/ui/needless_collect_indirect.rs
src/tools/clippy/tests/ui/needless_collect_indirect.stderr
src/tools/clippy/tests/ui/needless_match.fixed
src/tools/clippy/tests/ui/needless_match.rs
src/tools/clippy/tests/ui/needless_match.stderr
src/tools/clippy/tests/ui/needless_return.fixed
src/tools/clippy/tests/ui/needless_return.rs
src/tools/clippy/tests/ui/only_used_in_recursion.rs
src/tools/clippy/tests/ui/only_used_in_recursion.stderr
src/tools/clippy/tests/ui/only_used_in_recursion2.rs
src/tools/clippy/tests/ui/only_used_in_recursion2.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/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.rs
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/partialeq_to_none.fixed
src/tools/clippy/tests/ui/partialeq_to_none.rs
src/tools/clippy/tests/ui/partialeq_to_none.stderr
src/tools/clippy/tests/ui/positional_named_format_parameters.fixed
src/tools/clippy/tests/ui/positional_named_format_parameters.rs
src/tools/clippy/tests/ui/positional_named_format_parameters.stderr
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/regex.rs
src/tools/clippy/tests/ui/result_large_err.rs
src/tools/clippy/tests/ui/result_large_err.stderr
src/tools/clippy/tests/ui/same_item_push.rs
src/tools/clippy/tests/ui/string_add.rs
src/tools/clippy/tests/ui/string_add_assign.fixed
src/tools/clippy/tests/ui/string_add_assign.rs
src/tools/clippy/tests/ui/suspicious_to_owned.rs
src/tools/clippy/tests/ui/suspicious_to_owned.stderr
src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.rs
src/tools/clippy/tests/ui/trait_duplication_in_bounds_unfixable.stderr
src/tools/clippy/tests/ui/transmute_undefined_repr.rs
src/tools/clippy/tests/ui/transmute_undefined_repr.stderr
src/tools/clippy/tests/ui/unicode.fixed
src/tools/clippy/tests/ui/unicode.rs
src/tools/clippy/tests/ui/unicode.stderr
src/tools/clippy/tests/ui/unnecessary_cast.fixed
src/tools/clippy/tests/ui/unnecessary_cast.rs
src/tools/clippy/tests/ui/unnecessary_cast.stderr
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.fixed
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.rs
src/tools/clippy/tests/ui/unnecessary_owned_empty_strings.stderr
src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
src/tools/clippy/tests/ui/unnecessary_to_owned.rs
src/tools/clippy/tests/ui/unused_peekable.rs
src/tools/clippy/tests/ui/unused_peekable.stderr
src/tools/clippy/tests/ui/unwrap.rs
src/tools/clippy/tests/ui/unwrap.stderr
src/tools/clippy/tests/ui/unwrap_expect_used.rs
src/tools/clippy/tests/ui/unwrap_expect_used.stderr
src/tools/clippy/tests/ui/useless_conversion_try.rs
src/tools/clippy/tests/ui/useless_conversion_try.stderr
src/tools/clippy/tests/ui/vec_resize_to_zero.rs
src/tools/clippy/tests/ui/vec_resize_to_zero.stderr
src/tools/clippy/tests/ui/verbose_file_reads.rs
src/tools/clippy/tests/workspace.rs

diff --cc Cargo.lock
index 6a375528347f23aeedf3f059e4b1f740f623aa75,0000000000000000000000000000000000000000..5f2c56b10a9f77f5dd6afcd25dbe52a1548646e4
mode 100644,000000..100644
--- /dev/null
@@@ -1,5543 -1,0 +1,5544 @@@
 +# This file is automatically @generated by Cargo.
 +# It is not intended for manual editing.
 +version = 3
 +
 +[[package]]
 +name = "addr2line"
 +version = "0.16.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd"
 +dependencies = [
 + "compiler_builtins",
 + "gimli 0.25.0",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "addr2line"
 +version = "0.17.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
 +dependencies = [
 + "gimli 0.26.1",
 +]
 +
 +[[package]]
 +name = "adler"
 +version = "0.2.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "adler"
 +version = "1.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 +
 +[[package]]
 +name = "ahash"
 +version = "0.7.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "43bb833f0bf979d8475d38fbf09ed3b8a55e1885fe93ad3f93239fc6a4f17b98"
 +dependencies = [
 + "getrandom 0.2.0",
 + "once_cell",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "aho-corasick"
 +version = "0.7.18"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
 +dependencies = [
 + "memchr",
 +]
 +
 +[[package]]
 +name = "alloc"
 +version = "0.0.0"
 +dependencies = [
 + "compiler_builtins",
 + "core",
 + "rand 0.7.3",
 + "rand_xorshift",
 +]
 +
 +[[package]]
 +name = "ammonia"
 +version = "3.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d5ed2509ee88cc023cccee37a6fab35826830fe8b748b3869790e7720c2c4a74"
 +dependencies = [
 + "html5ever",
 + "maplit",
 + "once_cell",
 + "tendril",
 + "url",
 +]
 +
 +[[package]]
 +name = "annotate-snippets"
 +version = "0.9.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c3b9d411ecbaf79885c6df4d75fff75858d5995ff25385657a28af47e82f9c36"
 +dependencies = [
 + "unicode-width",
 + "yansi-term",
 +]
 +
 +[[package]]
 +name = "ansi_term"
 +version = "0.12.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "anyhow"
 +version = "1.0.60"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c794e162a5eff65c72ef524dfe393eb923c354e350bb78b9c7383df13f3bc142"
 +
 +[[package]]
 +name = "array_tool"
 +version = "1.0.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8f8cb5d814eb646a863c4f24978cff2880c4be96ad8cde2c0f0678732902e271"
 +
 +[[package]]
 +name = "arrayvec"
 +version = "0.7.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5a2f58b0bb10c380af2b26e57212856b8c9a59e0925b4c20f4a174a49734eaf7"
 +
 +[[package]]
 +name = "askama"
 +version = "0.11.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4d8f355701c672c2ba3d718acbd213f740beea577cc4eae66accdffe15be1882"
 +dependencies = [
 + "askama_derive",
 + "askama_escape",
 + "askama_shared",
 +]
 +
 +[[package]]
 +name = "askama_derive"
 +version = "0.11.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "84704cab5b7ae0fd3a9f78ee5eb7b27f3749df445f04623db6633459ae283267"
 +dependencies = [
 + "askama_shared",
 + "proc-macro2",
 + "syn",
 +]
 +
 +[[package]]
 +name = "askama_escape"
 +version = "0.10.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9a1bb320f97e6edf9f756bf015900038e43c7700e059688e5724a928c8f3b8d5"
 +
 +[[package]]
 +name = "askama_shared"
 +version = "0.12.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "dae03eebba55a2697a376e58b573a29fe36893157173ac8df312ad85f3c0e012"
 +dependencies = [
 + "askama_escape",
 + "nom",
 + "proc-macro2",
 + "quote",
 + "serde",
 + "syn",
 + "toml",
 +]
 +
 +[[package]]
 +name = "atty"
 +version = "0.2.14"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
 +dependencies = [
 + "hermit-abi 0.1.19",
 + "libc",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "autocfg"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 +
 +[[package]]
 +name = "backtrace"
 +version = "0.3.66"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
 +dependencies = [
 + "addr2line 0.17.0",
 + "cc",
 + "cfg-if 1.0.0",
 + "libc",
 + "miniz_oxide 0.5.3",
 + "object 0.29.0",
 + "rustc-demangle",
 +]
 +
 +[[package]]
 +name = "bitflags"
 +version = "1.3.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 +
 +[[package]]
 +name = "bitmaps"
 +version = "2.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
 +dependencies = [
 + "typenum",
 +]
 +
 +[[package]]
 +name = "block-buffer"
 +version = "0.7.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
 +dependencies = [
 + "block-padding",
 + "byte-tools",
 + "byteorder",
 + "generic-array 0.12.4",
 +]
 +
 +[[package]]
 +name = "block-buffer"
 +version = "0.10.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
 +dependencies = [
 + "generic-array 0.14.4",
 +]
 +
 +[[package]]
 +name = "block-padding"
 +version = "0.1.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
 +dependencies = [
 + "byte-tools",
 +]
 +
 +[[package]]
 +name = "bstr"
 +version = "0.2.17"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
 +dependencies = [
 + "lazy_static",
 + "memchr",
 + "regex-automata",
 +]
 +
 +[[package]]
 +name = "build-manifest"
 +version = "0.1.0"
 +dependencies = [
 + "anyhow",
 + "flate2",
 + "hex 0.4.2",
 + "rayon",
 + "serde",
 + "serde_json",
 + "sha2",
 + "tar",
 + "toml",
 +]
 +
 +[[package]]
 +name = "bump-stage0"
 +version = "0.1.0"
 +dependencies = [
 + "anyhow",
 + "curl",
 + "indexmap",
 + "serde",
 + "serde_json",
 + "toml",
 +]
 +
 +[[package]]
 +name = "byte-tools"
 +version = "0.3.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
 +
 +[[package]]
 +name = "bytecount"
 +version = "0.6.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
 +dependencies = [
 + "packed_simd_2",
 +]
 +
 +[[package]]
 +name = "byteorder"
 +version = "1.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
 +
 +[[package]]
 +name = "bytes"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
 +
 +[[package]]
 +name = "bytesize"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da"
 +
 +[[package]]
 +name = "camino"
 +version = "1.0.9"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "cargo"
 +version = "0.66.0"
 +dependencies = [
 + "anyhow",
 + "atty",
 + "bytesize",
 + "cargo-platform 0.1.2",
 + "cargo-test-macro",
 + "cargo-test-support",
 + "cargo-util",
 + "clap",
 + "crates-io",
 + "curl",
 + "curl-sys",
 + "env_logger 0.9.0",
 + "filetime",
 + "flate2",
 + "fwdansi",
 + "git2",
 + "git2-curl",
 + "glob",
 + "hex 0.4.2",
 + "home",
 + "humantime 2.0.1",
 + "ignore",
 + "im-rc",
 + "indexmap",
 + "itertools",
 + "jobserver",
 + "lazy_static",
 + "lazycell",
 + "libc",
 + "libgit2-sys",
 + "log",
 + "memchr",
 + "opener",
 + "openssl",
 + "os_info",
 + "pathdiff",
 + "percent-encoding",
 + "pretty_env_logger",
 + "rustc-workspace-hack",
 + "rustfix",
 + "semver",
 + "serde",
 + "serde_ignored",
 + "serde_json",
 + "shell-escape",
 + "snapbox",
 + "strip-ansi-escapes",
 + "tar",
 + "tempfile",
 + "termcolor",
 + "toml_edit",
 + "unicode-width",
 + "unicode-xid",
 + "url",
 + "walkdir",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "cargo-credential"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "cargo-credential-1password"
 +version = "0.1.0"
 +dependencies = [
 + "cargo-credential",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "cargo-credential-macos-keychain"
 +version = "0.1.0"
 +dependencies = [
 + "cargo-credential",
 + "security-framework",
 +]
 +
 +[[package]]
 +name = "cargo-credential-wincred"
 +version = "0.1.0"
 +dependencies = [
 + "cargo-credential",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "cargo-miri"
 +version = "0.1.0"
 +dependencies = [
 + "cargo_metadata 0.15.0",
 + "directories",
 + "rustc-workspace-hack",
 + "rustc_version",
 + "serde",
 + "serde_json",
 + "vergen",
 +]
 +
 +[[package]]
 +name = "cargo-platform"
 +version = "0.1.2"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "cargo-platform"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "cargo-test-macro"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "cargo-test-support"
 +version = "0.1.0"
 +dependencies = [
 + "anyhow",
 + "cargo-test-macro",
 + "cargo-util",
 + "filetime",
 + "flate2",
 + "git2",
 + "glob",
 + "itertools",
 + "lazy_static",
 + "remove_dir_all",
 + "serde_json",
 + "snapbox",
 + "tar",
 + "termcolor",
 + "toml_edit",
 + "url",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "cargo-util"
 +version = "0.2.1"
 +dependencies = [
 + "anyhow",
 + "core-foundation",
 + "crypto-hash",
 + "filetime",
 + "hex 0.4.2",
 + "jobserver",
 + "libc",
 + "log",
 + "miow",
 + "same-file",
 + "shell-escape",
 + "tempfile",
 + "walkdir",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "cargo_metadata"
 +version = "0.14.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c297bd3135f558552f99a0daa180876984ea2c4ffa7470314540dff8c654109a"
 +dependencies = [
 + "camino",
 + "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "semver",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "cargo_metadata"
 +version = "0.15.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3abb7553d5b9b8421c6de7cb02606ff15e0c6eea7d8eadd75ef013fd636bec36"
 +dependencies = [
 + "camino",
 + "cargo-platform 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 + "semver",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "cargotest2"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "cc"
 +version = "1.0.73"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
 +dependencies = [
 + "jobserver",
 +]
 +
 +[[package]]
 +name = "cfg-if"
 +version = "0.1.10"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "cfg-if"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 +
 +[[package]]
 +name = "chalk-derive"
 +version = "0.80.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d0001adf0cf12361e08b65e1898ea138f8f77d8f5177cbf29b6b3b3532252bd6"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 + "synstructure",
 +]
 +
 +[[package]]
 +name = "chalk-engine"
 +version = "0.80.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c44ee96f2d67cb5193d1503f185db1abad9933a1c6e6b4169c176f90baecd393"
 +dependencies = [
 + "chalk-derive",
 + "chalk-ir",
 + "chalk-solve",
 + "rustc-hash",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "chalk-ir"
 +version = "0.80.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "92d8a95548f23618fda86426e4304e563ec2bb7ba0216139f0748d63c107b5f1"
 +dependencies = [
 + "bitflags",
 + "chalk-derive",
 + "lazy_static",
 +]
 +
 +[[package]]
 +name = "chalk-solve"
 +version = "0.80.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f37f492dacfafe2e21319b80827da2779932909bb392f0cc86b2bd5c07c1b4e1"
 +dependencies = [
 + "chalk-derive",
 + "chalk-ir",
 + "ena",
 + "indexmap",
 + "itertools",
 + "petgraph",
 + "rustc-hash",
 + "tracing",
 + "tracing-subscriber",
 + "tracing-tree",
 +]
 +
 +[[package]]
 +name = "chrono"
 +version = "0.4.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
 +dependencies = [
 + "libc",
 + "num-integer",
 + "num-traits",
 + "time",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "clap"
 +version = "3.2.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d53da17d37dba964b9b3ecb5c5a1f193a2762c700e6829201e645b9381c99dc7"
 +dependencies = [
 + "atty",
 + "bitflags",
 + "clap_derive",
 + "clap_lex",
 + "indexmap",
 + "once_cell",
 + "strsim",
 + "termcolor",
 + "textwrap",
 +]
 +
 +[[package]]
 +name = "clap_complete"
 +version = "3.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "df6f3613c0a3cddfd78b41b10203eb322cb29b600cbdf808a7d3db95691b8e25"
 +dependencies = [
 + "clap",
 +]
 +
 +[[package]]
 +name = "clap_derive"
 +version = "3.2.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c11d40217d16aee8508cc8e5fde8b4ff24639758608e5374e731b53f85749fb9"
 +dependencies = [
 + "heck",
 + "proc-macro-error",
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "clap_lex"
 +version = "0.2.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5538cd660450ebeb4234cfecf8f2284b844ffc4c50531e66d584ad5b91293613"
 +dependencies = [
 + "os_str_bytes",
 +]
 +
 +[[package]]
 +name = "clippy"
 +version = "0.1.65"
 +dependencies = [
 + "clippy_lints",
 + "clippy_utils",
 + "compiletest_rs",
 + "derive-new",
 + "filetime",
 + "futures",
 + "if_chain",
 + "itertools",
 + "parking_lot 0.12.1",
 + "quote",
 + "regex",
 + "rustc-semver",
 + "rustc-workspace-hack",
 + "rustc_tools_util",
 + "semver",
 + "serde",
 + "syn",
 + "tempfile",
 + "termize",
 + "tester",
 + "tokio",
 + "toml",
 + "walkdir",
 +]
 +
 +[[package]]
 +name = "clippy_dev"
 +version = "0.0.1"
 +dependencies = [
 + "aho-corasick",
 + "clap",
 + "indoc",
 + "itertools",
 + "opener",
 + "shell-escape",
 + "tempfile",
 + "walkdir",
 +]
 +
 +[[package]]
 +name = "clippy_lints"
 +version = "0.1.65"
 +dependencies = [
 + "cargo_metadata 0.14.0",
 + "clippy_utils",
 + "if_chain",
 + "itertools",
 + "pulldown-cmark",
 + "quine-mc_cluskey",
 + "regex-syntax",
 + "rustc-semver",
 + "semver",
 + "serde",
 + "serde_json",
 + "tempfile",
 + "toml",
 + "unicode-normalization",
 + "unicode-script",
 + "url",
 +]
 +
 +[[package]]
 +name = "clippy_utils"
 +version = "0.1.65"
 +dependencies = [
 + "arrayvec",
 + "if_chain",
++ "itertools",
 + "rustc-semver",
 +]
 +
 +[[package]]
 +name = "color-eyre"
 +version = "0.6.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204"
 +dependencies = [
 + "backtrace",
 + "color-spantrace",
 + "eyre",
 + "indenter",
 + "once_cell",
 + "owo-colors",
 + "tracing-error",
 +]
 +
 +[[package]]
 +name = "color-spantrace"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce"
 +dependencies = [
 + "once_cell",
 + "owo-colors",
 + "tracing-core",
 + "tracing-error",
 +]
 +
 +[[package]]
 +name = "colored"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
 +dependencies = [
 + "atty",
 + "lazy_static",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "combine"
 +version = "4.6.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062"
 +dependencies = [
 + "bytes",
 + "memchr",
 +]
 +
 +[[package]]
 +name = "commoncrypto"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007"
 +dependencies = [
 + "commoncrypto-sys",
 +]
 +
 +[[package]]
 +name = "commoncrypto-sys"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "compiler_builtins"
 +version = "0.1.79"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4f873ce2bd3550b0b565f878b3d04ea8253f4259dc3d20223af2e1ba86f5ecca"
 +dependencies = [
 + "cc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "compiletest"
 +version = "0.0.0"
 +dependencies = [
 + "colored",
 + "diff",
 + "getopts",
 + "glob",
 + "lazy_static",
 + "lazycell",
 + "libc",
 + "miow",
 + "regex",
 + "rustfix",
 + "serde",
 + "serde_json",
 + "tracing",
 + "tracing-subscriber",
 + "unified-diff",
 + "walkdir",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "compiletest_rs"
 +version = "0.8.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "262134ef87408da1ddfe45e33daa0ca43b75286d6b1076446e602d264cf9847e"
 +dependencies = [
 + "diff",
 + "filetime",
 + "getopts",
 + "lazy_static",
 + "libc",
 + "log",
 + "miow",
 + "regex",
 + "rustfix",
 + "serde",
 + "serde_derive",
 + "serde_json",
 + "tempfile",
 + "tester",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "concolor"
 +version = "0.0.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "015267563b1df20adccdd00cb05257b1dfbea70a04928e9cf88ffb850c1a40af"
 +dependencies = [
 + "atty",
 + "bitflags",
 + "concolor-query",
 +]
 +
 +[[package]]
 +name = "concolor-query"
 +version = "0.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d6417fe6fc03a8b533fd2177742eeb39a90c7233eedec7bac96d4d6b69a09449"
 +
 +[[package]]
 +name = "content_inspector"
 +version = "0.2.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38"
 +dependencies = [
 + "memchr",
 +]
 +
 +[[package]]
 +name = "core"
 +version = "0.0.0"
 +dependencies = [
 + "rand 0.7.3",
 + "rand_xorshift",
 +]
 +
 +[[package]]
 +name = "core-foundation"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3b5ed8e7e76c45974e15e41bfa8d5b0483cd90191639e01d8f5f1e606299d3fb"
 +dependencies = [
 + "core-foundation-sys",
 + "libc",
 +]
 +
 +[[package]]
 +name = "core-foundation-sys"
 +version = "0.8.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9a21fa21941700a3cd8fcb4091f361a6a712fac632f85d9f487cc892045d55c6"
 +
 +[[package]]
 +name = "coverage_test_macros"
 +version = "0.0.0"
 +
 +[[package]]
 +name = "cpufeatures"
 +version = "0.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "crates-io"
 +version = "0.34.0"
 +dependencies = [
 + "anyhow",
 + "curl",
 + "percent-encoding",
 + "serde",
 + "serde_json",
 + "url",
 +]
 +
 +[[package]]
 +name = "crc32fast"
 +version = "1.3.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
 +dependencies = [
 + "cfg-if 1.0.0",
 +]
 +
 +[[package]]
 +name = "crossbeam"
 +version = "0.8.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "crossbeam-channel",
 + "crossbeam-deque",
 + "crossbeam-epoch",
 + "crossbeam-queue",
 + "crossbeam-utils",
 +]
 +
 +[[package]]
 +name = "crossbeam-channel"
 +version = "0.5.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "crossbeam-utils",
 +]
 +
 +[[package]]
 +name = "crossbeam-deque"
 +version = "0.8.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "crossbeam-epoch",
 + "crossbeam-utils",
 +]
 +
 +[[package]]
 +name = "crossbeam-epoch"
 +version = "0.9.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "crossbeam-utils",
 + "lazy_static",
 + "memoffset",
 + "scopeguard",
 +]
 +
 +[[package]]
 +name = "crossbeam-queue"
 +version = "0.3.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "crossbeam-utils",
 +]
 +
 +[[package]]
 +name = "crossbeam-utils"
 +version = "0.8.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "lazy_static",
 +]
 +
 +[[package]]
 +name = "crypto-common"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06"
 +dependencies = [
 + "generic-array 0.14.4",
 +]
 +
 +[[package]]
 +name = "crypto-hash"
 +version = "0.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8a77162240fd97248d19a564a565eb563a3f592b386e4136fb300909e67dddca"
 +dependencies = [
 + "commoncrypto",
 + "hex 0.3.2",
 + "openssl",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "cstr"
 +version = "0.2.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c11a39d776a3b35896711da8a04dc1835169dcd36f710878187637314e47941b"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 +]
 +
 +[[package]]
 +name = "ctor"
 +version = "0.1.22"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f877be4f7c9f246b183111634f75baa039715e3f46ce860677d3b19a69fb229c"
 +dependencies = [
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "curl"
 +version = "0.4.43"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "37d855aeef205b43f65a5001e0997d81f8efca7badad4fad7d897aa7f0d0651f"
 +dependencies = [
 + "curl-sys",
 + "libc",
 + "openssl-probe",
 + "openssl-sys",
 + "schannel",
 + "socket2",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "curl-sys"
 +version = "0.4.55+curl-7.83.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "23734ec77368ec583c2e61dd3f0b0e5c98b93abe6d2a004ca06b91dd7e3e2762"
 +dependencies = [
 + "cc",
 + "libc",
 + "libnghttp2-sys",
 + "libz-sys",
 + "openssl-sys",
 + "pkg-config",
 + "vcpkg",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "datafrog"
 +version = "2.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
 +
 +[[package]]
 +name = "derive-new"
 +version = "0.5.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "71f31892cd5c62e414316f2963c5689242c43d8e7bbcaaeca97e5e28c95d91d9"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "diff"
 +version = "0.1.13"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
 +
 +[[package]]
 +name = "difference"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
 +
 +[[package]]
 +name = "digest"
 +version = "0.8.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
 +dependencies = [
 + "generic-array 0.12.4",
 +]
 +
 +[[package]]
 +name = "digest"
 +version = "0.10.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837"
 +dependencies = [
 + "block-buffer 0.10.2",
 + "crypto-common",
 +]
 +
 +[[package]]
 +name = "directories"
 +version = "3.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7"
 +dependencies = [
 + "dirs-sys",
 +]
 +
 +[[package]]
 +name = "dirs"
 +version = "4.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
 +dependencies = [
 + "dirs-sys",
 +]
 +
 +[[package]]
 +name = "dirs-next"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "dirs-sys-next",
 +]
 +
 +[[package]]
 +name = "dirs-sys"
 +version = "0.3.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 +dependencies = [
 + "libc",
 + "redox_users",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "dirs-sys-next"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
 +dependencies = [
 + "libc",
 + "redox_users",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "dlmalloc"
 +version = "0.2.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a6fe28e0bf9357092740362502f5cc7955d8dc125ebda71dec72336c2e15c62e"
 +dependencies = [
 + "compiler_builtins",
 + "libc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "dunce"
 +version = "1.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
 +
 +[[package]]
 +name = "either"
 +version = "1.6.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cd56b59865bce947ac5958779cfa508f6c3b9497cc762b7e24a12d11ccde2c4f"
 +
 +[[package]]
 +name = "elasticlunr-rs"
 +version = "3.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e6dae5cac90640734ee881bc5f21b6e5123f4e5235e52428db114abffc2391d6"
 +dependencies = [
 + "regex",
 + "serde",
 + "serde_derive",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "ena"
 +version = "0.14.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d7402b94a93c24e742487327a7cd839dc9d36fec9de9fb25b09f2dae459f36c3"
 +dependencies = [
 + "log",
 +]
 +
 +[[package]]
 +name = "enum-iterator"
 +version = "0.6.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c79a6321a1197d7730510c7e3f6cb80432dfefecb32426de8cea0aa19b4bb8d7"
 +dependencies = [
 + "enum-iterator-derive",
 +]
 +
 +[[package]]
 +name = "enum-iterator-derive"
 +version = "0.6.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1e94aa31f7c0dc764f57896dc615ddd76fc13b0d5dca7eb6cc5e018a5a09ec06"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "env_logger"
 +version = "0.7.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
 +dependencies = [
 + "atty",
 + "humantime 1.3.0",
 + "log",
 + "regex",
 + "termcolor",
 +]
 +
 +[[package]]
 +name = "env_logger"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3"
 +dependencies = [
 + "atty",
 + "humantime 2.0.1",
 + "log",
 + "regex",
 + "termcolor",
 +]
 +
 +[[package]]
 +name = "error_index_generator"
 +version = "0.0.0"
 +dependencies = [
 + "rustdoc",
 +]
 +
 +[[package]]
 +name = "expand-yaml-anchors"
 +version = "0.1.0"
 +dependencies = [
 + "yaml-merge-keys",
 + "yaml-rust",
 +]
 +
 +[[package]]
 +name = "expect-test"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ceb96f3eaa0d4e8769c52dacfd4eb60183b817ed2f176171b3c691d5022b0f2e"
 +dependencies = [
 + "difference",
 + "once_cell",
 +]
 +
 +[[package]]
 +name = "eyre"
 +version = "0.6.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
 +dependencies = [
 + "indenter",
 + "once_cell",
 +]
 +
 +[[package]]
 +name = "fake-simd"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
 +
 +[[package]]
 +name = "fallible-iterator"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
 +
 +[[package]]
 +name = "filetime"
 +version = "0.2.14"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "libc",
 + "redox_syscall",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "fixedbitset"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d"
 +
 +[[package]]
 +name = "flate2"
 +version = "1.0.16"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "crc32fast",
 + "libc",
 + "libz-sys",
 + "miniz_oxide 0.4.0",
 +]
 +
 +[[package]]
 +name = "fluent-bundle"
 +version = "0.15.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd"
 +dependencies = [
 + "fluent-langneg",
 + "fluent-syntax",
 + "intl-memoizer",
 + "intl_pluralrules",
 + "rustc-hash",
 + "self_cell",
 + "smallvec",
 + "unic-langid",
 +]
 +
 +[[package]]
 +name = "fluent-langneg"
 +version = "0.13.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94"
 +dependencies = [
 + "unic-langid",
 +]
 +
 +[[package]]
 +name = "fluent-syntax"
 +version = "0.11.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78"
 +dependencies = [
 + "thiserror",
 +]
 +
 +[[package]]
 +name = "fnv"
 +version = "1.0.7"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 +
 +[[package]]
 +name = "foreign-types"
 +version = "0.3.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 +dependencies = [
 + "foreign-types-shared",
 +]
 +
 +[[package]]
 +name = "foreign-types-shared"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 +
 +[[package]]
 +name = "form_urlencoded"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
 +dependencies = [
 + "matches",
 + "percent-encoding",
 +]
 +
 +[[package]]
 +name = "fortanix-sgx-abi"
 +version = "0.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "57cafc2274c10fab234f176b25903ce17e690fca7597090d50880e047a0389c5"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "fs-err"
 +version = "2.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bcd1163ae48bda72a20ae26d66a04d3094135cadab911cff418ae5e33f253431"
 +
 +[[package]]
 +name = "fs_extra"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674"
 +
 +[[package]]
 +name = "futf"
 +version = "0.1.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
 +dependencies = [
 + "mac",
 + "new_debug_unreachable",
 +]
 +
 +[[package]]
 +name = "futures"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4"
 +dependencies = [
 + "futures-channel",
 + "futures-core",
 + "futures-executor",
 + "futures-io",
 + "futures-sink",
 + "futures-task",
 + "futures-util",
 +]
 +
 +[[package]]
 +name = "futures-channel"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b"
 +dependencies = [
 + "futures-core",
 + "futures-sink",
 +]
 +
 +[[package]]
 +name = "futures-core"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
 +
 +[[package]]
 +name = "futures-executor"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
 +dependencies = [
 + "futures-core",
 + "futures-task",
 + "futures-util",
 +]
 +
 +[[package]]
 +name = "futures-io"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2"
 +
 +[[package]]
 +name = "futures-macro"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "futures-sink"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508"
 +
 +[[package]]
 +name = "futures-task"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
 +
 +[[package]]
 +name = "futures-util"
 +version = "0.3.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
 +dependencies = [
 + "futures-channel",
 + "futures-core",
 + "futures-io",
 + "futures-macro",
 + "futures-sink",
 + "futures-task",
 + "memchr",
 + "pin-project-lite",
 + "pin-utils",
 + "slab",
 +]
 +
 +[[package]]
 +name = "fwdansi"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "08c1f5787fe85505d1f7777268db5103d80a7a374d2316a7ce262e57baf8f208"
 +dependencies = [
 + "memchr",
 + "termcolor",
 +]
 +
 +[[package]]
 +name = "generic-array"
 +version = "0.12.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
 +dependencies = [
 + "typenum",
 +]
 +
 +[[package]]
 +name = "generic-array"
 +version = "0.14.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
 +dependencies = [
 + "typenum",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "getopts"
 +version = "0.2.21"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
 +dependencies = [
 + "rustc-std-workspace-core",
 + "rustc-std-workspace-std",
 + "unicode-width",
 +]
 +
 +[[package]]
 +name = "getrandom"
 +version = "0.1.14"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "libc",
 + "wasi 0.9.0+wasi-snapshot-preview1",
 +]
 +
 +[[package]]
 +name = "getrandom"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "libc",
 + "wasi 0.9.0+wasi-snapshot-preview1",
 +]
 +
 +[[package]]
 +name = "getset"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "24b328c01a4d71d2d8173daa93562a73ab0fe85616876f02500f53d82948c504"
 +dependencies = [
 + "proc-macro-error",
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "gimli"
 +version = "0.25.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "gimli"
 +version = "0.26.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
 +dependencies = [
 + "fallible-iterator",
 + "indexmap",
 + "stable_deref_trait",
 +]
 +
 +[[package]]
 +name = "git2"
 +version = "0.15.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2994bee4a3a6a51eb90c218523be382fd7ea09b16380b9312e9dbe955ff7c7d1"
 +dependencies = [
 + "bitflags",
 + "libc",
 + "libgit2-sys",
 + "log",
 + "openssl-probe",
 + "openssl-sys",
 + "url",
 +]
 +
 +[[package]]
 +name = "git2-curl"
 +version = "0.16.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ed817a00721e2f8037ba722e60358d4956dae9cca10315fc982f967907d3b0cd"
 +dependencies = [
 + "curl",
 + "git2",
 + "log",
 + "url",
 +]
 +
 +[[package]]
 +name = "glob"
 +version = "0.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
 +
 +[[package]]
 +name = "globset"
 +version = "0.4.9"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
 +dependencies = [
 + "aho-corasick",
 + "bstr",
 + "fnv",
 + "log",
 + "regex",
 +]
 +
 +[[package]]
 +name = "gsgdt"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a0d876ce7262df96262a2a19531da6ff9a86048224d49580a585fc5c04617825"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "handlebars"
 +version = "4.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "72a0ffab8c36d0436114310c7e10b59b3307e650ddfabf6d006028e29a70c6e6"
 +dependencies = [
 + "log",
 + "pest",
 + "pest_derive",
 + "quick-error 2.0.0",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "hashbrown"
 +version = "0.12.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 +dependencies = [
 + "ahash",
 + "compiler_builtins",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "heck"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
 +
 +[[package]]
 +name = "hermit-abi"
 +version = "0.1.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "hermit-abi"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1ab7905ea95c6d9af62940f9d7dd9596d54c334ae2c15300c482051292d5637f"
 +dependencies = [
 + "compiler_builtins",
 + "libc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "hex"
 +version = "0.3.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77"
 +
 +[[package]]
 +name = "hex"
 +version = "0.4.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
 +
 +[[package]]
 +name = "home"
 +version = "0.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2456aef2e6b6a9784192ae780c0f15bc57df0e918585282325e8c8ac27737654"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "html-checker"
 +version = "0.1.0"
 +dependencies = [
 + "rayon",
 + "walkdir",
 +]
 +
 +[[package]]
 +name = "html5ever"
 +version = "0.26.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
 +dependencies = [
 + "log",
 + "mac",
 + "markup5ever",
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "humantime"
 +version = "1.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
 +dependencies = [
 + "quick-error 1.2.3",
 +]
 +
 +[[package]]
 +name = "humantime"
 +version = "2.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3c1ad908cc71012b7bea4d0c53ba96a8cba9962f048fa68d143376143d863b7a"
 +
 +[[package]]
 +name = "idna"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
 +dependencies = [
 + "matches",
 + "unicode-bidi",
 + "unicode-normalization",
 +]
 +
 +[[package]]
 +name = "if_chain"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d"
 +
 +[[package]]
 +name = "ignore"
 +version = "0.4.18"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
 +dependencies = [
 + "crossbeam-utils",
 + "globset",
 + "lazy_static",
 + "log",
 + "memchr",
 + "regex",
 + "same-file",
 + "thread_local",
 + "walkdir",
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "im-rc"
 +version = "15.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3ca8957e71f04a205cb162508f9326aea04676c8dfd0711220190d6b83664f3f"
 +dependencies = [
 + "bitmaps",
 + "rand_core 0.5.1",
 + "rand_xoshiro 0.4.0",
 + "sized-chunks",
 + "typenum",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "indenter"
 +version = "0.3.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
 +
 +[[package]]
 +name = "indexmap"
 +version = "1.9.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
 +dependencies = [
 + "autocfg",
 + "hashbrown",
 + "rustc-rayon",
 + "serde",
 +]
 +
 +[[package]]
 +name = "indoc"
 +version = "1.0.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "05a0bd019339e5d968b37855180087b7b9d512c5046fbd244cf8c95687927d6e"
 +
 +[[package]]
 +name = "installer"
 +version = "0.0.0"
 +dependencies = [
 + "anyhow",
 + "clap",
 + "flate2",
 + "lazy_static",
 + "num_cpus",
 + "rayon",
 + "remove_dir_all",
 + "tar",
 + "walkdir",
 + "winapi",
 + "xz2",
 +]
 +
 +[[package]]
 +name = "instant"
 +version = "0.1.12"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
 +dependencies = [
 + "cfg-if 1.0.0",
 +]
 +
 +[[package]]
 +name = "intl-memoizer"
 +version = "0.5.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f"
 +dependencies = [
 + "type-map",
 + "unic-langid",
 +]
 +
 +[[package]]
 +name = "intl_pluralrules"
 +version = "7.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf"
 +dependencies = [
 + "tinystr",
 + "unic-langid",
 +]
 +
 +[[package]]
 +name = "itertools"
 +version = "0.10.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
 +dependencies = [
 + "either",
 +]
 +
 +[[package]]
 +name = "itoa"
 +version = "1.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
 +
 +[[package]]
 +name = "jemalloc-sys"
 +version = "0.5.0+5.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f655c3ecfa6b0d03634595b4b54551d4bd5ac208b9e0124873949a7ab168f70b"
 +dependencies = [
 + "cc",
 + "fs_extra",
 + "libc",
 +]
 +
 +[[package]]
 +name = "jobserver"
 +version = "0.1.24"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "jsondocck"
 +version = "0.1.0"
 +dependencies = [
 + "fs-err",
 + "getopts",
 + "jsonpath_lib",
 + "once_cell",
 + "regex",
 + "serde_json",
 + "shlex",
 +]
 +
 +[[package]]
 +name = "jsonpath_lib"
 +version = "0.2.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "61352ec23883402b7d30b3313c16cbabefb8907361c4eb669d990cbb87ceee5a"
 +dependencies = [
 + "array_tool",
 + "env_logger 0.7.1",
 + "log",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "kstring"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
 +dependencies = [
 + "static_assertions",
 +]
 +
 +[[package]]
 +name = "lazy_static"
 +version = "1.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 +
 +[[package]]
 +name = "lazycell"
 +version = "1.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
 +
 +[[package]]
 +name = "libc"
 +version = "0.2.131"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40"
 +dependencies = [
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "libgit2-sys"
 +version = "0.14.0+1.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "47a00859c70c8a4f7218e6d1cc32875c4b55f6799445b842b0d8ed5e4c3d959b"
 +dependencies = [
 + "cc",
 + "libc",
 + "libssh2-sys",
 + "libz-sys",
 + "openssl-sys",
 + "pkg-config",
 +]
 +
 +[[package]]
 +name = "libloading"
 +version = "0.7.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c0cf036d15402bea3c5d4de17b3fce76b3e4a56ebc1f577be0e7a72f7c607cf0"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "libm"
 +version = "0.1.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
 +
 +[[package]]
 +name = "libnghttp2-sys"
 +version = "0.1.4+1.41.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "03624ec6df166e79e139a2310ca213283d6b3c30810c54844f307086d4488df1"
 +dependencies = [
 + "cc",
 + "libc",
 +]
 +
 +[[package]]
 +name = "libssh2-sys"
 +version = "0.2.23"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b094a36eb4b8b8c8a7b4b8ae43b2944502be3e59cd87687595cf6b0a71b3f4ca"
 +dependencies = [
 + "cc",
 + "libc",
 + "libz-sys",
 + "openssl-sys",
 + "pkg-config",
 + "vcpkg",
 +]
 +
 +[[package]]
 +name = "libz-sys"
 +version = "1.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
 +dependencies = [
 + "cc",
 + "libc",
 + "pkg-config",
 + "vcpkg",
 +]
 +
 +[[package]]
 +name = "linkchecker"
 +version = "0.1.0"
 +dependencies = [
 + "once_cell",
 + "regex",
 +]
 +
 +[[package]]
 +name = "linked-hash-map"
 +version = "0.5.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
 +
 +[[package]]
 +name = "lint-docs"
 +version = "0.1.0"
 +dependencies = [
 + "serde_json",
 + "tempfile",
 + "walkdir",
 +]
 +
 +[[package]]
 +name = "lld-wrapper"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "lock_api"
 +version = "0.4.7"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
 +dependencies = [
 + "autocfg",
 + "scopeguard",
 +]
 +
 +[[package]]
 +name = "log"
 +version = "0.4.14"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
 +dependencies = [
 + "cfg-if 1.0.0",
 +]
 +
 +[[package]]
 +name = "lzma-sys"
 +version = "0.1.16"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f24f76ec44a8ac23a31915d6e326bca17ce88da03096f1ff194925dc714dac99"
 +dependencies = [
 + "cc",
 + "libc",
 + "pkg-config",
 +]
 +
 +[[package]]
 +name = "mac"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
 +
 +[[package]]
 +name = "maplit"
 +version = "1.0.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 +
 +[[package]]
 +name = "markup5ever"
 +version = "0.11.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
 +dependencies = [
 + "log",
 + "phf",
 + "phf_codegen",
 + "string_cache",
 + "string_cache_codegen",
 + "tendril",
 +]
 +
 +[[package]]
 +name = "matchers"
 +version = "0.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
 +dependencies = [
 + "regex-automata",
 +]
 +
 +[[package]]
 +name = "matches"
 +version = "0.1.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
 +
 +[[package]]
 +name = "md-5"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e6a38fc55c8bbc10058782919516f88826e70320db6d206aebc49611d24216ae"
 +dependencies = [
 + "digest 0.10.2",
 +]
 +
 +[[package]]
 +name = "mdbook"
 +version = "0.4.21"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "23f3e133c6d515528745ffd3b9f0c7d975ae039f0b6abb099f2168daa2afb4f9"
 +dependencies = [
 + "ammonia",
 + "anyhow",
 + "chrono",
 + "clap",
 + "clap_complete",
 + "elasticlunr-rs",
 + "env_logger 0.9.0",
 + "handlebars",
 + "lazy_static",
 + "log",
 + "memchr",
 + "opener",
 + "pulldown-cmark",
 + "regex",
 + "serde",
 + "serde_json",
 + "shlex",
 + "tempfile",
 + "toml",
 + "topological-sort",
 +]
 +
 +[[package]]
 +name = "measureme"
 +version = "10.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cbdc226fa10994e8f66a4d2f6f000148bc563a1c671b6dcd2135737018033d8a"
 +dependencies = [
 + "log",
 + "memmap2",
 + "parking_lot 0.11.2",
 + "perf-event-open-sys",
 + "rustc-hash",
 + "smallvec",
 +]
 +
 +[[package]]
 +name = "memchr"
 +version = "2.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "memmap2"
 +version = "0.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "04e3e85b970d650e2ae6d70592474087051c11c54da7f7b4949725c5735fbcc6"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "memoffset"
 +version = "0.6.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
 +dependencies = [
 + "autocfg",
 +]
 +
 +[[package]]
 +name = "minifier"
 +version = "0.2.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8eb022374af2f446981254e6bf9efb6e2c9e1a53176d395fca02792fd4435729"
 +
 +[[package]]
 +name = "minimal-lexical"
 +version = "0.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 +
 +[[package]]
 +name = "miniz_oxide"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f"
 +dependencies = [
 + "adler 0.2.3",
 + "compiler_builtins",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "miniz_oxide"
 +version = "0.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
 +dependencies = [
 + "adler 1.0.2",
 +]
 +
 +[[package]]
 +name = "miow"
 +version = "0.3.7"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "miri"
 +version = "0.1.0"
 +dependencies = [
 + "colored",
 + "env_logger 0.9.0",
 + "getrandom 0.2.0",
 + "lazy_static",
 + "libc",
 + "log",
 + "measureme",
 + "rand 0.8.5",
 + "regex",
 + "rustc-workspace-hack",
 + "shell-escape",
 + "smallvec",
 + "ui_test",
 +]
 +
 +[[package]]
 +name = "new_debug_unreachable"
 +version = "1.0.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
 +
 +[[package]]
 +name = "nom"
 +version = "7.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
 +dependencies = [
 + "memchr",
 + "minimal-lexical",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "normalize-line-endings"
 +version = "0.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
 +
 +[[package]]
 +name = "num-integer"
 +version = "0.1.43"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
 +dependencies = [
 + "autocfg",
 + "num-traits",
 +]
 +
 +[[package]]
 +name = "num-traits"
 +version = "0.2.12"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
 +dependencies = [
 + "autocfg",
 +]
 +
 +[[package]]
 +name = "num_cpus"
 +version = "1.13.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
 +dependencies = [
 + "hermit-abi 0.1.19",
 + "libc",
 +]
 +
 +[[package]]
 +name = "object"
 +version = "0.26.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
 +dependencies = [
 + "compiler_builtins",
 + "memchr",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "object"
 +version = "0.29.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
 +dependencies = [
 + "crc32fast",
 + "flate2",
 + "hashbrown",
 + "indexmap",
 + "memchr",
 +]
 +
 +[[package]]
 +name = "odht"
 +version = "0.3.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5a518809ac14b25b569624d0268eba1e88498f71615893dca57982bed7621abb"
 +dependencies = [
 + "cfg-if 1.0.0",
 +]
 +
 +[[package]]
 +name = "once_cell"
 +version = "1.12.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
 +
 +[[package]]
 +name = "opaque-debug"
 +version = "0.2.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
 +
 +[[package]]
 +name = "opener"
 +version = "0.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
 +dependencies = [
 + "bstr",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "openssl"
 +version = "0.10.38"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
 +dependencies = [
 + "bitflags",
 + "cfg-if 1.0.0",
 + "foreign-types",
 + "libc",
 + "once_cell",
 + "openssl-sys",
 +]
 +
 +[[package]]
 +name = "openssl-probe"
 +version = "0.1.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
 +
 +[[package]]
 +name = "openssl-src"
 +version = "111.22.0+1.1.1q"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853"
 +dependencies = [
 + "cc",
 +]
 +
 +[[package]]
 +name = "openssl-sys"
 +version = "0.9.72"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
 +dependencies = [
 + "autocfg",
 + "cc",
 + "libc",
 + "openssl-src",
 + "pkg-config",
 + "vcpkg",
 +]
 +
 +[[package]]
 +name = "os_info"
 +version = "3.5.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5209b2162b2c140df493a93689e04f8deab3a67634f5bc7a553c0a98e5b8d399"
 +dependencies = [
 + "log",
 + "serde",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "os_str_bytes"
 +version = "6.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
 +
 +[[package]]
 +name = "output_vt100"
 +version = "0.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "owo-colors"
 +version = "3.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
 +
 +[[package]]
 +name = "packed_simd_2"
 +version = "0.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3278e0492f961fd4ae70909f56b2723a7e8d01a228427294e19cdfdebda89a17"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "libm",
 +]
 +
 +[[package]]
 +name = "panic_abort"
 +version = "0.0.0"
 +dependencies = [
 + "alloc",
 + "cfg-if 0.1.10",
 + "compiler_builtins",
 + "core",
 + "libc",
 +]
 +
 +[[package]]
 +name = "panic_unwind"
 +version = "0.0.0"
 +dependencies = [
 + "alloc",
 + "cfg-if 0.1.10",
 + "compiler_builtins",
 + "core",
 + "libc",
 + "unwind",
 +]
 +
 +[[package]]
 +name = "parking_lot"
 +version = "0.11.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
 +dependencies = [
 + "instant",
 + "lock_api",
 + "parking_lot_core 0.8.5",
 +]
 +
 +[[package]]
 +name = "parking_lot"
 +version = "0.12.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
 +dependencies = [
 + "lock_api",
 + "parking_lot_core 0.9.3",
 +]
 +
 +[[package]]
 +name = "parking_lot_core"
 +version = "0.8.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "instant",
 + "libc",
 + "redox_syscall",
 + "smallvec",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "parking_lot_core"
 +version = "0.9.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "libc",
 + "redox_syscall",
 + "smallvec",
 + "windows-sys",
 +]
 +
 +[[package]]
 +name = "pathdiff"
 +version = "0.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
 +
 +[[package]]
 +name = "percent-encoding"
 +version = "2.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
 +
 +[[package]]
 +name = "perf-event-open-sys"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "pest"
 +version = "2.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
 +dependencies = [
 + "ucd-trie",
 +]
 +
 +[[package]]
 +name = "pest_derive"
 +version = "2.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
 +dependencies = [
 + "pest",
 + "pest_generator",
 +]
 +
 +[[package]]
 +name = "pest_generator"
 +version = "2.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
 +dependencies = [
 + "pest",
 + "pest_meta",
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "pest_meta"
 +version = "2.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
 +dependencies = [
 + "maplit",
 + "pest",
 + "sha-1 0.8.2",
 +]
 +
 +[[package]]
 +name = "petgraph"
 +version = "0.5.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7"
 +dependencies = [
 + "fixedbitset",
 + "indexmap",
 +]
 +
 +[[package]]
 +name = "phf"
 +version = "0.10.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
 +dependencies = [
 + "phf_shared",
 +]
 +
 +[[package]]
 +name = "phf_codegen"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
 +dependencies = [
 + "phf_generator",
 + "phf_shared",
 +]
 +
 +[[package]]
 +name = "phf_generator"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
 +dependencies = [
 + "phf_shared",
 + "rand 0.8.5",
 +]
 +
 +[[package]]
 +name = "phf_shared"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
 +dependencies = [
 + "siphasher",
 +]
 +
 +[[package]]
 +name = "pin-project-lite"
 +version = "0.2.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
 +
 +[[package]]
 +name = "pin-utils"
 +version = "0.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
 +
 +[[package]]
 +name = "pkg-config"
 +version = "0.3.18"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
 +
 +[[package]]
 +name = "polonius-engine"
 +version = "0.13.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c4e8e505342045d397d0b6674dcb82d6faf5cf40484d30eeb88fc82ef14e903f"
 +dependencies = [
 + "datafrog",
 + "log",
 + "rustc-hash",
 +]
 +
 +[[package]]
 +name = "ppv-lite86"
 +version = "0.2.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
 +
 +[[package]]
 +name = "precomputed-hash"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 +
 +[[package]]
 +name = "pretty_assertions"
 +version = "1.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563"
 +dependencies = [
 + "ansi_term",
 + "ctor",
 + "diff",
 + "output_vt100",
 +]
 +
 +[[package]]
 +name = "pretty_env_logger"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
 +dependencies = [
 + "env_logger 0.7.1",
 + "log",
 +]
 +
 +[[package]]
 +name = "proc-macro-error"
 +version = "1.0.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
 +dependencies = [
 + "proc-macro-error-attr",
 + "proc-macro2",
 + "quote",
 + "syn",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "proc-macro-error-attr"
 +version = "1.0.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "version_check",
 +]
 +
 +[[package]]
 +name = "proc-macro-hack"
 +version = "0.5.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
 +
 +[[package]]
 +name = "proc-macro2"
 +version = "1.0.37"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
 +dependencies = [
 + "unicode-xid",
 +]
 +
 +[[package]]
 +name = "proc_macro"
 +version = "0.0.0"
 +dependencies = [
 + "core",
 + "std",
 +]
 +
 +[[package]]
 +name = "profiler_builtins"
 +version = "0.0.0"
 +dependencies = [
 + "cc",
 + "compiler_builtins",
 + "core",
 +]
 +
 +[[package]]
 +name = "psm"
 +version = "0.1.16"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cd136ff4382c4753fc061cb9e4712ab2af263376b95bbd5bd8cd50c020b78e69"
 +dependencies = [
 + "cc",
 +]
 +
 +[[package]]
 +name = "pulldown-cmark"
 +version = "0.9.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63"
 +dependencies = [
 + "bitflags",
 + "memchr",
 + "unicase",
 +]
 +
 +[[package]]
 +name = "punycode"
 +version = "0.4.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e9e1dcb320d6839f6edb64f7a4a59d39b30480d4d1765b56873f7c858538a5fe"
 +
 +[[package]]
 +name = "quick-error"
 +version = "1.2.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
 +
 +[[package]]
 +name = "quick-error"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3ac73b1112776fc109b2e61909bc46c7e1bf0d7f690ffb1676553acce16d5cda"
 +
 +[[package]]
 +name = "quine-mc_cluskey"
 +version = "0.2.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "07589615d719a60c8dd8a4622e7946465dfef20d1a428f969e3443e7386d5f45"
 +
 +[[package]]
 +name = "quote"
 +version = "1.0.18"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
 +dependencies = [
 + "proc-macro2",
 +]
 +
 +[[package]]
 +name = "rand"
 +version = "0.7.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
 +dependencies = [
 + "getrandom 0.1.14",
 + "libc",
 + "rand_chacha 0.2.2",
 + "rand_core 0.5.1",
 + "rand_hc",
 +]
 +
 +[[package]]
 +name = "rand"
 +version = "0.8.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
 +dependencies = [
 + "libc",
 + "rand_chacha 0.3.0",
 + "rand_core 0.6.2",
 +]
 +
 +[[package]]
 +name = "rand_chacha"
 +version = "0.2.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
 +dependencies = [
 + "ppv-lite86",
 + "rand_core 0.5.1",
 +]
 +
 +[[package]]
 +name = "rand_chacha"
 +version = "0.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
 +dependencies = [
 + "ppv-lite86",
 + "rand_core 0.6.2",
 +]
 +
 +[[package]]
 +name = "rand_core"
 +version = "0.5.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 +dependencies = [
 + "getrandom 0.1.14",
 +]
 +
 +[[package]]
 +name = "rand_core"
 +version = "0.6.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
 +dependencies = [
 + "getrandom 0.2.0",
 +]
 +
 +[[package]]
 +name = "rand_hc"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
 +dependencies = [
 + "rand_core 0.5.1",
 +]
 +
 +[[package]]
 +name = "rand_xorshift"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8"
 +dependencies = [
 + "rand_core 0.5.1",
 +]
 +
 +[[package]]
 +name = "rand_xoshiro"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
 +dependencies = [
 + "rand_core 0.5.1",
 +]
 +
 +[[package]]
 +name = "rand_xoshiro"
 +version = "0.6.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa"
 +dependencies = [
 + "rand_core 0.6.2",
 +]
 +
 +[[package]]
 +name = "rayon"
 +version = "1.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
 +dependencies = [
 + "autocfg",
 + "crossbeam-deque",
 + "either",
 + "rayon-core",
 +]
 +
 +[[package]]
 +name = "rayon-core"
 +version = "1.9.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
 +dependencies = [
 + "crossbeam-channel",
 + "crossbeam-deque",
 + "crossbeam-utils",
 + "num_cpus",
 +]
 +
 +[[package]]
 +name = "redox_syscall"
 +version = "0.2.10"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
 +dependencies = [
 + "bitflags",
 +]
 +
 +[[package]]
 +name = "redox_users"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
 +dependencies = [
 + "getrandom 0.2.0",
 + "redox_syscall",
 +]
 +
 +[[package]]
 +name = "regex"
 +version = "1.5.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1"
 +dependencies = [
 + "aho-corasick",
 + "memchr",
 + "regex-syntax",
 +]
 +
 +[[package]]
 +name = "regex-automata"
 +version = "0.1.10"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
 +dependencies = [
 + "regex-syntax",
 +]
 +
 +[[package]]
 +name = "regex-syntax"
 +version = "0.6.26"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64"
 +
 +[[package]]
 +name = "remote-test-client"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "remote-test-server"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "remove_dir_all"
 +version = "0.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "replace-version-placeholder"
 +version = "0.1.0"
 +dependencies = [
 + "tidy",
 + "walkdir",
 +]
 +
 +[[package]]
 +name = "rls"
 +version = "2.0.0"
 +dependencies = [
 + "rustc-workspace-hack",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "rls-data"
 +version = "0.19.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a58135eb039f3a3279a33779192f0ee78b56f57ae636e25cec83530e41debb99"
 +dependencies = [
 + "rls-span",
 + "serde",
 +]
 +
 +[[package]]
 +name = "rls-span"
 +version = "0.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f0eea58478fc06e15f71b03236612173a1b81e9770314edecfa664375e3e4c86"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "rust-demangler"
 +version = "0.0.1"
 +dependencies = [
 + "regex",
 + "rustc-demangle",
 +]
 +
 +[[package]]
 +name = "rustbook"
 +version = "0.1.0"
 +dependencies = [
 + "clap",
 + "env_logger 0.7.1",
 + "mdbook",
 +]
 +
 +[[package]]
 +name = "rustc-demangle"
 +version = "0.1.21"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "rustc-hash"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 +
 +[[package]]
 +name = "rustc-main"
 +version = "0.0.0"
 +dependencies = [
 + "jemalloc-sys",
 + "rustc_codegen_ssa",
 + "rustc_driver",
 + "rustc_smir",
 +]
 +
 +[[package]]
 +name = "rustc-rayon"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1a79f0b0b2609e2eacf9758013f50e7176cb4b29fd6436a747b14a5362c8727a"
 +dependencies = [
 + "autocfg",
 + "crossbeam-deque",
 + "either",
 + "rustc-rayon-core",
 +]
 +
 +[[package]]
 +name = "rustc-rayon-core"
 +version = "0.4.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "02269144a0db9bb55cf5d4a41a5a0e95b334b0b78b08269018ca9b0250718c30"
 +dependencies = [
 + "crossbeam-channel",
 + "crossbeam-deque",
 + "crossbeam-utils",
 + "num_cpus",
 +]
 +
 +[[package]]
 +name = "rustc-semver"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5be1bdc7edf596692617627bbfeaba522131b18e06ca4df2b6b689e3c5d5ce84"
 +
 +[[package]]
 +name = "rustc-std-workspace-alloc"
 +version = "1.99.0"
 +dependencies = [
 + "alloc",
 +]
 +
 +[[package]]
 +name = "rustc-std-workspace-core"
 +version = "1.99.0"
 +dependencies = [
 + "core",
 +]
 +
 +[[package]]
 +name = "rustc-std-workspace-std"
 +version = "1.99.0"
 +dependencies = [
 + "std",
 +]
 +
 +[[package]]
 +name = "rustc-workspace-hack"
 +version = "1.0.0"
 +dependencies = [
 + "bstr",
 + "clap",
 + "libz-sys",
 + "memchr",
 + "regex",
 + "serde_json",
 + "syn",
 + "url",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "rustc_apfloat"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "smallvec",
 +]
 +
 +[[package]]
 +name = "rustc_arena"
 +version = "0.0.0"
 +dependencies = [
 + "smallvec",
 +]
 +
 +[[package]]
 +name = "rustc_ast"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "rustc_data_structures",
 + "rustc_index",
 + "rustc_lexer",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_ast_lowering"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_query_system",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_ast_passes"
 +version = "0.0.0"
 +dependencies = [
 + "itertools",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_macros",
 + "rustc_parse",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_ast_pretty"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_span",
 +]
 +
 +[[package]]
 +name = "rustc_attr"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_lexer",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 +]
 +
 +[[package]]
 +name = "rustc_borrowck"
 +version = "0.0.0"
 +dependencies = [
 + "either",
 + "itertools",
 + "polonius-engine",
 + "rustc_const_eval",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_graphviz",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_lexer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_mir_dataflow",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "rustc_traits",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_builtin_macros"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_expand",
 + "rustc_feature",
 + "rustc_lexer",
 + "rustc_lint_defs",
 + "rustc_macros",
 + "rustc_parse",
 + "rustc_parse_format",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_codegen_llvm"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "cstr",
 + "libc",
 + "libloading",
 + "measureme",
 + "object 0.29.0",
 + "rustc-demangle",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_codegen_ssa",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_fs_util",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_llvm",
 + "rustc_macros",
 + "rustc_metadata",
 + "rustc_middle",
 + "rustc_query_system",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_symbol_mangling",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_codegen_ssa"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "cc",
 + "itertools",
 + "jobserver",
 + "libc",
 + "object 0.29.0",
 + "pathdiff",
 + "regex",
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_const_eval",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_fs_util",
 + "rustc_hir",
 + "rustc_incremental",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_metadata",
 + "rustc_middle",
 + "rustc_query_system",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_symbol_mangling",
 + "rustc_target",
 + "serde_json",
 + "smallvec",
 + "snap",
 + "tempfile",
 + "thorin-dwp",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_const_eval"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_apfloat",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_mir_dataflow",
 + "rustc_query_system",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "rustc_type_ir",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_data_structures"
 +version = "0.0.0"
 +dependencies = [
 + "arrayvec",
 + "bitflags",
 + "cfg-if 0.1.10",
 + "ena",
 + "indexmap",
 + "jobserver",
 + "libc",
 + "measureme",
 + "memmap2",
 + "parking_lot 0.11.2",
 + "rustc-hash",
 + "rustc-rayon",
 + "rustc-rayon-core",
 + "rustc_graphviz",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_serialize",
 + "smallvec",
 + "stable_deref_trait",
 + "stacker",
 + "tempfile",
 + "tracing",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "rustc_driver"
 +version = "0.0.0"
 +dependencies = [
 + "libc",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_codegen_ssa",
 + "rustc_data_structures",
 + "rustc_error_codes",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_hir",
 + "rustc_hir_pretty",
 + "rustc_interface",
 + "rustc_lint",
 + "rustc_log",
 + "rustc_macros",
 + "rustc_metadata",
 + "rustc_middle",
 + "rustc_parse",
 + "rustc_plugin_impl",
 + "rustc_save_analysis",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_typeck",
 + "serde_json",
 + "tracing",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "rustc_error_codes"
 +version = "0.0.0"
 +
 +[[package]]
 +name = "rustc_error_messages"
 +version = "0.0.0"
 +dependencies = [
 + "fluent-bundle",
 + "fluent-syntax",
 + "intl-memoizer",
 + "rustc_data_structures",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "tracing",
 + "unic-langid",
 +]
 +
 +[[package]]
 +name = "rustc_errors"
 +version = "0.0.0"
 +dependencies = [
 + "annotate-snippets",
 + "atty",
 + "rustc_data_structures",
 + "rustc_error_messages",
 + "rustc_hir",
 + "rustc_lint_defs",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "serde",
 + "serde_json",
 + "termcolor",
 + "termize",
 + "tracing",
 + "unicode-width",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "rustc_expand"
 +version = "0.0.0"
 +dependencies = [
 + "crossbeam-channel",
 + "rustc_ast",
 + "rustc_ast_passes",
 + "rustc_ast_pretty",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_lexer",
 + "rustc_lint_defs",
 + "rustc_macros",
 + "rustc_parse",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_feature"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_data_structures",
 + "rustc_span",
 +]
 +
 +[[package]]
 +name = "rustc_fs_util"
 +version = "0.0.0"
 +
 +[[package]]
 +name = "rustc_graphviz"
 +version = "0.0.0"
 +
 +[[package]]
 +name = "rustc_hir"
 +version = "0.0.0"
 +dependencies = [
 + "odht",
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_error_messages",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_hir_pretty"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_hir",
 + "rustc_span",
 + "rustc_target",
 +]
 +
 +[[package]]
 +name = "rustc_incremental"
 +version = "0.0.0"
 +dependencies = [
 + "rand 0.8.5",
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_fs_util",
 + "rustc_graphviz",
 + "rustc_hir",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_index"
 +version = "0.0.0"
 +dependencies = [
 + "arrayvec",
 + "rustc_macros",
 + "rustc_serialize",
 + "smallvec",
 +]
 +
 +[[package]]
 +name = "rustc_infer"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_interface"
 +version = "0.0.0"
 +dependencies = [
 + "libc",
 + "libloading",
 + "rustc-rayon",
 + "rustc-rayon-core",
 + "rustc_ast",
 + "rustc_ast_lowering",
 + "rustc_ast_passes",
 + "rustc_attr",
 + "rustc_borrowck",
 + "rustc_builtin_macros",
 + "rustc_codegen_llvm",
 + "rustc_codegen_ssa",
 + "rustc_const_eval",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_expand",
 + "rustc_hir",
 + "rustc_incremental",
 + "rustc_lint",
 + "rustc_macros",
 + "rustc_metadata",
 + "rustc_middle",
 + "rustc_mir_build",
 + "rustc_mir_transform",
 + "rustc_monomorphize",
 + "rustc_parse",
 + "rustc_passes",
 + "rustc_plugin_impl",
 + "rustc_privacy",
 + "rustc_query_impl",
 + "rustc_resolve",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_symbol_mangling",
 + "rustc_target",
 + "rustc_trait_selection",
 + "rustc_traits",
 + "rustc_ty_utils",
 + "rustc_typeck",
 + "smallvec",
 + "tracing",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "rustc_lexer"
 +version = "0.1.0"
 +dependencies = [
 + "expect-test",
 + "unic-emoji-char",
 + "unicode-xid",
 +]
 +
 +[[package]]
 +name = "rustc_lint"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_parse_format",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "rustc_type_ir",
 + "tracing",
 + "unicode-security",
 +]
 +
 +[[package]]
 +name = "rustc_lint_defs"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_error_messages",
 + "rustc_hir",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "rustc_target",
 + "serde",
 +]
 +
 +[[package]]
 +name = "rustc_llvm"
 +version = "0.0.0"
 +dependencies = [
 + "cc",
 + "libc",
 +]
 +
 +[[package]]
 +name = "rustc_log"
 +version = "0.0.0"
 +dependencies = [
 + "atty",
 + "rustc_span",
 + "tracing",
 + "tracing-subscriber",
 + "tracing-tree",
 +]
 +
 +[[package]]
 +name = "rustc_macros"
 +version = "0.1.0"
 +dependencies = [
 + "annotate-snippets",
 + "fluent-bundle",
 + "fluent-syntax",
 + "proc-macro2",
 + "quote",
 + "syn",
 + "synstructure",
 + "unic-langid",
 +]
 +
 +[[package]]
 +name = "rustc_metadata"
 +version = "0.0.0"
 +dependencies = [
 + "libloading",
 + "odht",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_expand",
 + "rustc_feature",
 + "rustc_hir",
 + "rustc_hir_pretty",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_type_ir",
 + "smallvec",
 + "snap",
 + "tempfile",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_middle"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "chalk-ir",
 + "either",
 + "gsgdt",
 + "polonius-engine",
 + "rand 0.8.5",
 + "rand_xoshiro 0.6.0",
 + "rustc-rayon",
 + "rustc-rayon-core",
 + "rustc_apfloat",
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_graphviz",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_query_system",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_type_ir",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_mir_build"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_apfloat",
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_mir_dataflow"
 +version = "0.0.0"
 +dependencies = [
 + "polonius-engine",
 + "regex",
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_graphviz",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_mir_transform"
 +version = "0.0.0"
 +dependencies = [
 + "coverage_test_macros",
 + "itertools",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_const_eval",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_middle",
 + "rustc_mir_dataflow",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_monomorphize"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_data_structures",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_middle",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_parse"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_lexer",
 + "rustc_macros",
 + "rustc_session",
 + "rustc_span",
 + "tracing",
 + "unicode-normalization",
 + "unicode-width",
 +]
 +
 +[[package]]
 +name = "rustc_parse_format"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_lexer",
 +]
 +
 +[[package]]
 +name = "rustc_passes"
 +version = "0.0.0"
 +dependencies = [
 + "itertools",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_expand",
 + "rustc_feature",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_lexer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_plugin_impl"
 +version = "0.0.0"
 +dependencies = [
 + "libloading",
 + "rustc_ast",
 + "rustc_errors",
 + "rustc_lint",
 + "rustc_macros",
 + "rustc_metadata",
 + "rustc_session",
 + "rustc_span",
 +]
 +
 +[[package]]
 +name = "rustc_privacy"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_session",
 + "rustc_span",
 + "rustc_trait_selection",
 + "rustc_typeck",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_query_impl"
 +version = "0.0.0"
 +dependencies = [
 + "measureme",
 + "rustc-rayon-core",
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_query_system",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_query_system"
 +version = "0.0.0"
 +dependencies = [
 + "parking_lot 0.11.2",
 + "rustc-rayon-core",
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_resolve"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_expand",
 + "rustc_feature",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_metadata",
 + "rustc_middle",
 + "rustc_query_system",
 + "rustc_session",
 + "rustc_span",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_save_analysis"
 +version = "0.0.0"
 +dependencies = [
 + "rls-data",
 + "rls-span",
 + "rustc_ast",
 + "rustc_ast_pretty",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_hir_pretty",
 + "rustc_lexer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_session",
 + "rustc_span",
 + "serde_json",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_serialize"
 +version = "0.0.0"
 +dependencies = [
 + "indexmap",
 + "rustc_macros",
 + "smallvec",
 +]
 +
 +[[package]]
 +name = "rustc_session"
 +version = "0.0.0"
 +dependencies = [
 + "getopts",
 + "rustc_ast",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_fs_util",
 + "rustc_hir",
 + "rustc_lint_defs",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "rustc_target",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_smir"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_borrowck",
 + "rustc_driver",
 + "rustc_hir",
 + "rustc_interface",
 + "rustc_middle",
 + "rustc_mir_dataflow",
 + "rustc_mir_transform",
 + "rustc_serialize",
 + "rustc_trait_selection",
 +]
 +
 +[[package]]
 +name = "rustc_span"
 +version = "0.0.0"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "md-5",
 + "rustc_arena",
 + "rustc_data_structures",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_serialize",
 + "scoped-tls",
 + "sha-1 0.10.0",
 + "sha2",
 + "tracing",
 + "unicode-width",
 +]
 +
 +[[package]]
 +name = "rustc_symbol_mangling"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "punycode",
 + "rustc-demangle",
 + "rustc_data_structures",
 + "rustc_hir",
 + "rustc_middle",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_target"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "rustc_data_structures",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_serialize",
 + "rustc_span",
 + "serde_json",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_tools_util"
 +version = "0.2.0"
 +
 +[[package]]
 +name = "rustc_trait_selection"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_lint_defs",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_parse_format",
 + "rustc_query_system",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_transmute",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_traits"
 +version = "0.0.0"
 +dependencies = [
 + "chalk-engine",
 + "chalk-ir",
 + "chalk-solve",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_middle",
 + "rustc_span",
 + "rustc_trait_selection",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_transmute"
 +version = "0.1.0"
 +dependencies = [
 + "itertools",
 + "rustc_data_structures",
 + "rustc_infer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_span",
 + "rustc_target",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_ty_utils"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_hir",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "rustc_type_ir",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_type_ir"
 +version = "0.0.0"
 +dependencies = [
 + "bitflags",
 + "rustc_data_structures",
 + "rustc_index",
 + "rustc_macros",
 + "rustc_serialize",
 + "smallvec",
 +]
 +
 +[[package]]
 +name = "rustc_typeck"
 +version = "0.0.0"
 +dependencies = [
 + "rustc_arena",
 + "rustc_ast",
 + "rustc_attr",
 + "rustc_data_structures",
 + "rustc_errors",
 + "rustc_feature",
 + "rustc_graphviz",
 + "rustc_hir",
 + "rustc_hir_pretty",
 + "rustc_index",
 + "rustc_infer",
 + "rustc_lint",
 + "rustc_macros",
 + "rustc_middle",
 + "rustc_serialize",
 + "rustc_session",
 + "rustc_span",
 + "rustc_target",
 + "rustc_trait_selection",
 + "rustc_ty_utils",
 + "rustc_type_ir",
 + "smallvec",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "rustc_version"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
 +dependencies = [
 + "semver",
 +]
 +
 +[[package]]
 +name = "rustdoc"
 +version = "0.0.0"
 +dependencies = [
 + "arrayvec",
 + "askama",
 + "atty",
 + "expect-test",
 + "itertools",
 + "minifier",
 + "once_cell",
 + "pulldown-cmark",
 + "rayon",
 + "regex",
 + "rustdoc-json-types",
 + "serde",
 + "serde_json",
 + "smallvec",
 + "tempfile",
 + "tracing",
 + "tracing-subscriber",
 + "tracing-tree",
 +]
 +
 +[[package]]
 +name = "rustdoc-json-types"
 +version = "0.1.0"
 +dependencies = [
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "rustdoc-themes"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "rustdoc-tool"
 +version = "0.0.0"
 +dependencies = [
 + "rustdoc",
 +]
 +
 +[[package]]
 +name = "rustfix"
 +version = "0.6.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481"
 +dependencies = [
 + "anyhow",
 + "log",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "rustfmt-config_proc_macro"
 +version = "0.2.0"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "serde",
 + "syn",
 +]
 +
 +[[package]]
 +name = "rustfmt-nightly"
 +version = "1.5.1"
 +dependencies = [
 + "annotate-snippets",
 + "anyhow",
 + "bytecount",
 + "cargo_metadata 0.14.0",
 + "clap",
 + "derive-new",
 + "diff",
 + "dirs",
 + "env_logger 0.9.0",
 + "getopts",
 + "ignore",
 + "itertools",
 + "lazy_static",
 + "log",
 + "regex",
 + "rustc-workspace-hack",
 + "rustfmt-config_proc_macro",
 + "serde",
 + "serde_json",
 + "term",
 + "thiserror",
 + "toml",
 + "unicode-segmentation",
 + "unicode-width",
 + "unicode_categories",
 +]
 +
 +[[package]]
 +name = "rustversion"
 +version = "1.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
 +
 +[[package]]
 +name = "ryu"
 +version = "1.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
 +
 +[[package]]
 +name = "same-file"
 +version = "1.0.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
 +dependencies = [
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "schannel"
 +version = "0.1.19"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
 +dependencies = [
 + "lazy_static",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "scoped-tls"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
 +
 +[[package]]
 +name = "scopeguard"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
 +
 +[[package]]
 +name = "security-framework"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
 +dependencies = [
 + "bitflags",
 + "core-foundation",
 + "core-foundation-sys",
 + "libc",
 + "security-framework-sys",
 +]
 +
 +[[package]]
 +name = "security-framework-sys"
 +version = "2.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
 +dependencies = [
 + "core-foundation-sys",
 + "libc",
 +]
 +
 +[[package]]
 +name = "self_cell"
 +version = "0.10.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af"
 +
 +[[package]]
 +name = "semver"
 +version = "1.0.12"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "serde"
 +version = "1.0.143"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
 +dependencies = [
 + "serde_derive",
 +]
 +
 +[[package]]
 +name = "serde_derive"
 +version = "1.0.143"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "serde_ignored"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1c2c7d39d14f2f2ea82239de71594782f186fd03501ac81f0ce08e674819ff2f"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "serde_json"
 +version = "1.0.83"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
 +dependencies = [
 + "indexmap",
 + "itoa",
 + "ryu",
 + "serde",
 +]
 +
 +[[package]]
 +name = "sha-1"
 +version = "0.8.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
 +dependencies = [
 + "block-buffer 0.7.3",
 + "digest 0.8.1",
 + "fake-simd",
 + "opaque-debug",
 +]
 +
 +[[package]]
 +name = "sha-1"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "cpufeatures",
 + "digest 0.10.2",
 +]
 +
 +[[package]]
 +name = "sha2"
 +version = "0.10.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "99c3bd8169c58782adad9290a9af5939994036b76187f7b4f0e6de91dbbfc0ec"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "cpufeatures",
 + "digest 0.10.2",
 +]
 +
 +[[package]]
 +name = "sharded-slab"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "79c719719ee05df97490f80a45acfc99e5a30ce98a1e4fb67aee422745ae14e3"
 +dependencies = [
 + "lazy_static",
 +]
 +
 +[[package]]
 +name = "shell-escape"
 +version = "0.1.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
 +
 +[[package]]
 +name = "shlex"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
 +
 +[[package]]
 +name = "similar"
 +version = "2.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2e24979f63a11545f5f2c60141afe249d4f19f84581ea2138065e400941d83d3"
 +
 +[[package]]
 +name = "siphasher"
 +version = "0.3.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fa8f3741c7372e75519bd9346068370c9cdaabcc1f9599cbcf2a2719352286b7"
 +
 +[[package]]
 +name = "sized-chunks"
 +version = "0.6.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "65e65d6a9f13cd78f361ea5a2cf53a45d67cdda421ba0316b9be101560f3d207"
 +dependencies = [
 + "bitmaps",
 + "typenum",
 +]
 +
 +[[package]]
 +name = "slab"
 +version = "0.4.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
 +
 +[[package]]
 +name = "smallvec"
 +version = "1.8.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2"
 +
 +[[package]]
 +name = "snap"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "da73c8f77aebc0e40c300b93f0a5f1bece7a248a36eee287d4e095f35c7b7d6e"
 +
 +[[package]]
 +name = "snapbox"
 +version = "0.3.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "44d199ccf8f606592df2d145db26f2aa45344e23c64b074cc5a4047f1d99b0f7"
 +dependencies = [
 + "concolor",
 + "content_inspector",
 + "dunce",
 + "filetime",
 + "normalize-line-endings",
 + "similar",
 + "snapbox-macros",
 + "tempfile",
 + "walkdir",
 + "yansi",
 +]
 +
 +[[package]]
 +name = "snapbox-macros"
 +version = "0.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8a253e6f894cfa440cba00600a249fa90869d8e0ec45ab274a456e043a0ce8f2"
 +
 +[[package]]
 +name = "socket2"
 +version = "0.4.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad"
 +dependencies = [
 + "libc",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "stable_deref_trait"
 +version = "1.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
 +
 +[[package]]
 +name = "stacker"
 +version = "0.1.14"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "90939d5171a4420b3ff5fbc8954d641e7377335454c259dcb80786f3f21dc9b4"
 +dependencies = [
 + "cc",
 + "cfg-if 1.0.0",
 + "libc",
 + "psm",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "static_assertions"
 +version = "1.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
 +
 +[[package]]
 +name = "std"
 +version = "0.0.0"
 +dependencies = [
 + "addr2line 0.16.0",
 + "alloc",
 + "cfg-if 0.1.10",
 + "compiler_builtins",
 + "core",
 + "dlmalloc",
 + "fortanix-sgx-abi",
 + "hashbrown",
 + "hermit-abi 0.2.0",
 + "libc",
 + "miniz_oxide 0.4.0",
 + "object 0.26.2",
 + "panic_abort",
 + "panic_unwind",
 + "profiler_builtins",
 + "rand 0.7.3",
 + "rustc-demangle",
 + "std_detect",
 + "unwind",
 + "wasi 0.11.0+wasi-snapshot-preview1",
 +]
 +
 +[[package]]
 +name = "std_detect"
 +version = "0.1.5"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "compiler_builtins",
 + "libc",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "string_cache"
 +version = "0.8.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
 +dependencies = [
 + "lazy_static",
 + "new_debug_unreachable",
 + "parking_lot 0.11.2",
 + "phf_shared",
 + "precomputed-hash",
 + "serde",
 +]
 +
 +[[package]]
 +name = "string_cache_codegen"
 +version = "0.5.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
 +dependencies = [
 + "phf_generator",
 + "phf_shared",
 + "proc-macro2",
 + "quote",
 +]
 +
 +[[package]]
 +name = "strip-ansi-escapes"
 +version = "0.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9d63676e2abafa709460982ddc02a3bb586b6d15a49b75c212e06edd3933acee"
 +dependencies = [
 + "vte",
 +]
 +
 +[[package]]
 +name = "strsim"
 +version = "0.10.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 +
 +[[package]]
 +name = "syn"
 +version = "1.0.91"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "unicode-xid",
 +]
 +
 +[[package]]
 +name = "synstructure"
 +version = "0.12.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 + "unicode-xid",
 +]
 +
 +[[package]]
 +name = "tar"
 +version = "0.4.38"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
 +dependencies = [
 + "filetime",
 + "libc",
 + "xattr",
 +]
 +
 +[[package]]
 +name = "tempfile"
 +version = "3.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "libc",
 + "rand 0.8.5",
 + "redox_syscall",
 + "remove_dir_all",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "tendril"
 +version = "0.4.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
 +dependencies = [
 + "futf",
 + "mac",
 + "utf-8",
 +]
 +
 +[[package]]
 +name = "term"
 +version = "0.7.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
 +dependencies = [
 + "dirs-next",
 + "rustversion",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "termcolor"
 +version = "1.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 +dependencies = [
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "termize"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1706be6b564323ce7092f5f7e6b118a14c8ef7ed0e69c8c5329c914a9f101295"
 +dependencies = [
 + "libc",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "test"
 +version = "0.0.0"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "core",
 + "getopts",
 + "libc",
 + "panic_abort",
 + "panic_unwind",
 + "proc_macro",
 + "std",
 +]
 +
 +[[package]]
 +name = "tester"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0639d10d8f4615f223a57275cf40f9bdb7cfbb806bcb7f7cc56e3beb55a576eb"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "getopts",
 + "libc",
 + "num_cpus",
 + "term",
 +]
 +
 +[[package]]
 +name = "textwrap"
 +version = "0.15.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
 +
 +[[package]]
 +name = "thiserror"
 +version = "1.0.30"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
 +dependencies = [
 + "thiserror-impl",
 +]
 +
 +[[package]]
 +name = "thiserror-impl"
 +version = "1.0.30"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "thorin-dwp"
 +version = "0.3.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e6cb0c7868d7f90407531108ab03263d9452a8811b7cdd87675343a40d4aa254"
 +dependencies = [
 + "gimli 0.26.1",
 + "hashbrown",
 + "object 0.29.0",
 + "tracing",
 +]
 +
 +[[package]]
 +name = "thread_local"
 +version = "1.1.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
 +dependencies = [
 + "once_cell",
 +]
 +
 +[[package]]
 +name = "tidy"
 +version = "0.1.0"
 +dependencies = [
 + "cargo_metadata 0.14.0",
 + "lazy_static",
 + "regex",
 + "walkdir",
 +]
 +
 +[[package]]
 +name = "tier-check"
 +version = "0.1.0"
 +
 +[[package]]
 +name = "time"
 +version = "0.1.43"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
 +dependencies = [
 + "libc",
 + "winapi",
 +]
 +
 +[[package]]
 +name = "tinystr"
 +version = "0.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1"
 +
 +[[package]]
 +name = "tinyvec"
 +version = "0.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
 +
 +[[package]]
 +name = "tokio"
 +version = "1.8.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "50dae83881bc9b0403dd5b44ea9deed3e939856cc8722d5be37f0d6e5c6d53dd"
 +dependencies = [
 + "autocfg",
 + "bytes",
 + "memchr",
 + "pin-project-lite",
 +]
 +
 +[[package]]
 +name = "toml"
 +version = "0.5.7"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "75cf45bb0bef80604d001caaec0d09da99611b3c0fd39d3080468875cdb65645"
 +dependencies = [
 + "serde",
 +]
 +
 +[[package]]
 +name = "toml_edit"
 +version = "0.14.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ba98375fd631b83696f87c64e4ed8e29e6a1f3404d6aed95fa95163bad38e705"
 +dependencies = [
 + "combine",
 + "indexmap",
 + "itertools",
 + "kstring",
 + "serde",
 +]
 +
 +[[package]]
 +name = "topological-sort"
 +version = "0.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "aa7c7f42dea4b1b99439786f5633aeb9c14c1b53f75e282803c2ec2ad545873c"
 +
 +[[package]]
 +name = "tracing"
 +version = "0.1.35"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
 +dependencies = [
 + "cfg-if 1.0.0",
 + "pin-project-lite",
 + "tracing-attributes",
 + "tracing-core",
 +]
 +
 +[[package]]
 +name = "tracing-attributes"
 +version = "0.1.22"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2"
 +dependencies = [
 + "proc-macro2",
 + "quote",
 + "syn",
 +]
 +
 +[[package]]
 +name = "tracing-core"
 +version = "0.1.28"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7"
 +dependencies = [
 + "once_cell",
 + "valuable",
 +]
 +
 +[[package]]
 +name = "tracing-error"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e"
 +dependencies = [
 + "tracing",
 + "tracing-subscriber",
 +]
 +
 +[[package]]
 +name = "tracing-log"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
 +dependencies = [
 + "lazy_static",
 + "log",
 + "tracing-core",
 +]
 +
 +[[package]]
 +name = "tracing-subscriber"
 +version = "0.3.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "245da694cc7fc4729f3f418b304cb57789f1bed2a78c575407ab8a23f53cb4d3"
 +dependencies = [
 + "ansi_term",
 + "lazy_static",
 + "matchers",
 + "parking_lot 0.11.2",
 + "regex",
 + "sharded-slab",
 + "smallvec",
 + "thread_local",
 + "tracing",
 + "tracing-core",
 + "tracing-log",
 +]
 +
 +[[package]]
 +name = "tracing-tree"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3ce989c9962c7f61fe084dd4a230eec784649dfc2392467c790007c3a6e134e7"
 +dependencies = [
 + "ansi_term",
 + "atty",
 + "tracing-core",
 + "tracing-log",
 + "tracing-subscriber",
 +]
 +
 +[[package]]
 +name = "type-map"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46"
 +dependencies = [
 + "rustc-hash",
 +]
 +
 +[[package]]
 +name = "typenum"
 +version = "1.12.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
 +
 +[[package]]
 +name = "ucd-parse"
 +version = "0.1.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5269f8d35df6b8b60758343a6d742ecf09e4bca13faee32af5503aebd1e11b7c"
 +dependencies = [
 + "lazy_static",
 + "regex",
 +]
 +
 +[[package]]
 +name = "ucd-trie"
 +version = "0.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
 +
 +[[package]]
 +name = "ui_test"
 +version = "0.1.0"
 +dependencies = [
 + "cargo_metadata 0.15.0",
 + "color-eyre",
 + "colored",
 + "crossbeam",
 + "lazy_static",
 + "pretty_assertions",
 + "regex",
 + "rustc_version",
 + "serde",
 + "serde_json",
 +]
 +
 +[[package]]
 +name = "unic-char-property"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
 +dependencies = [
 + "unic-char-range",
 +]
 +
 +[[package]]
 +name = "unic-char-range"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
 +
 +[[package]]
 +name = "unic-common"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
 +
 +[[package]]
 +name = "unic-emoji-char"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d"
 +dependencies = [
 + "unic-char-property",
 + "unic-char-range",
 + "unic-ucd-version",
 +]
 +
 +[[package]]
 +name = "unic-langid"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5"
 +dependencies = [
 + "unic-langid-impl",
 + "unic-langid-macros",
 +]
 +
 +[[package]]
 +name = "unic-langid-impl"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d"
 +dependencies = [
 + "tinystr",
 +]
 +
 +[[package]]
 +name = "unic-langid-macros"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "18f980d6d87e8805f2836d64b4138cc95aa7986fa63b1f51f67d5fbff64dd6e5"
 +dependencies = [
 + "proc-macro-hack",
 + "tinystr",
 + "unic-langid-impl",
 + "unic-langid-macros-impl",
 +]
 +
 +[[package]]
 +name = "unic-langid-macros-impl"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "29396ffd97e27574c3e01368b1a64267d3064969e4848e2e130ff668be9daa9f"
 +dependencies = [
 + "proc-macro-hack",
 + "quote",
 + "syn",
 + "unic-langid-impl",
 +]
 +
 +[[package]]
 +name = "unic-ucd-version"
 +version = "0.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
 +dependencies = [
 + "unic-common",
 +]
 +
 +[[package]]
 +name = "unicase"
 +version = "2.6.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
 +dependencies = [
 + "version_check",
 +]
 +
 +[[package]]
 +name = "unicode-bdd"
 +version = "0.1.0"
 +dependencies = [
 + "ucd-parse",
 +]
 +
 +[[package]]
 +name = "unicode-bidi"
 +version = "0.3.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
 +dependencies = [
 + "matches",
 +]
 +
 +[[package]]
 +name = "unicode-normalization"
 +version = "0.1.13"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
 +dependencies = [
 + "tinyvec",
 +]
 +
 +[[package]]
 +name = "unicode-script"
 +version = "0.5.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "098ec66172ce21cd55f8bcc786ee209dd20e04eff70acfca30cb79924d173ae9"
 +
 +[[package]]
 +name = "unicode-security"
 +version = "0.0.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5d87c28edc5b263377e448d6cdcb935c06b95413d8013ba6fae470558ccab18f"
 +dependencies = [
 + "unicode-normalization",
 + "unicode-script",
 +]
 +
 +[[package]]
 +name = "unicode-segmentation"
 +version = "1.9.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
 +
 +[[package]]
 +name = "unicode-width"
 +version = "0.1.8"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-core",
 + "rustc-std-workspace-std",
 +]
 +
 +[[package]]
 +name = "unicode-xid"
 +version = "0.2.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
 +
 +[[package]]
 +name = "unicode_categories"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
 +
 +[[package]]
 +name = "unified-diff"
 +version = "0.2.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "496a3d395ed0c30f411ceace4a91f7d93b148fb5a9b383d5d4cff7850f048d5f"
 +dependencies = [
 + "diff",
 +]
 +
 +[[package]]
 +name = "unstable-book-gen"
 +version = "0.1.0"
 +dependencies = [
 + "num-traits",
 + "tidy",
 +]
 +
 +[[package]]
 +name = "unwind"
 +version = "0.0.0"
 +dependencies = [
 + "cc",
 + "cfg-if 0.1.10",
 + "compiler_builtins",
 + "core",
 + "libc",
 +]
 +
 +[[package]]
 +name = "url"
 +version = "2.2.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
 +dependencies = [
 + "form_urlencoded",
 + "idna",
 + "matches",
 + "percent-encoding",
 + "serde",
 +]
 +
 +[[package]]
 +name = "utf-8"
 +version = "0.7.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "05e42f7c18b8f902290b009cde6d651262f956c98bc51bca4cd1d511c9cd85c7"
 +
 +[[package]]
 +name = "utf8parse"
 +version = "0.1.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
 +
 +[[package]]
 +name = "valuable"
 +version = "0.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
 +
 +[[package]]
 +name = "vcpkg"
 +version = "0.2.10"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
 +
 +[[package]]
 +name = "vergen"
 +version = "5.1.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "dfbc87f9a7a9d61b15d51d1d3547284f67b6b4f1494ce3fc5814c101f35a5183"
 +dependencies = [
 + "anyhow",
 + "chrono",
 + "enum-iterator",
 + "getset",
 + "git2",
 + "rustversion",
 + "thiserror",
 +]
 +
 +[[package]]
 +name = "version_check"
 +version = "0.9.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
 +
 +[[package]]
 +name = "vte"
 +version = "0.3.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4f42f536e22f7fcbb407639765c8fd78707a33109301f834a594758bedd6e8cf"
 +dependencies = [
 + "utf8parse",
 +]
 +
 +[[package]]
 +name = "walkdir"
 +version = "2.3.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
 +dependencies = [
 + "same-file",
 + "winapi",
 + "winapi-util",
 +]
 +
 +[[package]]
 +name = "wasi"
 +version = "0.9.0+wasi-snapshot-preview1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
 +
 +[[package]]
 +name = "wasi"
 +version = "0.11.0+wasi-snapshot-preview1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 +dependencies = [
 + "compiler_builtins",
 + "rustc-std-workspace-alloc",
 + "rustc-std-workspace-core",
 +]
 +
 +[[package]]
 +name = "winapi"
 +version = "0.3.9"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 +dependencies = [
 + "winapi-i686-pc-windows-gnu",
 + "winapi-x86_64-pc-windows-gnu",
 +]
 +
 +[[package]]
 +name = "winapi-i686-pc-windows-gnu"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 +
 +[[package]]
 +name = "winapi-util"
 +version = "0.1.5"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
 +dependencies = [
 + "winapi",
 +]
 +
 +[[package]]
 +name = "winapi-x86_64-pc-windows-gnu"
 +version = "0.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 +
 +[[package]]
 +name = "windows-sys"
 +version = "0.36.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
 +dependencies = [
 + "windows_aarch64_msvc",
 + "windows_i686_gnu",
 + "windows_i686_msvc",
 + "windows_x86_64_gnu",
 + "windows_x86_64_msvc",
 +]
 +
 +[[package]]
 +name = "windows_aarch64_msvc"
 +version = "0.36.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
 +
 +[[package]]
 +name = "windows_i686_gnu"
 +version = "0.36.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
 +
 +[[package]]
 +name = "windows_i686_msvc"
 +version = "0.36.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
 +
 +[[package]]
 +name = "windows_x86_64_gnu"
 +version = "0.36.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
 +
 +[[package]]
 +name = "windows_x86_64_msvc"
 +version = "0.36.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
 +
 +[[package]]
 +name = "xattr"
 +version = "0.2.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c"
 +dependencies = [
 + "libc",
 +]
 +
 +[[package]]
 +name = "xz2"
 +version = "0.1.6"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "c179869f34fc7c01830d3ce7ea2086bc3a07e0d35289b667d0a8bf910258926c"
 +dependencies = [
 + "lzma-sys",
 +]
 +
 +[[package]]
 +name = "yaml-merge-keys"
 +version = "0.4.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fd236a7dc9bb598f349fe4a8754f49181fee50284daa15cd1ba652d722280004"
 +dependencies = [
 + "lazy_static",
 + "thiserror",
 + "yaml-rust",
 +]
 +
 +[[package]]
 +name = "yaml-rust"
 +version = "0.4.4"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
 +dependencies = [
 + "linked-hash-map",
 +]
 +
 +[[package]]
 +name = "yansi"
 +version = "0.5.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
 +
 +[[package]]
 +name = "yansi-term"
 +version = "0.1.2"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "fe5c30ade05e61656247b2e334a031dfd0cc466fadef865bdcdea8d537951bf1"
 +dependencies = [
 + "winapi",
 +]
index 0e27cc927acea4c010f810cd350f047eebece429,0000000000000000000000000000000000000000..fac2c99714d9bf808d6a13f6bae1b6a82af59356
mode 100644,000000..100644
--- /dev/null
@@@ -1,76 -1,0 +1,77 @@@
 +name: Clippy Test
 +
 +on:
 +  push:
 +    # Ignore bors branches, since they are covered by `clippy_bors.yml`
 +    branches-ignore:
 +      - auto
 +      - try
 +    # Don't run Clippy tests, when only text files were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +  pull_request:
 +    # Don't run Clippy tests, when only text files were modified
 +    paths-ignore:
 +    - 'COPYRIGHT'
 +    - 'LICENSE-*'
 +    - '**.md'
 +    - '**.txt'
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
++  CARGO_INCREMENTAL: 0
 +
 +jobs:
 +  base:
 +    # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Set LD_LIBRARY_PATH (Linux)
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 +
 +    - name: Build
 +      run: cargo build --features deny-warnings,internal
 +
 +    - name: Test
 +      run: cargo test --features deny-warnings,internal
 +
 +    - name: Test clippy_lints
 +      run: cargo test --features deny-warnings,internal
 +      working-directory: clippy_lints
 +
 +    - name: Test clippy_utils
 +      run: cargo test --features deny-warnings,internal
 +      working-directory: clippy_utils
 +
 +    - name: Test rustc_tools_util
 +      run: cargo test --features deny-warnings
 +      working-directory: rustc_tools_util
 +
 +    - name: Test clippy_dev
 +      run: cargo test --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test clippy-driver
 +      run: bash .github/driver.sh
 +      env:
 +        OS: ${{ runner.os }}
index 97453303cd6aae58c0959ede46d21704e254464e,0000000000000000000000000000000000000000..30607af490124589f44ce0cf831ca57ab8edad98
mode 100644,000000..100644
--- /dev/null
@@@ -1,281 -1,0 +1,282 @@@
 +name: Clippy Test (bors)
 +
 +on:
 +  push:
 +    branches:
 +      - auto
 +      - try
 +
 +env:
 +  RUST_BACKTRACE: 1
 +  CARGO_TARGET_DIR: '${{ github.workspace }}/target'
 +  NO_FMT_TEST: 1
++  CARGO_INCREMENTAL: 0
 +
 +defaults:
 +  run:
 +    shell: bash
 +
 +jobs:
 +  changelog:
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +      with:
 +        ref: ${{ github.ref }}
 +
 +    # Run
 +    - name: Check Changelog
 +      run: |
 +        MESSAGE=$(git log --format=%B -n 1)
 +        PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//')
 +        body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \
 +          python -c "import sys, json; print(json.load(sys.stdin)['body'])")
 +        output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || {
 +          echo "ERROR: PR body must contain 'changelog: ...'"
 +          exit 1
 +        }
 +        if [[ "$output" = "none" ]]; then
 +          echo "WARNING: changelog is 'none'"
 +        else
 +          echo "changelog: $output"
 +        fi
 +      env:
 +        PYTHONIOENCODING: 'utf-8'
 +  base:
 +    needs: changelog
 +    strategy:
 +      matrix:
 +        os: [ubuntu-latest, windows-latest, macos-latest]
 +        host: [x86_64-unknown-linux-gnu, i686-unknown-linux-gnu, x86_64-apple-darwin, x86_64-pc-windows-msvc]
 +        exclude:
 +        - os: ubuntu-latest
 +          host: x86_64-apple-darwin
 +        - os: ubuntu-latest
 +          host: x86_64-pc-windows-msvc
 +        - os: macos-latest
 +          host: x86_64-unknown-linux-gnu
 +        - os: macos-latest
 +          host: i686-unknown-linux-gnu
 +        - os: macos-latest
 +          host: x86_64-pc-windows-msvc
 +        - os: windows-latest
 +          host: x86_64-unknown-linux-gnu
 +        - os: windows-latest
 +          host: i686-unknown-linux-gnu
 +        - os: windows-latest
 +          host: x86_64-apple-darwin
 +
 +    runs-on: ${{ matrix.os }}
 +
 +    # NOTE: If you modify this job, make sure you copy the changes to clippy.yml
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Install dependencies (Linux-i686)
 +      run: |
 +        sudo dpkg --add-architecture i386
 +        sudo apt-get update
 +        sudo apt-get install gcc-multilib libssl-dev:i386 libgit2-dev:i386
 +      if: matrix.host == 'i686-unknown-linux-gnu'
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Set LD_LIBRARY_PATH (Linux)
 +      if: runner.os == 'Linux'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "LD_LIBRARY_PATH=${SYSROOT}/lib${LD_LIBRARY_PATH+:${LD_LIBRARY_PATH}}" >> $GITHUB_ENV
 +    - name: Link rustc dylib (MacOS)
 +      if: runner.os == 'macOS'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        sudo mkdir -p /usr/local/lib
 +        sudo find "${SYSROOT}/lib" -maxdepth 1 -name '*dylib' -exec ln -s {} /usr/local/lib \;
 +    - name: Set PATH (Windows)
 +      if: runner.os == 'Windows'
 +      run: |
 +        SYSROOT=$(rustc --print sysroot)
 +        echo "$SYSROOT/bin" >> $GITHUB_PATH
 +
 +    - name: Build
 +      run: cargo build --features deny-warnings,internal
 +
 +    - name: Test
 +      if: runner.os == 'Linux'
 +      run: cargo test --features deny-warnings,internal
 +
 +    - name: Test
 +      if: runner.os != 'Linux'
 +      run: cargo test --features deny-warnings,internal -- --skip dogfood
 +
 +    - name: Test clippy_lints
 +      run: cargo test --features deny-warnings,internal
 +      working-directory: clippy_lints
 +
 +    - name: Test clippy_utils
 +      run: cargo test --features deny-warnings,internal
 +      working-directory: clippy_utils
 +
 +    - name: Test rustc_tools_util
 +      run: cargo test --features deny-warnings
 +      working-directory: rustc_tools_util
 +
 +    - name: Test clippy_dev
 +      run: cargo test --features deny-warnings
 +      working-directory: clippy_dev
 +
 +    - name: Test clippy-driver
 +      run: bash .github/driver.sh
 +      env:
 +        OS: ${{ runner.os }}
 +
 +  metadata_collection:
 +    needs: changelog
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +     # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    - name: Test metadata collection
 +      run: cargo collect-metadata
 +
 +  integration_build:
 +    needs: changelog
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Run
 +    - name: Build Integration Test
 +      run: cargo test --test integration --features integration --no-run
 +
 +    # Upload
 +    - name: Extract Binaries
 +      run: |
 +        DIR=$CARGO_TARGET_DIR/debug
 +        rm $DIR/deps/integration-*.d
 +        mv $DIR/deps/integration-* $DIR/integration
 +        find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf
 +        rm -rf $CARGO_TARGET_DIR/release
 +
 +    - name: Upload Binaries
 +      uses: actions/upload-artifact@v1
 +      with:
 +        name: target
 +        path: target
 +
 +  integration:
 +    needs: integration_build
 +    strategy:
 +      fail-fast: false
 +      max-parallel: 6
 +      matrix:
 +        integration:
 +        - 'rust-lang/cargo'
 +        # FIXME: re-enable once fmt_macros is renamed in RLS
 +        # - 'rust-lang/rls'
 +        - 'rust-lang/chalk'
 +        - 'rust-lang/rustfmt'
 +        - 'Marwes/combine'
 +        - 'Geal/nom'
 +        - 'rust-lang/stdarch'
 +        - 'serde-rs/serde'
 +        # FIXME: chrono currently cannot be compiled with `--all-targets`
 +        # - 'chronotope/chrono'
 +        - 'hyperium/hyper'
 +        - 'rust-random/rand'
 +        - 'rust-lang/futures-rs'
 +        - 'rust-itertools/itertools'
 +        - 'rust-lang-nursery/failure'
 +        - 'rust-lang/log'
 +
 +    runs-on: ubuntu-latest
 +
 +    steps:
 +    # Setup
 +    - uses: rust-lang/simpleinfra/github-actions/cancel-outdated-builds@master
 +      with:
 +        github_token: "${{ secrets.github_token }}"
 +
 +    - name: Checkout
 +      uses: actions/checkout@v3.0.2
 +
 +    - name: Install toolchain
 +      run: rustup show active-toolchain
 +
 +    # Download
 +    - name: Download target dir
 +      uses: actions/download-artifact@v1
 +      with:
 +        name: target
 +        path: target
 +
 +    - name: Make Binaries Executable
 +      run: chmod +x $CARGO_TARGET_DIR/debug/*
 +
 +    # Run
 +    - name: Test ${{ matrix.integration }}
 +      run: |
 +        RUSTUP_TOOLCHAIN="$(rustup show active-toolchain | grep -o -E "nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}")" \
 +          $CARGO_TARGET_DIR/debug/integration
 +      env:
 +        INTEGRATION: ${{ matrix.integration }}
 +
 +  # These jobs doesn't actually test anything, but they're only used to tell
 +  # bors the build completed, as there is no practical way to detect when a
 +  # workflow is successful listening to webhooks only.
 +  #
 +  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
 +
 +  end-success:
 +    name: bors test finished
 +    if: github.event.pusher.name == 'bors' && success()
 +    runs-on: ubuntu-latest
 +    needs: [changelog, base, metadata_collection, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as successful
 +        run: exit 0
 +
 +  end-failure:
 +    name: bors test finished
 +    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
 +    runs-on: ubuntu-latest
 +    needs: [changelog, base, metadata_collection, integration_build, integration]
 +
 +    steps:
 +      - name: Mark the job as a failure
 +        run: exit 1
index 380cd451987bfc982d00448668e1699fb999ffc9,0000000000000000000000000000000000000000..c488c142e46fc2847fe41d7c3c3582508a705eac
mode 100644,000000..100644
--- /dev/null
@@@ -1,4048 -1,0 +1,4204 @@@
- [7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
 +# Changelog
 +
 +All notable changes to this project will be documented in this file.
 +See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
 +document.
 +
 +## Unreleased / In Rust Nightly
 +
- Current stable, released 2022-06-30
++[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
++
++## Rust 1.63
++
++Current stable, released 2022-08-11
++
++[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
++
++### New Lints
++
++* [`borrow_deref_ref`]
++  [#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
++* [`doc_link_with_quotes`]
++  [#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
++* [`no_effect_replace`]
++  [#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
++* [`rc_clone_in_vec_init`]
++  [#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
++* [`derive_partial_eq_without_eq`]
++  [#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
++* [`mismatching_type_param_order`]
++  [#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
++* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
++* [`unused_rounding`]
++  [#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
++* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
++* [`swap_ptr_to_ref`]
++  [#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
++* [`almost_complete_letter_range`]
++  [#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
++* [`needless_parens_on_range_literals`]
++  [#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
++* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
++
++### Moves and Deprecations
++
++* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
++  `nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
++
++### Enhancements
++
++* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
++  [#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
++* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
++  [#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
++* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
++  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
++* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
++  option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
++* [`disallowed_methods`]: Now also lints indirect usages
++  [#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
++* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
++  [#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
++* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
++  [#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
++* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
++  [#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
++* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
++  [#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
++* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
++  [#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
++* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
++  method chains inside `map`
++  [#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
++* [`needless_return`]: Now also lints on macro expressions in return statements
++  [#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
++* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
++  should extend the default and not replace it
++  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
++* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
++  config should extend the default and not replace it
++  [#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
++* [`never_loop`]: Now checks for `continue` in struct expression
++  [#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
++
++### False Positive Fixes
++
++* [`useless_transmute`]: No longer lints on types with erased regions
++  [#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
++* [`vec_init_then_push`]: No longer lints when further extended
++  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
++* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
++  [#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
++* [`redundant_allocation`]: No longer lints on fat pointers that would become
++  thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
++* [`derive_partial_eq_without_eq`]:
++    * Handle differing predicates applied by `#[derive(PartialEq)]` and
++      `#[derive(Eq)]`
++      [#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
++    * No longer lints on non-public types and better handles generics
++      [#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
++* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
++  string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
++* [`branches_sharing_code`]: No longer lints when using different binding names
++  [#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
++* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
++  desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
++* [`checked_conversions`]: No longer lints in `const` contexts
++  [#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
++* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
++  `T::Item` doesn't implement `IntoIterator`
++  [#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
++
++### Suggestion Fixes/Improvements
++
++* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
++  [#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
++* [`manual_range_contains`]: Fix suggestion for integers with different signs
++  [#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
++* [`identity_op`]: Add parenthesis to suggestions where required
++  [#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
++* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
++  [#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
++* [`rc_clone_in_vec_init`]: Add suggestion
++  [#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
++* The "unknown field" error messages for config files now wraps the field names
++  [#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
++* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
++  [#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
++* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
++  references and not trivially clone-able
++  [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
++* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
++  `iter_mut()` or `into_iter()`
++  [#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
++
++### ICE Fixes
++
++* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
++  [#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
++* Fix ICEs on callable `static`/`const`s
++  [#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
++* [`needless_late_init`]
++  [#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
++* Fix ICE in shadow lints
++  [#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
++
++### Documentation Improvements
++
++* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
++  [#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
++* Add a *copy lint name*-button to Clippy's lint list
++  [#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
++* Display past names of renamed lints on Clippy's lint list
++  [#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
++* Add the ability to show the lint output in the lint list
++  [#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
 +
 +## Rust 1.62
 +
++Released 2022-06-30
 +
 +[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
 +
 +### New Lints
 +
 +* [`large_include_file`]
 +  [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
 +* [`cast_abs_to_unsigned`]
 +  [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
 +* [`err_expect`]
 +  [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
 +* [`unnecessary_owned_empty_strings`]
 +  [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
 +* [`empty_structs_with_brackets`]
 +  [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
 +* [`crate_in_macro_def`]
 +  [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
 +* [`needless_option_take`]
 +  [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
 +* [`bytes_count_to_len`]
 +  [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
 +* [`is_digit_ascii_radix`]
 +  [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
 +* [`await_holding_invalid_type`]
 +  [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
 +* [`trim_split_whitespace`]
 +  [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
 +* [`pub_use`]
 +  [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
 +* [`format_push_string`]
 +  [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
 +* [`empty_drop`]
 +  [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
 +* [`drop_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +* [`forget_non_drop`]
 +  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
 +
 +### Moves and Deprecations
 +
 +* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
 +  [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
 +* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
 +  [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
 +
 +### Enhancements
 +
 +* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`map_identity`]: Now checks for needless `map_err`
 +  [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
 +* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
 +  [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
 +* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
 +  [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
 +* [`identity_op`]: Now checks for modulo expressions
 +  [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
 +* [`panic`]: No longer lint in constant context
 +  [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
 +* [`manual_split_once`]: Now lints manual iteration of `splitn`
 +  [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
 +* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
 +  [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
 +* [`unsound_collection_transmute`]: Now has better size and alignment checks
 +  [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
 +* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
 +  [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
 +
 +### False Positive Fixes
 +
 +* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
 +  [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
 +* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
 +  changes the drop order significantly
 +  [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
 +* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
 +  [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
 +* [`init_numbered_fields`]: No longer lints type aliases
 +  [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
 +* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
 +  [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
 +* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
 +  [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
 +* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
 +  [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
 +* [`needless_match`]: Now checks for more complex types and ignores type coercion
 +  [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
 +* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
 +  [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
 +* [`indexing_slicing`]: Fix false positives with constant indices in
 +  [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
 +* [`iter_with_drain`]: Now ignores iterator references
 +  [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
 +* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
 +  [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
 +* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
 +  [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
 +* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
 +  [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
 +* [`mut_from_ref`]: Only lint in unsafe code
 +  [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
 +* [`redundant_pub_crate`]: Now allows macro exports
 +  [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
 +* [`needless_match`]: Ignores cases where the else block expression is different
 +  [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
 +* [`transmute_int_to_char`]: Now allows transmutations in `const` code
 +  [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
 +* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
 +  [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
 +* [`redundant_closure`]: Now ignores coerced closure
 +  [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
 +* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
 +  [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
 +* [`let_unit_value`]: Now ignores cases which are used for type inference
 +  [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
 +  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
 +* [`bytes_nth`]: Fix typos in the diagnostic message
 +  [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
 +* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
 +  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
 +* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
 +  [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
 +* [`single_element_loop`]: Improve lint for Edition 2021 arrays
 +  [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
 +* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
 +  [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
 +* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
 +  [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
 +* [`collapsible_else_if`]: Add whitespace in suggestion
 +  [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
 +* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
 +  [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
 +* [`map_clone`]: Improve message and suggestion based on the msrv
 +  [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
 +* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
 +  [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
 +
 +### ICE Fixes
 +
 +* [`only_used_in_recursion`]
 +  [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
 +* [`cast_slice_different_sizes`]
 +  [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
 +* [`iter_overeager_cloned`]
 +  [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
 +* [`undocumented_unsafe_blocks`]
 +  [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
 +
 +## Rust 1.61
 +
 +Released 2022-05-19
 +
 +[57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
 +
 +### New Lints
 +
 +* [`only_used_in_recursion`]
 +  [#8422](https://github.com/rust-lang/rust-clippy/pull/8422)
 +* [`cast_enum_truncation`]
 +  [#8381](https://github.com/rust-lang/rust-clippy/pull/8381)
 +* [`missing_spin_loop`]
 +  [#8174](https://github.com/rust-lang/rust-clippy/pull/8174)
 +* [`deref_by_slicing`]
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`needless_match`]
 +  [#8471](https://github.com/rust-lang/rust-clippy/pull/8471)
 +* [`allow_attributes_without_reason`] (Requires `#![feature(lint_reasons)]`)
 +  [#8504](https://github.com/rust-lang/rust-clippy/pull/8504)
 +* [`print_in_format_impl`]
 +  [#8253](https://github.com/rust-lang/rust-clippy/pull/8253)
 +* [`unnecessary_find_map`]
 +  [#8489](https://github.com/rust-lang/rust-clippy/pull/8489)
 +* [`or_then_unwrap`]
 +  [#8561](https://github.com/rust-lang/rust-clippy/pull/8561)
 +* [`unnecessary_join`]
 +  [#8579](https://github.com/rust-lang/rust-clippy/pull/8579)
 +* [`iter_with_drain`]
 +  [#8483](https://github.com/rust-lang/rust-clippy/pull/8483)
 +* [`cast_enum_constructor`]
 +  [#8562](https://github.com/rust-lang/rust-clippy/pull/8562)
 +* [`cast_slice_different_sizes`]
 +  [#8445](https://github.com/rust-lang/rust-clippy/pull/8445)
 +
 +### Moves and Deprecations
 +
 +* Moved [`transmute_undefined_repr`] to `nursery` (now allow-by-default)
 +  [#8432](https://github.com/rust-lang/rust-clippy/pull/8432)
 +* Moved [`try_err`] to `restriction`
 +  [#8544](https://github.com/rust-lang/rust-clippy/pull/8544)
 +* Move [`iter_with_drain`] to `nursery`
 +  [#8541](https://github.com/rust-lang/rust-clippy/pull/8541)
 +* Renamed `to_string_in_display` to [`recursive_format_impl`]
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### Enhancements
 +
 +* [`dbg_macro`]: The lint level can now be set with crate attributes and works inside macros
 +  [#8411](https://github.com/rust-lang/rust-clippy/pull/8411)
 +* [`ptr_as_ptr`]: Now works inside macros
 +  [#8442](https://github.com/rust-lang/rust-clippy/pull/8442)
 +* [`use_self`]: Now works for variants in match expressions
 +  [#8456](https://github.com/rust-lang/rust-clippy/pull/8456)
 +* [`await_holding_lock`]: Now lints for `parking_lot::{Mutex, RwLock}`
 +  [#8419](https://github.com/rust-lang/rust-clippy/pull/8419)
 +* [`recursive_format_impl`]: Now checks for format calls on `self`
 +  [#8188](https://github.com/rust-lang/rust-clippy/pull/8188)
 +
 +### False Positive Fixes
 +
 +* [`new_without_default`]: No longer lints for `new()` methods with `#[doc(hidden)]`
 +  [#8472](https://github.com/rust-lang/rust-clippy/pull/8472)
 +* [`transmute_undefined_repr`]: No longer lints for single field structs with `#[repr(C)]`,
 +  generic parameters, wide pointers, unions, tuples and allow several forms of type erasure
 +  [#8425](https://github.com/rust-lang/rust-clippy/pull/8425)
 +  [#8553](https://github.com/rust-lang/rust-clippy/pull/8553)
 +  [#8440](https://github.com/rust-lang/rust-clippy/pull/8440)
 +  [#8547](https://github.com/rust-lang/rust-clippy/pull/8547)
 +* [`match_single_binding`], [`match_same_arms`], [`match_as_ref`], [`match_bool`]: No longer
 +  lint `match` expressions with `cfg`ed arms
 +  [#8443](https://github.com/rust-lang/rust-clippy/pull/8443)
 +* [`single_component_path_imports`]: No longer lint on macros
 +  [#8537](https://github.com/rust-lang/rust-clippy/pull/8537)
 +* [`ptr_arg`]: Allow `&mut` arguments for `Cow<_>`
 +  [#8552](https://github.com/rust-lang/rust-clippy/pull/8552)
 +* [`needless_borrow`]: No longer lints for method calls
 +  [#8441](https://github.com/rust-lang/rust-clippy/pull/8441)
 +* [`match_same_arms`]: Now ensures that interposing arm patterns don't overlap
 +  [#8232](https://github.com/rust-lang/rust-clippy/pull/8232)
 +* [`default_trait_access`]: Now allows `Default::default` in update expressions
 +  [#8433](https://github.com/rust-lang/rust-clippy/pull/8433)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_slicing`]: Fixed suggestion for a method calls
 +  [#8218](https://github.com/rust-lang/rust-clippy/pull/8218)
 +* [`map_flatten`]: Long suggestions will now be split up into two help messages
 +  [#8520](https://github.com/rust-lang/rust-clippy/pull/8520)
 +* [`unnecessary_lazy_evaluations`]: Now shows suggestions for longer code snippets
 +  [#8543](https://github.com/rust-lang/rust-clippy/pull/8543)
 +* [`unnecessary_sort_by`]: Now suggests `Reverse` including the path
 +  [#8462](https://github.com/rust-lang/rust-clippy/pull/8462)
 +* [`search_is_some`]: More suggestions are now `MachineApplicable`
 +  [#8536](https://github.com/rust-lang/rust-clippy/pull/8536)
 +
 +### Documentation Improvements
 +
 +* [`new_without_default`]: Document `pub` requirement for the struct and fields
 +  [#8429](https://github.com/rust-lang/rust-clippy/pull/8429)
 +
 +## Rust 1.60
 +
 +Released 2022-04-07
 +
 +[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
 +
 +### New Lints
 +
 +* [`single_char_lifetime_names`]
 +  [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
 +* [`iter_overeager_cloned`]
 +  [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
 +* [`transmute_undefined_repr`]
 +  [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
 +* [`default_union_representation`]
 +  [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
 +* [`manual_bits`]
 +  [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
 +* [`borrow_as_ptr`]
 +  [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
 +
 +### Moves and Deprecations
 +
 +* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
 +  [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
 +* Rename `ref_in_deref` to [`needless_borrow`]
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
 +  [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
 +
 +### Enhancements
 +
 +* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
 +  [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
 +* [`unused_io_amount`]: Now supports async read and write traits
 +  [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
 +* [`while_let_on_iterator`]: Improved detection to catch more cases
 +  [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
 +* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
 +  [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
 +* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
 +  [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
 +* [`map_clone`]: The suggestion takes `msrv` into account
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
 +  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
 +* [`disallowed_methods`]: Now works for methods on primitive types
 +  [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
 +* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
 +  [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
 +* [`needless_question_mark`]: Now works for async functions
 +  [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
 +* [`iter_not_returning_iterator`]: Now handles type projections
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
 +  [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
 +* [`single_match`]: Now works for `match` statements with tuples
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +
 +### False Positive Fixes
 +
 +* [`erasing_op`]: No longer triggers if the output type changes
 +  [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
 +* [`if_same_then_else`]: No longer triggers for `if let` statements
 +  [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
 +* [`manual_memcpy`]: No longer lints on `VecDeque`
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`trait_duplication_in_bounds`]: Now takes path segments into account
 +  [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
 +* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
 +  [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
 +* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
 +  [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
 +* [`ptr_arg`]: No longer lint for mutable references in traits
 +  [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
 +* [`implicit_clone`]: No longer lints for double references
 +  [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
 +* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
 +  [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
 +* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
 +  [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
 +* [`enum_variant_names`]: No longer triggers for empty variant names
 +  [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
 +* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
 +  [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
 +* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
 +  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
 +* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
 +  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
 +* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
 +  [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
 +* [`useless_format`]: Now works for implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
 +  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
 +* [`chars_next_cmp`]: Correctly escapes the suggestion
 +  [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
 +* [`explicit_write`]: Add suggestions for `write!`s with format arguments
 +  [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
 +* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
 +  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
 +* [`or_fun_call`]: Improved suggestion display for long arguments
 +  [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
 +* [`unnecessary_cast`]: Now correctly includes the sign
 +  [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
 +* [`cmp_owned`]: No longer flips the comparison order
 +  [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
 +* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
 +  [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
 +
 +### ICE Fixes
 +
 +* [`manual_split_once`]
 +  [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
 +
 +### Documentation Improvements
 +
 +* [`map_flatten`]: Add documentation for the `Option` type
 +  [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
 +* Document that Clippy's driver might use a different code generation than rustc
 +  [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
 +* Clippy's lint list will now automatically focus the search box
 +  [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
 +
 +### Others
 +
 +* Clippy now warns if we find multiple Clippy config files exist
 +  [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
 +
 +## Rust 1.59
 +
 +Released 2022-02-24
 +
 +[e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
 +
 +### New Lints
 +
 +* [`index_refutable_slice`]
 +  [#7643](https://github.com/rust-lang/rust-clippy/pull/7643)
 +* [`needless_splitn`]
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* [`unnecessary_to_owned`]
 +  [#7978](https://github.com/rust-lang/rust-clippy/pull/7978)
 +* [`needless_late_init`]
 +  [#7995](https://github.com/rust-lang/rust-clippy/pull/7995)
 +* [`octal_escapes`] [#8007](https://github.com/rust-lang/rust-clippy/pull/8007)
 +* [`return_self_not_must_use`]
 +  [#8071](https://github.com/rust-lang/rust-clippy/pull/8071)
 +* [`init_numbered_fields`]
 +  [#8170](https://github.com/rust-lang/rust-clippy/pull/8170)
 +
 +### Moves and Deprecations
 +
 +* Move `if_then_panic` to `pedantic` and rename to [`manual_assert`] (now
 +  allow-by-default) [#7810](https://github.com/rust-lang/rust-clippy/pull/7810)
 +* Rename `disallow_type` to [`disallowed_types`] and `disallowed_method` to
 +  [`disallowed_methods`]
 +  [#7984](https://github.com/rust-lang/rust-clippy/pull/7984)
 +* Move [`map_flatten`] to `complexity` (now warn-by-default)
 +  [#8054](https://github.com/rust-lang/rust-clippy/pull/8054)
 +
 +### Enhancements
 +
 +* [`match_overlapping_arm`]: Fix false negative where after included ranges,
 +  overlapping ranges weren't linted anymore
 +  [#7909](https://github.com/rust-lang/rust-clippy/pull/7909)
 +* [`deprecated_cfg_attr`]: Now takes the specified MSRV into account
 +  [#7944](https://github.com/rust-lang/rust-clippy/pull/7944)
 +* [`cast_lossless`]: Now also lints for `bool` to integer casts
 +  [#7948](https://github.com/rust-lang/rust-clippy/pull/7948)
 +* [`let_underscore_lock`]: Also emit lints for the `parking_lot` crate
 +  [#7957](https://github.com/rust-lang/rust-clippy/pull/7957)
 +* [`needless_borrow`]
 +  [#7977](https://github.com/rust-lang/rust-clippy/pull/7977)
 +    * Lint when a borrow is auto-dereffed more than once
 +    * Lint in the trailing expression of a block for a match arm
 +* [`strlen_on_c_strings`]
 +  [8001](https://github.com/rust-lang/rust-clippy/pull/8001)
 +    * Lint when used without a fully-qualified path
 +    * Suggest removing the surrounding unsafe block when possible
 +* [`non_ascii_literal`]: Now also lints on `char`s, not just `string`s
 +  [#8034](https://github.com/rust-lang/rust-clippy/pull/8034)
 +* [`single_char_pattern`]: Now also lints on `split_inclusive`, `split_once`,
 +  `rsplit_once`, `replace`, and `replacen`
 +  [#8077](https://github.com/rust-lang/rust-clippy/pull/8077)
 +* [`unwrap_or_else_default`]: Now also lints on `std` constructors like
 +  `Vec::new`, `HashSet::new`, and `HashMap::new`
 +  [#8163](https://github.com/rust-lang/rust-clippy/pull/8163)
 +* [`shadow_reuse`]: Now also lints on shadowed `if let` bindings, instead of
 +  [`shadow_unrelated`]
 +  [#8165](https://github.com/rust-lang/rust-clippy/pull/8165)
 +
 +### False Positive Fixes
 +
 +* [`or_fun_call`], [`unnecessary_lazy_evaluations`]: Improve heuristics, so that
 +  cheap functions (e.g. calling `.len()` on a `Vec`) won't get linted anymore
 +  [#7639](https://github.com/rust-lang/rust-clippy/pull/7639)
 +* [`manual_split_once`]: No longer suggests code changing the original behavior
 +  [#7896](https://github.com/rust-lang/rust-clippy/pull/7896)
 +* Don't show [`no_effect`] or [`unnecessary_operation`] warning for unit struct
 +  implementing `FnOnce`
 +  [#7898](https://github.com/rust-lang/rust-clippy/pull/7898)
 +* [`semicolon_if_nothing_returned`]: Fixed a bug, where the lint wrongly
 +  triggered on `let-else` statements
 +  [#7955](https://github.com/rust-lang/rust-clippy/pull/7955)
 +* [`if_then_some_else_none`]: No longer lints if there is an early return
 +  [#7980](https://github.com/rust-lang/rust-clippy/pull/7980)
 +* [`needless_collect`]: No longer suggests removal of `collect` when removal
 +  would create code requiring mutably borrowing a value multiple times
 +  [#7982](https://github.com/rust-lang/rust-clippy/pull/7982)
 +* [`shadow_same`]: Fix false positive for `async` function's params
 +  [#7997](https://github.com/rust-lang/rust-clippy/pull/7997)
 +* [`suboptimal_flops`]: No longer triggers in constant functions
 +  [#8009](https://github.com/rust-lang/rust-clippy/pull/8009)
 +* [`type_complexity`]: No longer lints on associated types in traits
 +  [#8030](https://github.com/rust-lang/rust-clippy/pull/8030)
 +* [`question_mark`]: No longer lints if returned object is not local
 +  [#8080](https://github.com/rust-lang/rust-clippy/pull/8080)
 +* [`option_if_let_else`]: No longer lint on complex sub-patterns
 +  [#8086](https://github.com/rust-lang/rust-clippy/pull/8086)
 +* [`blocks_in_if_conditions`]: No longer lints on empty closures
 +  [#8100](https://github.com/rust-lang/rust-clippy/pull/8100)
 +* [`enum_variant_names`]: No longer lint when first prefix is only a substring
 +  of a camel-case word
 +  [#8127](https://github.com/rust-lang/rust-clippy/pull/8127)
 +* [`identity_op`]: Only lint on integral operands
 +  [#8183](https://github.com/rust-lang/rust-clippy/pull/8183)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`search_is_some`]: Fix suggestion for `any()` not taking item by reference
 +  [#7463](https://github.com/rust-lang/rust-clippy/pull/7463)
 +* [`almost_swapped`]: Now detects if there is a `no_std` or `no_core` attribute
 +  and adapts the suggestion accordingly
 +  [#7877](https://github.com/rust-lang/rust-clippy/pull/7877)
 +* [`redundant_pattern_matching`]: Fix suggestion for deref expressions
 +  [#7949](https://github.com/rust-lang/rust-clippy/pull/7949)
 +* [`explicit_counter_loop`]: Now also produces a suggestion for non-`usize`
 +  types [#7950](https://github.com/rust-lang/rust-clippy/pull/7950)
 +* [`manual_map`]: Fix suggestion when used with unsafe functions and blocks
 +  [#7968](https://github.com/rust-lang/rust-clippy/pull/7968)
 +* [`option_map_or_none`]: Suggest `map` over `and_then` when possible
 +  [#7971](https://github.com/rust-lang/rust-clippy/pull/7971)
 +* [`option_if_let_else`]: No longer expands macros in the suggestion
 +  [#7974](https://github.com/rust-lang/rust-clippy/pull/7974)
 +* [`iter_cloned_collect`]: Suggest `copied` over `cloned` when possible
 +  [#8006](https://github.com/rust-lang/rust-clippy/pull/8006)
 +* [`doc_markdown`]: No longer uses inline hints to improve readability of
 +  suggestion [#8011](https://github.com/rust-lang/rust-clippy/pull/8011)
 +* [`needless_question_mark`]: Now better explains the suggestion
 +  [#8028](https://github.com/rust-lang/rust-clippy/pull/8028)
 +* [`single_char_pattern`]: Escape backslash `\` in suggestion
 +  [#8067](https://github.com/rust-lang/rust-clippy/pull/8067)
 +* [`needless_bool`]: Suggest `a != b` over `!(a == b)`
 +  [#8117](https://github.com/rust-lang/rust-clippy/pull/8117)
 +* [`iter_skip_next`]: Suggest to add a `mut` if it is necessary in order to
 +  apply this lints suggestion
 +  [#8133](https://github.com/rust-lang/rust-clippy/pull/8133)
 +* [`neg_multiply`]: Now produces a suggestion
 +  [#8144](https://github.com/rust-lang/rust-clippy/pull/8144)
 +* [`needless_return`]: Now suggests the unit type `()` over an empty block `{}`
 +  in match arms [#8185](https://github.com/rust-lang/rust-clippy/pull/8185)
 +* [`suboptimal_flops`]: Now gives a syntactically correct suggestion for
 +  `to_radians` and `to_degrees`
 +  [#8187](https://github.com/rust-lang/rust-clippy/pull/8187)
 +
 +### ICE Fixes
 +
 +* [`undocumented_unsafe_blocks`]
 +  [#7945](https://github.com/rust-lang/rust-clippy/pull/7945)
 +  [#7988](https://github.com/rust-lang/rust-clippy/pull/7988)
 +* [`unnecessary_cast`]
 +  [#8167](https://github.com/rust-lang/rust-clippy/pull/8167)
 +
 +### Documentation Improvements
 +
 +* [`print_stdout`], [`print_stderr`], [`dbg_macro`]: Document how the lint level
 +  can be changed crate-wide
 +  [#8040](https://github.com/rust-lang/rust-clippy/pull/8040)
 +* Added a note to the `README` that config changes don't apply to already
 +  compiled code [#8175](https://github.com/rust-lang/rust-clippy/pull/8175)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now displays
 +  the version a lint was added. :tada:
 +  [#7813](https://github.com/rust-lang/rust-clippy/pull/7813)
 +* New and improved issue templates
 +  [#8032](https://github.com/rust-lang/rust-clippy/pull/8032)
 +* _Dev:_ Add `cargo dev lint` command, to run your modified Clippy version on a
 +  file [#7917](https://github.com/rust-lang/rust-clippy/pull/7917)
 +
 +## Rust 1.58
 +
 +Released 2022-01-13
 +
 +[00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
 +
 +### Rust 1.58.1
 +
 +* Move [`non_send_fields_in_send_ty`] to `nursery` (now allow-by-default)
 +  [#8075](https://github.com/rust-lang/rust-clippy/pull/8075)
 +* [`useless_format`]: Handle implicit named arguments
 +  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
 +
 +### New lints
 +
 +* [`transmute_num_to_bytes`]
 +  [#7805](https://github.com/rust-lang/rust-clippy/pull/7805)
 +* [`match_str_case_mismatch`]
 +  [#7806](https://github.com/rust-lang/rust-clippy/pull/7806)
 +* [`format_in_format_args`], [`to_string_in_format_args`]
 +  [#7743](https://github.com/rust-lang/rust-clippy/pull/7743)
 +* [`uninit_vec`]
 +  [#7682](https://github.com/rust-lang/rust-clippy/pull/7682)
 +* [`fn_to_numeric_cast_any`]
 +  [#7705](https://github.com/rust-lang/rust-clippy/pull/7705)
 +* [`undocumented_unsafe_blocks`]
 +  [#7748](https://github.com/rust-lang/rust-clippy/pull/7748)
 +* [`trailing_empty_array`]
 +  [#7838](https://github.com/rust-lang/rust-clippy/pull/7838)
 +* [`string_slice`]
 +  [#7878](https://github.com/rust-lang/rust-clippy/pull/7878)
 +
 +### Moves or deprecations of lints
 +
 +* Move [`non_send_fields_in_send_ty`] to `suspicious`
 +  [#7874](https://github.com/rust-lang/rust-clippy/pull/7874)
 +* Move [`non_ascii_literal`] to `restriction`
 +  [#7907](https://github.com/rust-lang/rust-clippy/pull/7907)
 +
 +### Changes that expand what code existing lints cover
 +
 +* [`question_mark`] now covers `Result`
 +  [#7840](https://github.com/rust-lang/rust-clippy/pull/7840)
 +* Make [`useless_format`] recognize bare `format!("")`
 +  [#7801](https://github.com/rust-lang/rust-clippy/pull/7801)
 +* Lint on underscored variables with no side effects in [`no_effect`]
 +  [#7775](https://github.com/rust-lang/rust-clippy/pull/7775)
 +* Expand [`match_ref_pats`] to check for multiple reference patterns
 +  [#7800](https://github.com/rust-lang/rust-clippy/pull/7800)
 +
 +### False positive fixes
 +
 +* Fix false positive of [`implicit_saturating_sub`] with `else` clause
 +  [#7832](https://github.com/rust-lang/rust-clippy/pull/7832)
 +* Fix [`question_mark`] when there is call in conditional predicate
 +  [#7860](https://github.com/rust-lang/rust-clippy/pull/7860)
 +* [`mut_mut`] no longer lints when type is defined in external macros
 +  [#7795](https://github.com/rust-lang/rust-clippy/pull/7795)
 +* Avoid [`eq_op`] in test functions
 +  [#7811](https://github.com/rust-lang/rust-clippy/pull/7811)
 +* [`cast_possible_truncation`] no longer lints when cast is coming from `signum`
 +  method call [#7850](https://github.com/rust-lang/rust-clippy/pull/7850)
 +* [`match_str_case_mismatch`] no longer lints on uncased characters
 +  [#7865](https://github.com/rust-lang/rust-clippy/pull/7865)
 +* [`ptr_arg`] no longer lints references to type aliases
 +  [#7890](https://github.com/rust-lang/rust-clippy/pull/7890)
 +* [`missing_safety_doc`] now also accepts "implementation safety" headers
 +  [#7856](https://github.com/rust-lang/rust-clippy/pull/7856)
 +* [`missing_safety_doc`] no longer lints if any parent has `#[doc(hidden)]`
 +  attribute [#7849](https://github.com/rust-lang/rust-clippy/pull/7849)
 +* [`if_not_else`] now ignores else-if statements
 +  [#7895](https://github.com/rust-lang/rust-clippy/pull/7895)
 +* Avoid linting [`cast_possible_truncation`] on bit-reducing operations
 +  [#7819](https://github.com/rust-lang/rust-clippy/pull/7819)
 +* Avoid linting [`field_reassign_with_default`] when `Drop` and `Copy` are
 +  involved [#7794](https://github.com/rust-lang/rust-clippy/pull/7794)
 +* [`unnecessary_sort_by`] now checks if argument implements `Ord` trait
 +  [#7824](https://github.com/rust-lang/rust-clippy/pull/7824)
 +* Fix false positive in [`match_overlapping_arm`]
 +  [#7847](https://github.com/rust-lang/rust-clippy/pull/7847)
 +* Prevent [`needless_lifetimes`] false positive in `async` function definition
 +  [#7901](https://github.com/rust-lang/rust-clippy/pull/7901)
 +
 +### Suggestion fixes/improvements
 +
 +* Keep an initial `::` when [`doc_markdown`] suggests to use ticks
 +  [#7916](https://github.com/rust-lang/rust-clippy/pull/7916)
 +* Add a machine applicable suggestion for the [`doc_markdown`] missing backticks
 +  lint [#7904](https://github.com/rust-lang/rust-clippy/pull/7904)
 +* [`equatable_if_let`] no longer expands macros in the suggestion
 +  [#7788](https://github.com/rust-lang/rust-clippy/pull/7788)
 +* Make [`shadow_reuse`] suggestion less verbose
 +  [#7782](https://github.com/rust-lang/rust-clippy/pull/7782)
 +
 +### ICE fixes
 +
 +* Fix ICE in [`enum_variant_names`]
 +  [#7873](https://github.com/rust-lang/rust-clippy/pull/7873)
 +* Fix ICE in [`undocumented_unsafe_blocks`]
 +  [#7891](https://github.com/rust-lang/rust-clippy/pull/7891)
 +
 +### Documentation improvements
 +
 +* Fixed naive doc formatting for `#[must_use]` lints ([`must_use_unit`],
 +  [`double_must_use`], [`must_use_candidate`], [`let_underscore_must_use`])
 +  [#7827](https://github.com/rust-lang/rust-clippy/pull/7827)
 +* Fix typo in example for [`match_result_ok`]
 +  [#7815](https://github.com/rust-lang/rust-clippy/pull/7815)
 +
 +### Others
 +
 +* Allow giving reasons for [`disallowed_types`]
 +  [#7791](https://github.com/rust-lang/rust-clippy/pull/7791)
 +* Fix [`manual_assert`] and [`match_wild_err_arm`] for `#![no_std]` and Rust
 +  2021. [#7851](https://github.com/rust-lang/rust-clippy/pull/7851)
 +* Fix regression in [`semicolon_if_nothing_returned`] on macros containing while
 +  loops [#7789](https://github.com/rust-lang/rust-clippy/pull/7789)
 +* Added a new configuration `literal-suffix-style` to enforce a certain style
 +  writing [`unseparated_literal_suffix`]
 +  [#7726](https://github.com/rust-lang/rust-clippy/pull/7726)
 +
 +## Rust 1.57
 +
 +Released 2021-12-02
 +
 +[7bfc26e...00e31fa](https://github.com/rust-lang/rust-clippy/compare/7bfc26e...00e31fa)
 +
 +### New Lints
 +
 +* [`negative_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`redundant_feature_names`]
 +  [#7539](https://github.com/rust-lang/rust-clippy/pull/7539)
 +* [`mod_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`self_named_module_files`]
 +  [#7543](https://github.com/rust-lang/rust-clippy/pull/7543)
 +* [`manual_split_once`]
 +  [#7565](https://github.com/rust-lang/rust-clippy/pull/7565)
 +* [`derivable_impls`]
 +  [#7570](https://github.com/rust-lang/rust-clippy/pull/7570)
 +* [`needless_option_as_deref`]
 +  [#7596](https://github.com/rust-lang/rust-clippy/pull/7596)
 +* [`iter_not_returning_iterator`]
 +  [#7610](https://github.com/rust-lang/rust-clippy/pull/7610)
 +* [`same_name_method`]
 +  [#7653](https://github.com/rust-lang/rust-clippy/pull/7653)
 +* [`manual_assert`] [#7669](https://github.com/rust-lang/rust-clippy/pull/7669)
 +* [`non_send_fields_in_send_ty`]
 +  [#7709](https://github.com/rust-lang/rust-clippy/pull/7709)
 +* [`equatable_if_let`]
 +  [#7762](https://github.com/rust-lang/rust-clippy/pull/7762)
 +
 +### Moves and Deprecations
 +
 +* Move [`shadow_unrelated`] to `restriction`
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* Move [`option_if_let_else`] to `nursery`
 +  [#7568](https://github.com/rust-lang/rust-clippy/pull/7568)
 +* Move [`branches_sharing_code`] to `nursery`
 +  [#7595](https://github.com/rust-lang/rust-clippy/pull/7595)
 +* Rename `if_let_some_result` to [`match_result_ok`] which now also handles
 +  `while let` cases [#7608](https://github.com/rust-lang/rust-clippy/pull/7608)
 +* Move [`many_single_char_names`] to `pedantic`
 +  [#7671](https://github.com/rust-lang/rust-clippy/pull/7671)
 +* Move [`float_cmp`] to `pedantic`
 +  [#7692](https://github.com/rust-lang/rust-clippy/pull/7692)
 +* Rename `box_vec` to [`box_collection`] and lint on more general cases
 +  [#7693](https://github.com/rust-lang/rust-clippy/pull/7693)
 +* Uplift `invalid_atomic_ordering` to rustc
 +  [rust-lang/rust#84039](https://github.com/rust-lang/rust/pull/84039)
 +
 +### Enhancements
 +
 +* Rewrite the `shadow*` lints, so that they find a lot more shadows and are not
 +  limited to certain patterns
 +  [#7338](https://github.com/rust-lang/rust-clippy/pull/7338)
 +* The `avoid-breaking-exported-api` configuration now also works for
 +  [`box_collection`], [`redundant_allocation`], [`rc_buffer`], [`vec_box`],
 +  [`option_option`], [`linkedlist`], [`rc_mutex`]
 +  [#7560](https://github.com/rust-lang/rust-clippy/pull/7560)
 +* [`unnecessary_unwrap`]: Now also checks for `expect`s
 +  [#7584](https://github.com/rust-lang/rust-clippy/pull/7584)
 +* [`disallowed_methods`]: Allow adding a reason that will be displayed with the
 +  lint message
 +  [#7621](https://github.com/rust-lang/rust-clippy/pull/7621)
 +* [`approx_constant`]: Now checks the MSRV for `LOG10_2` and `LOG2_10`
 +  [#7629](https://github.com/rust-lang/rust-clippy/pull/7629)
 +* [`approx_constant`]: Add `TAU`
 +  [#7642](https://github.com/rust-lang/rust-clippy/pull/7642)
 +* [`needless_borrow`]: Now also lints on needless mutable borrows
 +  [#7657](https://github.com/rust-lang/rust-clippy/pull/7657)
 +* [`missing_safety_doc`]: Now also lints on unsafe traits
 +  [#7734](https://github.com/rust-lang/rust-clippy/pull/7734)
 +
 +### False Positive Fixes
 +
 +* [`manual_map`]: No longer lints when the option is borrowed in the match and
 +  also consumed in the arm
 +  [#7531](https://github.com/rust-lang/rust-clippy/pull/7531)
 +* [`filter_next`]: No longer lints if `filter` method is not the
 +  `Iterator::filter` method
 +  [#7562](https://github.com/rust-lang/rust-clippy/pull/7562)
 +* [`manual_flatten`]: No longer lints if expression is used after `if let`
 +  [#7566](https://github.com/rust-lang/rust-clippy/pull/7566)
 +* [`option_if_let_else`]: Multiple fixes
 +  [#7573](https://github.com/rust-lang/rust-clippy/pull/7573)
 +    * `break` and `continue` statements local to the would-be closure are
 +      allowed
 +    * Don't lint in const contexts
 +    * Don't lint when yield expressions are used
 +    * Don't lint when the captures made by the would-be closure conflict with
 +      the other branch
 +    * Don't lint when a field of a local is used when the type could be
 +      potentially moved from
 +    * In some cases, don't lint when scrutinee expression conflicts with the
 +      captures of the would-be closure
 +* [`redundant_allocation`]: No longer lints on `Box<Box<dyn T>>` which replaces
 +  wide pointers with thin pointers
 +  [#7592](https://github.com/rust-lang/rust-clippy/pull/7592)
 +* [`bool_assert_comparison`]: No longer lints on types that do not implement the
 +  `Not` trait with `Output = bool`
 +  [#7605](https://github.com/rust-lang/rust-clippy/pull/7605)
 +* [`mut_range_bound`]: No longer lints on range bound mutations, that are
 +  immediately followed by a `break;`
 +  [#7607](https://github.com/rust-lang/rust-clippy/pull/7607)
 +* [`mutable_key_type`]: Improve accuracy and document remaining false positives
 +  and false negatives
 +  [#7640](https://github.com/rust-lang/rust-clippy/pull/7640)
 +* [`redundant_closure`]: Rewrite the lint to fix various false positives and
 +  false negatives [#7661](https://github.com/rust-lang/rust-clippy/pull/7661)
 +* [`large_enum_variant`]: No longer wrongly identifies the second largest
 +  variant [#7677](https://github.com/rust-lang/rust-clippy/pull/7677)
 +* [`needless_return`]: No longer lints on let-else expressions
 +  [#7685](https://github.com/rust-lang/rust-clippy/pull/7685)
 +* [`suspicious_else_formatting`]: No longer lints in proc-macros
 +  [#7707](https://github.com/rust-lang/rust-clippy/pull/7707)
 +* [`excessive_precision`]: No longer lints when in some cases the float was
 +  already written in the shortest form
 +  [#7722](https://github.com/rust-lang/rust-clippy/pull/7722)
 +* [`doc_markdown`]: No longer lints on intra-doc links
 +  [#7772](https://github.com/rust-lang/rust-clippy/pull/7772)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_operation`]: Recommend using an `assert!` instead of using a
 +  function call in an indexing operation
 +  [#7453](https://github.com/rust-lang/rust-clippy/pull/7453)
 +* [`manual_split_once`]: Produce semantically equivalent suggestion when
 +  `rsplitn` is used [#7663](https://github.com/rust-lang/rust-clippy/pull/7663)
 +* [`while_let_on_iterator`]: Produce correct suggestion when using `&mut`
 +  [#7690](https://github.com/rust-lang/rust-clippy/pull/7690)
 +* [`manual_assert`]: No better handles complex conditions
 +  [#7741](https://github.com/rust-lang/rust-clippy/pull/7741)
 +* Correctly handle signs in exponents in numeric literals lints
 +  [#7747](https://github.com/rust-lang/rust-clippy/pull/7747)
 +* [`suspicious_map`]: Now also suggests to use `inspect` as an alternative
 +  [#7770](https://github.com/rust-lang/rust-clippy/pull/7770)
 +* Drop exponent from suggestion if it is 0 in numeric literals lints
 +  [#7774](https://github.com/rust-lang/rust-clippy/pull/7774)
 +
 +### ICE Fixes
 +
 +* [`implicit_hasher`]
 +  [#7761](https://github.com/rust-lang/rust-clippy/pull/7761)
 +
 +### Others
 +
 +* Clippy now uses the 2021
 +  [Edition!](https://www.youtube.com/watch?v=q0aNduqb2Ro)
 +  [#7664](https://github.com/rust-lang/rust-clippy/pull/7664)
 +
 +## Rust 1.56
 +
 +Released 2021-10-21
 +
 +[74d1561...7bfc26e](https://github.com/rust-lang/rust-clippy/compare/74d1561...7bfc26e)
 +
 +### New Lints
 +
 +* [`unwrap_or_else_default`]
 +  [#7516](https://github.com/rust-lang/rust-clippy/pull/7516)
 +
 +### Enhancements
 +
 +* [`needless_continue`]: Now also lints in `loop { continue; }` case
 +  [#7477](https://github.com/rust-lang/rust-clippy/pull/7477)
 +* [`disallowed_types`]: Now also primitive types can be disallowed
 +  [#7488](https://github.com/rust-lang/rust-clippy/pull/7488)
 +* [`manual_swap`]: Now also lints on xor swaps
 +  [#7506](https://github.com/rust-lang/rust-clippy/pull/7506)
 +* [`map_flatten`]: Now also lints on the `Result` type
 +  [#7522](https://github.com/rust-lang/rust-clippy/pull/7522)
 +* [`no_effect`]: Now also lints on inclusive ranges
 +  [#7556](https://github.com/rust-lang/rust-clippy/pull/7556)
 +
 +### False Positive Fixes
 +
 +* [`nonstandard_macro_braces`]: No longer lints on similar named nested macros
 +  [#7478](https://github.com/rust-lang/rust-clippy/pull/7478)
 +* [`too_many_lines`]: No longer lints in closures to avoid duplicated diagnostics
 +  [#7534](https://github.com/rust-lang/rust-clippy/pull/7534)
 +* [`similar_names`]: No longer complains about `iter` and `item` being too
 +  similar [#7546](https://github.com/rust-lang/rust-clippy/pull/7546)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`similar_names`]: No longer suggests to insert or add an underscore as a fix
 +  [#7221](https://github.com/rust-lang/rust-clippy/pull/7221)
 +* [`new_without_default`]: No longer shows the full qualified type path when
 +  suggesting adding a `Default` implementation
 +  [#7493](https://github.com/rust-lang/rust-clippy/pull/7493)
 +* [`while_let_on_iterator`]: Now suggests re-borrowing mutable references
 +  [#7520](https://github.com/rust-lang/rust-clippy/pull/7520)
 +* [`extend_with_drain`]: Improve code suggestion for mutable and immutable
 +  references [#7533](https://github.com/rust-lang/rust-clippy/pull/7533)
 +* [`trivially_copy_pass_by_ref`]: Now properly handles `Self` type
 +  [#7535](https://github.com/rust-lang/rust-clippy/pull/7535)
 +* [`never_loop`]: Now suggests using `if let` instead of a `for` loop when
 +  applicable [#7541](https://github.com/rust-lang/rust-clippy/pull/7541)
 +
 +### Documentation Improvements
 +
 +* Clippy now uses a lint to generate its lint documentation. [Lints all the way
 +  down](https://en.wikipedia.org/wiki/Turtles_all_the_way_down).
 +  [#7502](https://github.com/rust-lang/rust-clippy/pull/7502)
 +* Reworked Clippy's website:
 +  [#7172](https://github.com/rust-lang/rust-clippy/issues/7172)
 +  [#7279](https://github.com/rust-lang/rust-clippy/pull/7279)
 +  * Added applicability information about lints
 +  * Added a link to jump into the implementation
 +  * Improved loading times
 +  * Adapted some styling
 +* `cargo clippy --help` now also explains the `--fix` and `--no-deps` flag
 +  [#7492](https://github.com/rust-lang/rust-clippy/pull/7492)
 +* [`unnested_or_patterns`]: Removed `or_patterns` feature gate in the code
 +  example [#7507](https://github.com/rust-lang/rust-clippy/pull/7507)
 +
 +## Rust 1.55
 +
 +Released 2021-09-09
 +
 +[3ae8faf...74d1561](https://github.com/rust-lang/rust-clippy/compare/3ae8faf...74d1561)
 +
 +### Important Changes
 +
 +* Stabilized `cargo clippy --fix` :tada:
 +  [#7405](https://github.com/rust-lang/rust-clippy/pull/7405)
 +
 +### New Lints
 +
 +* [`rc_mutex`]
 +  [#7316](https://github.com/rust-lang/rust-clippy/pull/7316)
 +* [`nonstandard_macro_braces`]
 +  [#7299](https://github.com/rust-lang/rust-clippy/pull/7299)
 +* [`strlen_on_c_strings`]
 +  [#7243](https://github.com/rust-lang/rust-clippy/pull/7243)
 +* [`self_named_constructors`]
 +  [#7403](https://github.com/rust-lang/rust-clippy/pull/7403)
 +* [`disallowed_script_idents`]
 +  [#7400](https://github.com/rust-lang/rust-clippy/pull/7400)
 +* [`disallowed_types`]
 +  [#7315](https://github.com/rust-lang/rust-clippy/pull/7315)
 +* [`missing_enforced_import_renames`]
 +  [#7300](https://github.com/rust-lang/rust-clippy/pull/7300)
 +* [`extend_with_drain`]
 +  [#7270](https://github.com/rust-lang/rust-clippy/pull/7270)
 +
 +### Moves and Deprecations
 +
 +* Moved [`from_iter_instead_of_collect`] to `pedantic`
 +  [#7375](https://github.com/rust-lang/rust-clippy/pull/7375)
 +* Added `suspicious` as a new lint group for *code that is most likely wrong or useless*
 +  [#7350](https://github.com/rust-lang/rust-clippy/pull/7350)
 +  * Moved [`blanket_clippy_restriction_lints`] to `suspicious`
 +  * Moved [`empty_loop`] to `suspicious`
 +  * Moved [`eval_order_dependence`] to `suspicious`
 +  * Moved [`float_equality_without_abs`] to `suspicious`
 +  * Moved [`for_loops_over_fallibles`] to `suspicious`
 +  * Moved [`misrefactored_assign_op`] to `suspicious`
 +  * Moved [`mut_range_bound`] to `suspicious`
 +  * Moved [`mutable_key_type`] to `suspicious`
 +  * Moved [`suspicious_arithmetic_impl`] to `suspicious`
 +  * Moved [`suspicious_assignment_formatting`] to `suspicious`
 +  * Moved [`suspicious_else_formatting`] to `suspicious`
 +  * Moved [`suspicious_map`] to `suspicious`
 +  * Moved [`suspicious_op_assign_impl`] to `suspicious`
 +  * Moved [`suspicious_unary_op_formatting`] to `suspicious`
 +
 +### Enhancements
 +
 +* [`while_let_on_iterator`]: Now suggests `&mut iter` inside closures
 +  [#7262](https://github.com/rust-lang/rust-clippy/pull/7262)
 +* [`doc_markdown`]:
 +  * Now detects unbalanced ticks
 +    [#7357](https://github.com/rust-lang/rust-clippy/pull/7357)
 +  * Add `FreeBSD` to the default configuration as an allowed identifier
 +    [#7334](https://github.com/rust-lang/rust-clippy/pull/7334)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]: Now allows wildcards for enums with unstable
 +  or hidden variants
 +  [#7407](https://github.com/rust-lang/rust-clippy/pull/7407)
 +* [`redundant_allocation`]: Now additionally supports the `Arc<>` type
 +  [#7308](https://github.com/rust-lang/rust-clippy/pull/7308)
 +* [`disallowed_names`]: Now allows disallowed names in test code
 +  [#7379](https://github.com/rust-lang/rust-clippy/pull/7379)
 +* [`redundant_closure`]: Suggests `&mut` for `FnMut`
 +  [#7437](https://github.com/rust-lang/rust-clippy/pull/7437)
 +* [`disallowed_methods`], [`disallowed_types`]: The configuration values `disallowed-method` and `disallowed-type`
 +  no longer require fully qualified paths
 +  [#7345](https://github.com/rust-lang/rust-clippy/pull/7345)
 +* [`zst_offset`]: Fixed lint invocation after it was accidentally suppressed
 +  [#7396](https://github.com/rust-lang/rust-clippy/pull/7396)
 +
 +### False Positive Fixes
 +
 +* [`default_numeric_fallback`]: No longer lints on float literals as function arguments
 +  [#7446](https://github.com/rust-lang/rust-clippy/pull/7446)
 +* [`use_self`]: No longer lints on type parameters
 +  [#7288](https://github.com/rust-lang/rust-clippy/pull/7288)
 +* [`unimplemented`]: Now ignores the `assert` and `debug_assert` macros
 +  [#7439](https://github.com/rust-lang/rust-clippy/pull/7439)
 +* [`branches_sharing_code`]: Now always checks for block expressions
 +  [#7462](https://github.com/rust-lang/rust-clippy/pull/7462)
 +* [`field_reassign_with_default`]: No longer triggers in macros
 +  [#7160](https://github.com/rust-lang/rust-clippy/pull/7160)
 +* [`redundant_clone`]: No longer lints on required clones for borrowed data
 +  [#7346](https://github.com/rust-lang/rust-clippy/pull/7346)
 +* [`default_numeric_fallback`]: No longer triggers in external macros
 +  [#7325](https://github.com/rust-lang/rust-clippy/pull/7325)
 +* [`needless_bool`]: No longer lints in macros
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`useless_format`]: No longer triggers when additional text is being appended
 +  [#7442](https://github.com/rust-lang/rust-clippy/pull/7442)
 +* [`assertions_on_constants`]: `cfg!(...)` is no longer considered to be a constant
 +  [#7319](https://github.com/rust-lang/rust-clippy/pull/7319)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`needless_collect`]: Now show correct lint messages for shadowed values
 +  [#7289](https://github.com/rust-lang/rust-clippy/pull/7289)
 +* [`wrong_pub_self_convention`]: The deprecated message now suggest the correct configuration value
 +  [#7382](https://github.com/rust-lang/rust-clippy/pull/7382)
 +* [`semicolon_if_nothing_returned`]: Allow missing semicolon in blocks with only one expression
 +  [#7326](https://github.com/rust-lang/rust-clippy/pull/7326)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#7470](https://github.com/rust-lang/rust-clippy/pull/7470)
 +* [`redundant_pattern_matching`]
 +  [#7471](https://github.com/rust-lang/rust-clippy/pull/7471)
 +* [`modulo_one`]
 +  [#7473](https://github.com/rust-lang/rust-clippy/pull/7473)
 +* [`use_self`]
 +  [#7428](https://github.com/rust-lang/rust-clippy/pull/7428)
 +
 +## Rust 1.54
 +
 +Released 2021-07-29
 +
 +[7c7683c...3ae8faf](https://github.com/rust-lang/rust-clippy/compare/7c7683c...3ae8faf)
 +
 +### New Lints
 +
 +- [`ref_binding_to_reference`]
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`needless_bitwise_bool`]
 +  [#7133](https://github.com/rust-lang/rust-clippy/pull/7133)
 +- [`unused_async`] [#7225](https://github.com/rust-lang/rust-clippy/pull/7225)
 +- [`manual_str_repeat`]
 +  [#7265](https://github.com/rust-lang/rust-clippy/pull/7265)
 +- [`suspicious_splitn`]
 +  [#7292](https://github.com/rust-lang/rust-clippy/pull/7292)
 +
 +### Moves and Deprecations
 +
 +- Deprecate `pub_enum_variant_names` and `wrong_pub_self_convention` in favor of
 +  the new `avoid-breaking-exported-api` config option (see
 +  [Enhancements](#1-54-enhancements))
 +  [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- Move [`inconsistent_struct_constructor`] to `pedantic`
 +  [#7193](https://github.com/rust-lang/rust-clippy/pull/7193)
 +- Move [`needless_borrow`] to `style` (now warn-by-default)
 +  [#7254](https://github.com/rust-lang/rust-clippy/pull/7254)
 +- Move [`suspicious_operation_groupings`] to `nursery`
 +  [#7266](https://github.com/rust-lang/rust-clippy/pull/7266)
 +- Move [`semicolon_if_nothing_returned`] to `pedantic`
 +  [#7268](https://github.com/rust-lang/rust-clippy/pull/7268)
 +
 +### Enhancements <a name="1-54-enhancements"></a>
 +
 +- [`while_let_on_iterator`]: Now also lints in nested loops
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`single_char_pattern`]: Now also lints on `strip_prefix` and `strip_suffix`
 +  [#7156](https://github.com/rust-lang/rust-clippy/pull/7156)
 +- [`needless_collect`]: Now also lints on assignments with type annotations
 +  [#7163](https://github.com/rust-lang/rust-clippy/pull/7163)
 +- [`if_then_some_else_none`]: Now works with the MSRV config
 +  [#7177](https://github.com/rust-lang/rust-clippy/pull/7177)
 +- Add `avoid-breaking-exported-api` config option for the lints
 +  [`enum_variant_names`], [`large_types_passed_by_value`],
 +  [`trivially_copy_pass_by_ref`], [`unnecessary_wraps`],
 +  [`upper_case_acronyms`], and [`wrong_self_convention`]. We recommend to set
 +  this configuration option to `false` before a major release (1.0/2.0/...) to
 +  clean up the API [#7187](https://github.com/rust-lang/rust-clippy/pull/7187)
 +- [`needless_collect`]: Now lints on even more data structures
 +  [#7188](https://github.com/rust-lang/rust-clippy/pull/7188)
 +- [`missing_docs_in_private_items`]: No longer sees `#[<name> = "<value>"]` like
 +  attributes as sufficient documentation
 +  [#7281](https://github.com/rust-lang/rust-clippy/pull/7281)
 +- [`needless_collect`], [`short_circuit_statement`], [`unnecessary_operation`]:
 +  Now work as expected when used with `allow`
 +  [#7282](https://github.com/rust-lang/rust-clippy/pull/7282)
 +
 +### False Positive Fixes
 +
 +- [`implicit_return`]: Now takes all diverging functions in account to avoid
 +  false positives [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +- [`while_let_on_iterator`]: No longer lints when the iterator is a struct field
 +  and the struct is used in the loop
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`multiple_inherent_impl`]: No longer lints with generic arguments
 +  [#7089](https://github.com/rust-lang/rust-clippy/pull/7089)
 +- [`comparison_chain`]: No longer lints in a `const` context
 +  [#7118](https://github.com/rust-lang/rust-clippy/pull/7118)
 +- [`while_immutable_condition`]: Fix false positive where mutation in the loop
 +  variable wasn't picked up
 +  [#7144](https://github.com/rust-lang/rust-clippy/pull/7144)
 +- [`default_trait_access`]: No longer lints in macros
 +  [#7150](https://github.com/rust-lang/rust-clippy/pull/7150)
 +- [`needless_question_mark`]: No longer lints when the inner value is implicitly
 +  dereferenced [#7165](https://github.com/rust-lang/rust-clippy/pull/7165)
 +- [`unused_unit`]: No longer lints when multiple macro contexts are involved
 +  [#7167](https://github.com/rust-lang/rust-clippy/pull/7167)
 +- [`eval_order_dependence`]: Fix false positive in async context
 +  [#7174](https://github.com/rust-lang/rust-clippy/pull/7174)
 +- [`unnecessary_filter_map`]: No longer lints if the `filter_map` changes the
 +  type [#7175](https://github.com/rust-lang/rust-clippy/pull/7175)
 +- [`wrong_self_convention`]: No longer lints in trait implementations of
 +  non-`Copy` types [#7182](https://github.com/rust-lang/rust-clippy/pull/7182)
 +- [`suboptimal_flops`]: No longer lints on `powi(2)`
 +  [#7201](https://github.com/rust-lang/rust-clippy/pull/7201)
 +- [`wrong_self_convention`]: No longer lints if there is no implicit `self`
 +  [#7215](https://github.com/rust-lang/rust-clippy/pull/7215)
 +- [`option_if_let_else`]: No longer lints on `else if let` pattern
 +  [#7216](https://github.com/rust-lang/rust-clippy/pull/7216)
 +- [`use_self`], [`useless_conversion`]: Fix false positives when generic
 +  arguments are involved
 +  [#7223](https://github.com/rust-lang/rust-clippy/pull/7223)
 +- [`manual_unwrap_or`]: Fix false positive with deref coercion
 +  [#7233](https://github.com/rust-lang/rust-clippy/pull/7233)
 +- [`similar_names`]: No longer lints on `wparam`/`lparam`
 +  [#7255](https://github.com/rust-lang/rust-clippy/pull/7255)
 +- [`redundant_closure`]: No longer lints on using the `vec![]` macro in a
 +  closure [#7263](https://github.com/rust-lang/rust-clippy/pull/7263)
 +
 +### Suggestion Fixes/Improvements
 +
 +- [`implicit_return`]
 +  [#6951](https://github.com/rust-lang/rust-clippy/pull/6951)
 +    - Fix suggestion for async functions
 +    - Improve suggestion with macros
 +    - Suggest to change `break` to `return` when appropriate
 +- [`while_let_on_iterator`]: Now suggests `&mut iter` when necessary
 +  [#6966](https://github.com/rust-lang/rust-clippy/pull/6966)
 +- [`match_single_binding`]: Improve suggestion when match scrutinee has side
 +  effects [#7095](https://github.com/rust-lang/rust-clippy/pull/7095)
 +- [`needless_borrow`]: Now suggests to also change usage sites as needed
 +  [#7105](https://github.com/rust-lang/rust-clippy/pull/7105)
 +- [`write_with_newline`]: Improve suggestion when only `\n` is written to the
 +  buffer [#7183](https://github.com/rust-lang/rust-clippy/pull/7183)
 +- [`from_iter_instead_of_collect`]: The suggestion is now auto applicable also
 +  when a `<_ as Trait>::_` is involved
 +  [#7264](https://github.com/rust-lang/rust-clippy/pull/7264)
 +- [`not_unsafe_ptr_arg_deref`]: Improved error message
 +  [#7294](https://github.com/rust-lang/rust-clippy/pull/7294)
 +
 +### ICE Fixes
 +
 +- Fix ICE when running Clippy on `libstd`
 +  [#7140](https://github.com/rust-lang/rust-clippy/pull/7140)
 +- [`implicit_return`]
 +  [#7242](https://github.com/rust-lang/rust-clippy/pull/7242)
 +
 +## Rust 1.53
 +
 +Released 2021-06-17
 +
 +[6ed6f1e...7c7683c](https://github.com/rust-lang/rust-clippy/compare/6ed6f1e...7c7683c)
 +
 +### New Lints
 +
 +* [`option_filter_map`]
 +  [#6342](https://github.com/rust-lang/rust-clippy/pull/6342)
 +* [`branches_sharing_code`]
 +  [#6463](https://github.com/rust-lang/rust-clippy/pull/6463)
 +* [`needless_for_each`]
 +  [#6706](https://github.com/rust-lang/rust-clippy/pull/6706)
 +* [`if_then_some_else_none`]
 +  [#6859](https://github.com/rust-lang/rust-clippy/pull/6859)
 +* [`non_octal_unix_permissions`]
 +  [#7001](https://github.com/rust-lang/rust-clippy/pull/7001)
 +* [`unnecessary_self_imports`]
 +  [#7072](https://github.com/rust-lang/rust-clippy/pull/7072)
 +* [`bool_assert_comparison`]
 +  [#7083](https://github.com/rust-lang/rust-clippy/pull/7083)
 +* [`cloned_instead_of_copied`]
 +  [#7098](https://github.com/rust-lang/rust-clippy/pull/7098)
 +* [`flat_map_option`]
 +  [#7101](https://github.com/rust-lang/rust-clippy/pull/7101)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`filter_map`] lint
 +  [#7059](https://github.com/rust-lang/rust-clippy/pull/7059)
 +* Move [`transmute_ptr_to_ptr`] to `pedantic`
 +  [#7102](https://github.com/rust-lang/rust-clippy/pull/7102)
 +
 +### Enhancements
 +
 +* [`mem_replace_with_default`]: Also lint on common std constructors
 +  [#6820](https://github.com/rust-lang/rust-clippy/pull/6820)
 +* [`wrong_self_convention`]: Also lint on `to_*_mut` methods
 +  [#6828](https://github.com/rust-lang/rust-clippy/pull/6828)
 +* [`wildcard_enum_match_arm`], [`match_wildcard_for_single_variants`]:
 +  [#6863](https://github.com/rust-lang/rust-clippy/pull/6863)
 +    * Attempt to find a common path prefix in suggestion
 +    * Don't lint on `Option` and `Result`
 +    * Consider `Self` prefix
 +* [`explicit_deref_methods`]: Also lint on chained `deref` calls
 +  [#6865](https://github.com/rust-lang/rust-clippy/pull/6865)
 +* [`or_fun_call`]: Also lint on `unsafe` blocks
 +  [#6928](https://github.com/rust-lang/rust-clippy/pull/6928)
 +* [`vec_box`], [`linkedlist`], [`option_option`]: Also lint in `const` and
 +  `static` items [#6938](https://github.com/rust-lang/rust-clippy/pull/6938)
 +* [`search_is_some`]: Also check for `is_none`
 +  [#6942](https://github.com/rust-lang/rust-clippy/pull/6942)
 +* [`string_lit_as_bytes`]: Also lint on `into_bytes`
 +  [#6959](https://github.com/rust-lang/rust-clippy/pull/6959)
 +* [`len_without_is_empty`]: Also lint if function signatures of `len` and
 +  `is_empty` don't match
 +  [#6980](https://github.com/rust-lang/rust-clippy/pull/6980)
 +* [`redundant_pattern_matching`]: Also lint if the pattern is a `&` pattern
 +  [#6991](https://github.com/rust-lang/rust-clippy/pull/6991)
 +* [`clone_on_copy`]: Also lint on chained method calls taking `self` by value
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`missing_panics_doc`]: Also lint on `assert_eq!` and `assert_ne!`
 +  [#7029](https://github.com/rust-lang/rust-clippy/pull/7029)
 +* [`needless_return`]: Also lint in `async` functions
 +  [#7067](https://github.com/rust-lang/rust-clippy/pull/7067)
 +* [`unused_io_amount`]: Also lint on expressions like `_.read().ok()?`
 +  [#7100](https://github.com/rust-lang/rust-clippy/pull/7100)
 +* [`iter_cloned_collect`]: Also lint on large arrays, since const-generics are
 +  now stable [#7138](https://github.com/rust-lang/rust-clippy/pull/7138)
 +
 +### False Positive Fixes
 +
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6805](https://github.com/rust-lang/rust-clippy/pull/6805)
 +* [`suspicious_map`]: No longer lints when side effects may occur inside the
 +  `map` call [#6831](https://github.com/rust-lang/rust-clippy/pull/6831)
 +* [`manual_map`], [`manual_unwrap_or`]: No longer lints in `const` functions
 +  [#6917](https://github.com/rust-lang/rust-clippy/pull/6917)
 +* [`wrong_self_convention`]: Now respects `Copy` types
 +  [#6924](https://github.com/rust-lang/rust-clippy/pull/6924)
 +* [`needless_question_mark`]: No longer lints if the `?` and the `Some(..)` come
 +  from different macro contexts [#6935](https://github.com/rust-lang/rust-clippy/pull/6935)
 +* [`map_entry`]: Better detect if the entry API can be used
 +  [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`or_fun_call`]: No longer lints on some `len` function calls
 +  [#6950](https://github.com/rust-lang/rust-clippy/pull/6950)
 +* [`new_ret_no_self`]: No longer lints when `Self` is returned with different
 +  generic arguments [#6952](https://github.com/rust-lang/rust-clippy/pull/6952)
 +* [`upper_case_acronyms`]: No longer lints on public items
 +  [#6981](https://github.com/rust-lang/rust-clippy/pull/6981)
 +* [`explicit_into_iter_loop`]: Only lint when `into_iter` is an implementation
 +  of `IntoIterator` [#6982](https://github.com/rust-lang/rust-clippy/pull/6982)
 +* [`expl_impl_clone_on_copy`]: Take generic constraints into account before
 +  suggesting to use `derive` instead
 +  [#6993](https://github.com/rust-lang/rust-clippy/pull/6993)
 +* [`missing_panics_doc`]: No longer lints when only debug-assertions are used
 +  [#6996](https://github.com/rust-lang/rust-clippy/pull/6996)
 +* [`clone_on_copy`]: Only lint when using the `Clone` trait
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`wrong_self_convention`]: No longer lints inside a trait implementation
 +  [#7002](https://github.com/rust-lang/rust-clippy/pull/7002)
 +* [`redundant_clone`]: No longer lints when the cloned value is modified while
 +  the clone is in use
 +  [#7011](https://github.com/rust-lang/rust-clippy/pull/7011)
 +* [`same_item_push`]: No longer lints if the `Vec` is used in the loop body
 +  [#7018](https://github.com/rust-lang/rust-clippy/pull/7018)
 +* [`cargo_common_metadata`]: Remove author requirement
 +  [#7026](https://github.com/rust-lang/rust-clippy/pull/7026)
 +* [`panic_in_result_fn`]: No longer lints on `debug_assert` family
 +  [#7060](https://github.com/rust-lang/rust-clippy/pull/7060)
 +* [`panic`]: No longer wrongfully lints on `debug_assert` with message
 +  [#7063](https://github.com/rust-lang/rust-clippy/pull/7063)
 +* [`wrong_self_convention`]: No longer lints in trait implementations where no
 +  `self` is involved [#7064](https://github.com/rust-lang/rust-clippy/pull/7064)
 +* [`missing_const_for_fn`]: No longer lints when unstable `const` function is
 +  involved [#7076](https://github.com/rust-lang/rust-clippy/pull/7076)
 +* [`suspicious_else_formatting`]: Allow Allman style braces
 +  [#7087](https://github.com/rust-lang/rust-clippy/pull/7087)
 +* [`inconsistent_struct_constructor`]: No longer lints in macros
 +  [#7097](https://github.com/rust-lang/rust-clippy/pull/7097)
 +* [`single_component_path_imports`]: No longer lints on macro re-exports
 +  [#7120](https://github.com/rust-lang/rust-clippy/pull/7120)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`redundant_pattern_matching`]: Add a note when applying this lint would
 +  change the drop order
 +  [#6568](https://github.com/rust-lang/rust-clippy/pull/6568)
 +* [`write_literal`], [`print_literal`]: Add auto-applicable suggestion
 +  [#6821](https://github.com/rust-lang/rust-clippy/pull/6821)
 +* [`manual_map`]: Fix suggestion for complex `if let ... else` chains
 +  [#6856](https://github.com/rust-lang/rust-clippy/pull/6856)
 +* [`inconsistent_struct_constructor`]: Make lint description and message clearer
 +  [#6892](https://github.com/rust-lang/rust-clippy/pull/6892)
 +* [`map_entry`]: Now suggests `or_insert`, `insert_with` or `match _.entry(_)`
 +  as appropriate [#6937](https://github.com/rust-lang/rust-clippy/pull/6937)
 +* [`manual_flatten`]: Suggest to insert `copied` if necessary
 +  [#6962](https://github.com/rust-lang/rust-clippy/pull/6962)
 +* [`redundant_slicing`]: Fix suggestion when a re-borrow might be required or
 +  when the value is from a macro call
 +  [#6975](https://github.com/rust-lang/rust-clippy/pull/6975)
 +* [`match_wildcard_for_single_variants`]: Fix suggestion for hidden variant
 +  [#6988](https://github.com/rust-lang/rust-clippy/pull/6988)
 +* [`clone_on_copy`]: Correct suggestion when the cloned value is a macro call
 +  [#7000](https://github.com/rust-lang/rust-clippy/pull/7000)
 +* [`manual_map`]: Fix suggestion at the end of an if chain
 +  [#7004](https://github.com/rust-lang/rust-clippy/pull/7004)
 +* Fix needless parenthesis output in multiple lint suggestions
 +  [#7013](https://github.com/rust-lang/rust-clippy/pull/7013)
 +* [`needless_collect`]: Better explanation in the lint message
 +  [#7020](https://github.com/rust-lang/rust-clippy/pull/7020)
 +* [`useless_vec`]: Now considers mutability
 +  [#7036](https://github.com/rust-lang/rust-clippy/pull/7036)
 +* [`useless_format`]: Wrap the content in braces if necessary
 +  [#7092](https://github.com/rust-lang/rust-clippy/pull/7092)
 +* [`single_match`]: Don't suggest an equality check for types which don't
 +  implement `PartialEq`
 +  [#7093](https://github.com/rust-lang/rust-clippy/pull/7093)
 +* [`from_over_into`]: Mention type in help message
 +  [#7099](https://github.com/rust-lang/rust-clippy/pull/7099)
 +* [`manual_unwrap_or`]: Fix invalid code suggestion due to a macro call
 +  [#7136](https://github.com/rust-lang/rust-clippy/pull/7136)
 +
 +### ICE Fixes
 +
 +* [`macro_use_imports`]
 +  [#7022](https://github.com/rust-lang/rust-clippy/pull/7022)
 +* [`missing_panics_doc`]
 +  [#7034](https://github.com/rust-lang/rust-clippy/pull/7034)
 +* [`tabs_in_doc_comments`]
 +  [#7039](https://github.com/rust-lang/rust-clippy/pull/7039)
 +* [`missing_const_for_fn`]
 +  [#7128](https://github.com/rust-lang/rust-clippy/pull/7128)
 +
 +### Others
 +
 +* [Clippy's lint
 +  list](https://rust-lang.github.io/rust-clippy/master/index.html) now supports
 +  themes [#7030](https://github.com/rust-lang/rust-clippy/pull/7030)
 +* Lints that were uplifted to `rustc` now mention the new `rustc` name in the
 +  deprecation warning
 +  [#7056](https://github.com/rust-lang/rust-clippy/pull/7056)
 +
 +## Rust 1.52
 +
 +Released 2021-05-06
 +
 +[3e41797...6ed6f1e](https://github.com/rust-lang/rust-clippy/compare/3e41797...6ed6f1e)
 +
 +### New Lints
 +
 +* [`from_str_radix_10`]
 +  [#6717](https://github.com/rust-lang/rust-clippy/pull/6717)
 +* [`implicit_clone`]
 +  [#6730](https://github.com/rust-lang/rust-clippy/pull/6730)
 +* [`semicolon_if_nothing_returned`]
 +  [#6681](https://github.com/rust-lang/rust-clippy/pull/6681)
 +* [`manual_flatten`]
 +  [#6646](https://github.com/rust-lang/rust-clippy/pull/6646)
 +* [`inconsistent_struct_constructor`]
 +  [#6769](https://github.com/rust-lang/rust-clippy/pull/6769)
 +* [`iter_count`]
 +  [#6791](https://github.com/rust-lang/rust-clippy/pull/6791)
 +* [`default_numeric_fallback`]
 +  [#6662](https://github.com/rust-lang/rust-clippy/pull/6662)
 +* [`bytes_nth`]
 +  [#6695](https://github.com/rust-lang/rust-clippy/pull/6695)
 +* [`filter_map_identity`]
 +  [#6685](https://github.com/rust-lang/rust-clippy/pull/6685)
 +* [`manual_map`]
 +  [#6573](https://github.com/rust-lang/rust-clippy/pull/6573)
 +
 +### Moves and Deprecations
 +
 +* Moved [`upper_case_acronyms`] to `pedantic`
 +  [#6775](https://github.com/rust-lang/rust-clippy/pull/6775)
 +* Moved [`manual_map`] to `nursery`
 +  [#6796](https://github.com/rust-lang/rust-clippy/pull/6796)
 +* Moved [`unnecessary_wraps`] to `pedantic`
 +  [#6765](https://github.com/rust-lang/rust-clippy/pull/6765)
 +* Moved [`trivial_regex`] to `nursery`
 +  [#6696](https://github.com/rust-lang/rust-clippy/pull/6696)
 +* Moved [`naive_bytecount`] to `pedantic`
 +  [#6825](https://github.com/rust-lang/rust-clippy/pull/6825)
 +* Moved [`upper_case_acronyms`] to `style`
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* Moved [`manual_map`] to `style`
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +
 +### Enhancements
 +
 +* [`disallowed_methods`]: Now supports functions in addition to methods
 +  [#6674](https://github.com/rust-lang/rust-clippy/pull/6674)
 +* [`upper_case_acronyms`]: Added a new configuration `upper-case-acronyms-aggressive` to
 +  trigger the lint if there is more than one uppercase character next to each other
 +  [#6788](https://github.com/rust-lang/rust-clippy/pull/6788)
 +* [`collapsible_match`]: Now supports block comparison with different value names
 +  [#6754](https://github.com/rust-lang/rust-clippy/pull/6754)
 +* [`unnecessary_wraps`]: Will now suggest removing unnecessary wrapped return unit type, like `Option<()>`
 +  [#6665](https://github.com/rust-lang/rust-clippy/pull/6665)
 +* Improved value usage detection in closures
 +  [#6698](https://github.com/rust-lang/rust-clippy/pull/6698)
 +
 +### False Positive Fixes
 +
 +* [`use_self`]: No longer lints in macros
 +  [#6833](https://github.com/rust-lang/rust-clippy/pull/6833)
 +* [`use_self`]: Fixed multiple false positives for: generics, associated types and derive implementations
 +  [#6179](https://github.com/rust-lang/rust-clippy/pull/6179)
 +* [`missing_inline_in_public_items`]: No longer lints for procedural macros
 +  [#6814](https://github.com/rust-lang/rust-clippy/pull/6814)
 +* [`inherent_to_string`]: No longer lints on functions with function generics
 +  [#6771](https://github.com/rust-lang/rust-clippy/pull/6771)
 +* [`doc_markdown`]: Add `OpenDNS` to the default configuration as an allowed identifier
 +  [#6783](https://github.com/rust-lang/rust-clippy/pull/6783)
 +* [`missing_panics_doc`]: No longer lints on [`unreachable!`](https://doc.rust-lang.org/std/macro.unreachable.html)
 +  [#6700](https://github.com/rust-lang/rust-clippy/pull/6700)
 +* [`collapsible_if`]: No longer lints on if statements with attributes
 +  [#6701](https://github.com/rust-lang/rust-clippy/pull/6701)
 +* [`match_same_arms`]: Only considers empty blocks as equal if the tokens contained are the same
 +  [#6843](https://github.com/rust-lang/rust-clippy/pull/6843)
 +* [`redundant_closure`]: Now ignores macros
 +  [#6871](https://github.com/rust-lang/rust-clippy/pull/6871)
 +* [`manual_map`]: Fixed false positives when control flow statements like `return`, `break` etc. are used
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* [`vec_init_then_push`]: Fixed false positives for loops and if statements
 +  [#6697](https://github.com/rust-lang/rust-clippy/pull/6697)
 +* [`len_without_is_empty`]: Will now consider multiple impl blocks and `#[allow]` on
 +  the `len` method as well as the type definition.
 +  [#6853](https://github.com/rust-lang/rust-clippy/pull/6853)
 +* [`let_underscore_drop`]: Only lints on types which implement `Drop`
 +  [#6682](https://github.com/rust-lang/rust-clippy/pull/6682)
 +* [`unit_arg`]: No longer lints on unit arguments when they come from a path expression.
 +  [#6601](https://github.com/rust-lang/rust-clippy/pull/6601)
 +* [`cargo_common_metadata`]: No longer lints if
 +  [`publish = false`](https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field)
 +  is defined in the manifest
 +  [#6650](https://github.com/rust-lang/rust-clippy/pull/6650)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`collapsible_match`]: Fixed lint message capitalization
 +  [#6766](https://github.com/rust-lang/rust-clippy/pull/6766)
 +* [`or_fun_call`]: Improved suggestions for `or_insert(vec![])`
 +  [#6790](https://github.com/rust-lang/rust-clippy/pull/6790)
 +* [`manual_map`]: No longer expands macros in the suggestions
 +  [#6801](https://github.com/rust-lang/rust-clippy/pull/6801)
 +* Aligned Clippy's lint messages with the rustc dev guide
 +  [#6787](https://github.com/rust-lang/rust-clippy/pull/6787)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6866](https://github.com/rust-lang/rust-clippy/pull/6866)
 +
 +### Documentation Improvements
 +
 +* [`useless_format`]: Improved the documentation example
 +  [#6854](https://github.com/rust-lang/rust-clippy/pull/6854)
 +* Clippy's [`README.md`]: Includes a new subsection on running Clippy as a rustc wrapper
 +  [#6782](https://github.com/rust-lang/rust-clippy/pull/6782)
 +
 +### Others
 +* Running `cargo clippy` after `cargo check` now works as expected
 +  (`cargo clippy` and `cargo check` no longer shares the same build cache)
 +  [#6687](https://github.com/rust-lang/rust-clippy/pull/6687)
 +* Cargo now re-runs Clippy if arguments after `--` provided to `cargo clippy` are changed.
 +  [#6834](https://github.com/rust-lang/rust-clippy/pull/6834)
 +* Extracted Clippy's `utils` module into the new `clippy_utils` crate
 +  [#6756](https://github.com/rust-lang/rust-clippy/pull/6756)
 +* Clippy lintcheck tool improvements
 +  [#6800](https://github.com/rust-lang/rust-clippy/pull/6800)
 +  [#6735](https://github.com/rust-lang/rust-clippy/pull/6735)
 +  [#6764](https://github.com/rust-lang/rust-clippy/pull/6764)
 +  [#6708](https://github.com/rust-lang/rust-clippy/pull/6708)
 +  [#6780](https://github.com/rust-lang/rust-clippy/pull/6780)
 +  [#6686](https://github.com/rust-lang/rust-clippy/pull/6686)
 +
 +## Rust 1.51
 +
 +Released 2021-03-25
 +
 +[4911ab1...3e41797](https://github.com/rust-lang/rust-clippy/compare/4911ab1...3e41797)
 +
 +### New Lints
 +
 +* [`upper_case_acronyms`]
 +  [#6475](https://github.com/rust-lang/rust-clippy/pull/6475)
 +* [`from_over_into`] [#6476](https://github.com/rust-lang/rust-clippy/pull/6476)
 +* [`case_sensitive_file_extension_comparisons`]
 +  [#6500](https://github.com/rust-lang/rust-clippy/pull/6500)
 +* [`needless_question_mark`]
 +  [#6507](https://github.com/rust-lang/rust-clippy/pull/6507)
 +* [`missing_panics_doc`]
 +  [#6523](https://github.com/rust-lang/rust-clippy/pull/6523)
 +* [`redundant_slicing`]
 +  [#6528](https://github.com/rust-lang/rust-clippy/pull/6528)
 +* [`vec_init_then_push`]
 +  [#6538](https://github.com/rust-lang/rust-clippy/pull/6538)
 +* [`ptr_as_ptr`] [#6542](https://github.com/rust-lang/rust-clippy/pull/6542)
 +* [`collapsible_else_if`] (split out from `collapsible_if`)
 +  [#6544](https://github.com/rust-lang/rust-clippy/pull/6544)
 +* [`inspect_for_each`] [#6577](https://github.com/rust-lang/rust-clippy/pull/6577)
 +* [`manual_filter_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* [`exhaustive_enums`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +* [`exhaustive_structs`]
 +  [#6617](https://github.com/rust-lang/rust-clippy/pull/6617)
 +
 +### Moves and Deprecations
 +
 +* Replace [`find_map`] with [`manual_find_map`]
 +  [#6591](https://github.com/rust-lang/rust-clippy/pull/6591)
 +* `unknown_clippy_lints` Now integrated in the `unknown_lints` rustc lint
 +  [#6653](https://github.com/rust-lang/rust-clippy/pull/6653)
 +
 +### Enhancements
 +
 +* [`ptr_arg`] Now also suggests to use `&Path` instead of `&PathBuf`
 +  [#6506](https://github.com/rust-lang/rust-clippy/pull/6506)
 +* [`cast_ptr_alignment`] Also lint when the `pointer::cast` method is used
 +  [#6557](https://github.com/rust-lang/rust-clippy/pull/6557)
 +* [`collapsible_match`] Now also deals with `&` and `*` operators in the `match`
 +  scrutinee [#6619](https://github.com/rust-lang/rust-clippy/pull/6619)
 +
 +### False Positive Fixes
 +
 +* [`similar_names`] Ignore underscore prefixed names
 +  [#6403](https://github.com/rust-lang/rust-clippy/pull/6403)
 +* [`print_literal`] and [`write_literal`] No longer lint numeric literals
 +  [#6408](https://github.com/rust-lang/rust-clippy/pull/6408)
 +* [`large_enum_variant`] No longer lints in external macros
 +  [#6485](https://github.com/rust-lang/rust-clippy/pull/6485)
 +* [`empty_enum`] Only lint if `never_type` feature is enabled
 +  [#6513](https://github.com/rust-lang/rust-clippy/pull/6513)
 +* [`field_reassign_with_default`] No longer lints in macros
 +  [#6553](https://github.com/rust-lang/rust-clippy/pull/6553)
 +* [`size_of_in_element_count`] No longer lints when dividing by element size
 +  [#6578](https://github.com/rust-lang/rust-clippy/pull/6578)
 +* [`needless_return`] No longer lints in macros
 +  [#6586](https://github.com/rust-lang/rust-clippy/pull/6586)
 +* [`match_overlapping_arm`] No longer lint when first arm is completely included
 +  in second arm [#6603](https://github.com/rust-lang/rust-clippy/pull/6603)
 +* [`doc_markdown`] Add `WebGL` to the default configuration as an allowed
 +  identifier [#6605](https://github.com/rust-lang/rust-clippy/pull/6605)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`field_reassign_with_default`] Don't expand macro in lint suggestion
 +  [#6531](https://github.com/rust-lang/rust-clippy/pull/6531)
 +* [`match_like_matches_macro`] Strip references in suggestion
 +  [#6532](https://github.com/rust-lang/rust-clippy/pull/6532)
 +* [`single_match`] Suggest `if` over `if let` when possible
 +  [#6574](https://github.com/rust-lang/rust-clippy/pull/6574)
 +* `ref_in_deref` Use parentheses correctly in suggestion
 +  [#6609](https://github.com/rust-lang/rust-clippy/pull/6609)
 +* [`stable_sort_primitive`] Clarify error message
 +  [#6611](https://github.com/rust-lang/rust-clippy/pull/6611)
 +
 +### ICE Fixes
 +
 +* [`zero_sized_map_values`]
 +  [#6582](https://github.com/rust-lang/rust-clippy/pull/6582)
 +
 +### Documentation Improvements
 +
 +* Improve search performance on the Clippy website and make it possible to
 +  directly search for lints on the GitHub issue tracker
 +  [#6483](https://github.com/rust-lang/rust-clippy/pull/6483)
 +* Clean up `README.md` by removing outdated paragraph
 +  [#6488](https://github.com/rust-lang/rust-clippy/pull/6488)
 +* [`await_holding_refcell_ref`] and [`await_holding_lock`]
 +  [#6585](https://github.com/rust-lang/rust-clippy/pull/6585)
 +* [`as_conversions`] [#6608](https://github.com/rust-lang/rust-clippy/pull/6608)
 +
 +### Others
 +
 +* Clippy now has a [Roadmap] for 2021. If you like to get involved in a bigger
 +  project, take a look at the [Roadmap project page]. All issues listed there
 +  are actively mentored
 +  [#6462](https://github.com/rust-lang/rust-clippy/pull/6462)
 +* The Clippy version number now corresponds to the Rust version number
 +  [#6526](https://github.com/rust-lang/rust-clippy/pull/6526)
 +* Fix oversight which caused Clippy to lint deps in some environments, where
 +  `CLIPPY_TESTS=true` was set somewhere
 +  [#6575](https://github.com/rust-lang/rust-clippy/pull/6575)
 +* Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
 +  [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 +
 +[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
 +[Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 +
 +## Rust 1.50
 +
 +Released 2021-02-11
 +
 +[b20d4c1...4bd77a1](https://github.com/rust-lang/rust-clippy/compare/b20d4c1...4bd77a1)
 +
 +### New Lints
 +
 +* [`suspicious_operation_groupings`] [#6086](https://github.com/rust-lang/rust-clippy/pull/6086)
 +* [`size_of_in_element_count`] [#6394](https://github.com/rust-lang/rust-clippy/pull/6394)
 +* [`unnecessary_wraps`] [#6070](https://github.com/rust-lang/rust-clippy/pull/6070)
 +* [`let_underscore_drop`] [#6305](https://github.com/rust-lang/rust-clippy/pull/6305)
 +* [`collapsible_match`] [#6402](https://github.com/rust-lang/rust-clippy/pull/6402)
 +* [`redundant_else`] [#6330](https://github.com/rust-lang/rust-clippy/pull/6330)
 +* [`zero_sized_map_values`] [#6218](https://github.com/rust-lang/rust-clippy/pull/6218)
 +* [`print_stderr`] [#6367](https://github.com/rust-lang/rust-clippy/pull/6367)
 +* [`string_from_utf8_as_bytes`] [#6134](https://github.com/rust-lang/rust-clippy/pull/6134)
 +
 +### Moves and Deprecations
 +
 +* Previously deprecated [`str_to_string`] and [`string_to_string`] have been un-deprecated
 +  as `restriction` lints [#6333](https://github.com/rust-lang/rust-clippy/pull/6333)
 +* Deprecate `panic_params` lint. This is now available in rustc as `non_fmt_panics`
 +  [#6351](https://github.com/rust-lang/rust-clippy/pull/6351)
 +* Move [`map_err_ignore`] to `restriction`
 +  [#6416](https://github.com/rust-lang/rust-clippy/pull/6416)
 +* Move [`await_holding_refcell_ref`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +* Move [`await_holding_lock`] to `pedantic`
 +  [#6354](https://github.com/rust-lang/rust-clippy/pull/6354)
 +
 +### Enhancements
 +
 +* Add the `unreadable-literal-lint-fractions` configuration to disable
 +  the `unreadable_literal` lint for fractions
 +  [#6421](https://github.com/rust-lang/rust-clippy/pull/6421)
 +* [`clone_on_copy`]: Now shows the type in the lint message
 +  [#6443](https://github.com/rust-lang/rust-clippy/pull/6443)
 +* [`redundant_pattern_matching`]: Now also lints on `std::task::Poll`
 +  [#6339](https://github.com/rust-lang/rust-clippy/pull/6339)
 +* [`redundant_pattern_matching`]: Additionally also lints on `std::net::IpAddr`
 +  [#6377](https://github.com/rust-lang/rust-clippy/pull/6377)
 +* [`search_is_some`]: Now suggests `contains` instead of `find(foo).is_some()`
 +  [#6119](https://github.com/rust-lang/rust-clippy/pull/6119)
 +* [`clone_double_ref`]: Now prints the reference type in the lint message
 +  [#6442](https://github.com/rust-lang/rust-clippy/pull/6442)
 +* [`modulo_one`]: Now also lints on -1.
 +  [#6360](https://github.com/rust-lang/rust-clippy/pull/6360)
 +* [`empty_loop`]: Now lints no_std crates, too
 +  [#6205](https://github.com/rust-lang/rust-clippy/pull/6205)
 +* [`or_fun_call`]: Now also lints when indexing `HashMap` or `BTreeMap`
 +  [#6267](https://github.com/rust-lang/rust-clippy/pull/6267)
 +* [`wrong_self_convention`]: Now also lints in trait definitions
 +  [#6316](https://github.com/rust-lang/rust-clippy/pull/6316)
 +* [`needless_borrow`]: Print the type in the lint message
 +  [#6449](https://github.com/rust-lang/rust-clippy/pull/6449)
 +
 +[msrv_readme]: https://github.com/rust-lang/rust-clippy#specifying-the-minimum-supported-rust-version
 +
 +### False Positive Fixes
 +
 +* [`manual_range_contains`]: No longer lints in `const fn`
 +  [#6382](https://github.com/rust-lang/rust-clippy/pull/6382)
 +* [`unnecessary_lazy_evaluations`]: No longer lints if closure argument is used
 +  [#6370](https://github.com/rust-lang/rust-clippy/pull/6370)
 +* [`match_single_binding`]: Now ignores cases with `#[cfg()]` macros
 +  [#6435](https://github.com/rust-lang/rust-clippy/pull/6435)
 +* [`match_like_matches_macro`]: No longer lints on arms with attributes
 +  [#6290](https://github.com/rust-lang/rust-clippy/pull/6290)
 +* [`map_clone`]: No longer lints with deref and clone
 +  [#6269](https://github.com/rust-lang/rust-clippy/pull/6269)
 +* [`map_clone`]: No longer lints in the case of &mut
 +  [#6301](https://github.com/rust-lang/rust-clippy/pull/6301)
 +* [`needless_update`]: Now ignores `non_exhaustive` structs
 +  [#6464](https://github.com/rust-lang/rust-clippy/pull/6464)
 +* [`needless_collect`]: No longer lints when a collect is needed multiple times
 +  [#6313](https://github.com/rust-lang/rust-clippy/pull/6313)
 +* [`unnecessary_cast`] No longer lints cfg-dependent types
 +  [#6369](https://github.com/rust-lang/rust-clippy/pull/6369)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]:
 +  Both now ignore enums with frozen variants
 +  [#6110](https://github.com/rust-lang/rust-clippy/pull/6110)
 +* [`field_reassign_with_default`] No longer lint for private fields
 +  [#6537](https://github.com/rust-lang/rust-clippy/pull/6537)
 +
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`vec_box`]: Provide correct type scope suggestion
 +  [#6271](https://github.com/rust-lang/rust-clippy/pull/6271)
 +* [`manual_range_contains`]: Give correct suggestion when using floats
 +  [#6320](https://github.com/rust-lang/rust-clippy/pull/6320)
 +* [`unnecessary_lazy_evaluations`]: Don't always mark suggestion as MachineApplicable
 +  [#6272](https://github.com/rust-lang/rust-clippy/pull/6272)
 +* [`manual_async_fn`]: Improve suggestion formatting
 +  [#6294](https://github.com/rust-lang/rust-clippy/pull/6294)
 +* [`unnecessary_cast`]: Fix incorrectly formatted float literal suggestion
 +  [#6362](https://github.com/rust-lang/rust-clippy/pull/6362)
 +
 +### ICE Fixes
 +
 +* Fix a crash in [`from_iter_instead_of_collect`]
 +  [#6304](https://github.com/rust-lang/rust-clippy/pull/6304)
 +* Fix a silent crash when parsing doc comments in [`needless_doctest_main`]
 +  [#6458](https://github.com/rust-lang/rust-clippy/pull/6458)
 +
 +### Documentation Improvements
 +
 +* The lint website search has been improved ([#6477](https://github.com/rust-lang/rust-clippy/pull/6477)):
 +  * Searching for lints with dashes and spaces is possible now. For example
 +    `missing-errors-doc` and `missing errors doc` are now valid aliases for lint names
 +  * Improved fuzzy search in lint descriptions
 +* Various README improvements
 +  [#6287](https://github.com/rust-lang/rust-clippy/pull/6287)
 +* Add known problems to [`comparison_chain`] documentation
 +  [#6390](https://github.com/rust-lang/rust-clippy/pull/6390)
 +* Fix example used in [`cargo_common_metadata`]
 +  [#6293](https://github.com/rust-lang/rust-clippy/pull/6293)
 +* Improve [`map_clone`] documentation
 +  [#6340](https://github.com/rust-lang/rust-clippy/pull/6340)
 +
 +### Others
 +
 +* You can now tell Clippy about the MSRV your project supports. Please refer to
 +  the specific README section to learn more about MSRV support [here][msrv_readme]
 +  [#6201](https://github.com/rust-lang/rust-clippy/pull/6201)
 +* Add `--no-deps` option to avoid running on path dependencies in workspaces
 +  [#6188](https://github.com/rust-lang/rust-clippy/pull/6188)
 +
 +## Rust 1.49
 +
 +Released 2020-12-31
 +
 +[e636b88...b20d4c1](https://github.com/rust-lang/rust-clippy/compare/e636b88...b20d4c1)
 +
 +### New Lints
 +
 +* [`field_reassign_with_default`] [#5911](https://github.com/rust-lang/rust-clippy/pull/5911)
 +* [`await_holding_refcell_ref`] [#6029](https://github.com/rust-lang/rust-clippy/pull/6029)
 +* [`disallowed_methods`] [#6081](https://github.com/rust-lang/rust-clippy/pull/6081)
 +* [`inline_asm_x86_att_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`inline_asm_x86_intel_syntax`] [#6092](https://github.com/rust-lang/rust-clippy/pull/6092)
 +* [`from_iter_instead_of_collect`] [#6101](https://github.com/rust-lang/rust-clippy/pull/6101)
 +* [`mut_mutex_lock`] [#6103](https://github.com/rust-lang/rust-clippy/pull/6103)
 +* [`single_element_loop`] [#6109](https://github.com/rust-lang/rust-clippy/pull/6109)
 +* [`manual_unwrap_or`] [#6123](https://github.com/rust-lang/rust-clippy/pull/6123)
 +* [`large_types_passed_by_value`] [#6135](https://github.com/rust-lang/rust-clippy/pull/6135)
 +* [`result_unit_err`] [#6157](https://github.com/rust-lang/rust-clippy/pull/6157)
 +* [`ref_option_ref`] [#6165](https://github.com/rust-lang/rust-clippy/pull/6165)
 +* [`manual_range_contains`] [#6177](https://github.com/rust-lang/rust-clippy/pull/6177)
 +* [`unusual_byte_groupings`] [#6183](https://github.com/rust-lang/rust-clippy/pull/6183)
 +* [`comparison_to_empty`] [#6226](https://github.com/rust-lang/rust-clippy/pull/6226)
 +* [`map_collect_result_unit`] [#6227](https://github.com/rust-lang/rust-clippy/pull/6227)
 +* [`manual_ok_or`] [#6233](https://github.com/rust-lang/rust-clippy/pull/6233)
 +
 +### Moves and Deprecations
 +
 +* Rename `single_char_push_str` to [`single_char_add_str`]
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* Rename `zero_width_space` to [`invisible_characters`]
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* Deprecate `drop_bounds` (uplifted)
 +  [#6111](https://github.com/rust-lang/rust-clippy/pull/6111)
 +* Move [`string_lit_as_bytes`] to `nursery`
 +  [#6117](https://github.com/rust-lang/rust-clippy/pull/6117)
 +* Move [`rc_buffer`] to `restriction`
 +  [#6128](https://github.com/rust-lang/rust-clippy/pull/6128)
 +
 +### Enhancements
 +
 +* [`manual_memcpy`]: Also lint when there are loop counters (and produce a
 +  reliable suggestion)
 +  [#5727](https://github.com/rust-lang/rust-clippy/pull/5727)
 +* [`single_char_add_str`]: Also lint on `String::insert_str`
 +  [#6037](https://github.com/rust-lang/rust-clippy/pull/6037)
 +* [`invisible_characters`]: Also lint the characters `\u{AD}` and `\u{2060}`
 +  [#6105](https://github.com/rust-lang/rust-clippy/pull/6105)
 +* [`eq_op`]: Also lint on the `assert_*!` macro family
 +  [#6167](https://github.com/rust-lang/rust-clippy/pull/6167)
 +* [`items_after_statements`]: Also lint in local macro expansions
 +  [#6176](https://github.com/rust-lang/rust-clippy/pull/6176)
 +* [`unnecessary_cast`]: Also lint casts on integer and float literals
 +  [#6187](https://github.com/rust-lang/rust-clippy/pull/6187)
 +* [`manual_unwrap_or`]: Also lint `Result::unwrap_or`
 +  [#6190](https://github.com/rust-lang/rust-clippy/pull/6190)
 +* [`match_like_matches_macro`]: Also lint when `match` has more than two arms
 +  [#6216](https://github.com/rust-lang/rust-clippy/pull/6216)
 +* [`integer_arithmetic`]: Better handle `/` an `%` operators
 +  [#6229](https://github.com/rust-lang/rust-clippy/pull/6229)
 +
 +### False Positive Fixes
 +
 +* [`needless_lifetimes`]: Bail out if the function has a `where` clause with the
 +  lifetime [#5978](https://github.com/rust-lang/rust-clippy/pull/5978)
 +* [`explicit_counter_loop`]: No longer lints, when loop counter is used after it
 +  is incremented [#6076](https://github.com/rust-lang/rust-clippy/pull/6076)
 +* [`or_fun_call`]: Revert changes addressing the handling of `const fn`
 +  [#6077](https://github.com/rust-lang/rust-clippy/pull/6077)
 +* [`needless_range_loop`]: No longer lints, when the iterable is used in the
 +  range [#6102](https://github.com/rust-lang/rust-clippy/pull/6102)
 +* [`inconsistent_digit_grouping`]: Fix bug when using floating point exponent
 +  [#6104](https://github.com/rust-lang/rust-clippy/pull/6104)
 +* [`mistyped_literal_suffixes`]: No longer lints on the fractional part of a
 +  float (e.g. `713.32_64`)
 +  [#6114](https://github.com/rust-lang/rust-clippy/pull/6114)
 +* [`invalid_regex`]: No longer lint on unicode characters within `bytes::Regex`
 +  [#6132](https://github.com/rust-lang/rust-clippy/pull/6132)
 +* [`boxed_local`]: No longer lints on `extern fn` arguments
 +  [#6133](https://github.com/rust-lang/rust-clippy/pull/6133)
 +* [`needless_lifetimes`]: Fix regression, where lifetime is used in `where`
 +  clause [#6198](https://github.com/rust-lang/rust-clippy/pull/6198)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`unnecessary_sort_by`]: Avoid dereferencing the suggested closure parameter
 +  [#6078](https://github.com/rust-lang/rust-clippy/pull/6078)
 +* [`needless_arbitrary_self_type`]: Correctly handle expanded code
 +  [#6093](https://github.com/rust-lang/rust-clippy/pull/6093)
 +* [`useless_format`]: Preserve raw strings in suggestion
 +  [#6151](https://github.com/rust-lang/rust-clippy/pull/6151)
 +* [`empty_loop`]: Suggest alternatives
 +  [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`borrowed_box`]: Correctly add parentheses in suggestion
 +  [#6200](https://github.com/rust-lang/rust-clippy/pull/6200)
 +* [`unused_unit`]: Improve suggestion formatting
 +  [#6247](https://github.com/rust-lang/rust-clippy/pull/6247)
 +
 +### Documentation Improvements
 +
 +* Some doc improvements:
 +    * [`rc_buffer`] [#6090](https://github.com/rust-lang/rust-clippy/pull/6090)
 +    * [`empty_loop`] [#6162](https://github.com/rust-lang/rust-clippy/pull/6162)
 +* [`doc_markdown`]: Document problematic link text style
 +  [#6107](https://github.com/rust-lang/rust-clippy/pull/6107)
 +
 +## Rust 1.48
 +
 +Released 2020-11-19
 +
 +[09bd400...e636b88](https://github.com/rust-lang/rust-clippy/compare/09bd400...e636b88)
 +
 +### New lints
 +
 +* [`self_assignment`] [#5894](https://github.com/rust-lang/rust-clippy/pull/5894)
 +* [`unnecessary_lazy_evaluations`] [#5720](https://github.com/rust-lang/rust-clippy/pull/5720)
 +* [`manual_strip`] [#6038](https://github.com/rust-lang/rust-clippy/pull/6038)
 +* [`map_err_ignore`] [#5998](https://github.com/rust-lang/rust-clippy/pull/5998)
 +* [`rc_buffer`] [#6044](https://github.com/rust-lang/rust-clippy/pull/6044)
 +* `to_string_in_display` [#5831](https://github.com/rust-lang/rust-clippy/pull/5831)
 +* `single_char_push_str` [#5881](https://github.com/rust-lang/rust-clippy/pull/5881)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`verbose_bit_mask`] to pedantic
 +  [#6036](https://github.com/rust-lang/rust-clippy/pull/6036)
 +
 +### Enhancements
 +
 +* Extend [`precedence`] to handle chains of methods combined with unary negation
 +  [#5928](https://github.com/rust-lang/rust-clippy/pull/5928)
 +* [`useless_vec`]: add a configuration value for the maximum allowed size on the stack
 +  [#5907](https://github.com/rust-lang/rust-clippy/pull/5907)
 +* [`suspicious_arithmetic_impl`]: extend to implementations of `BitAnd`, `BitOr`, `BitXor`, `Rem`, `Shl`, and `Shr`
 +  [#5884](https://github.com/rust-lang/rust-clippy/pull/5884)
 +* `invalid_atomic_ordering`: detect misuse of `compare_exchange`, `compare_exchange_weak`, and `fetch_update`
 +  [#6025](https://github.com/rust-lang/rust-clippy/pull/6025)
 +* Avoid [`redundant_pattern_matching`] triggering in macros
 +  [#6069](https://github.com/rust-lang/rust-clippy/pull/6069)
 +* [`option_if_let_else`]: distinguish pure from impure `else` expressions
 +  [#5937](https://github.com/rust-lang/rust-clippy/pull/5937)
 +* [`needless_doctest_main`]: parse doctests instead of using textual search
 +  [#5912](https://github.com/rust-lang/rust-clippy/pull/5912)
 +* [`wildcard_imports`]: allow `prelude` to appear in any segment of an import
 +  [#5929](https://github.com/rust-lang/rust-clippy/pull/5929)
 +* Re-enable [`len_zero`] for ranges now that `range_is_empty` is stable
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +* [`option_as_ref_deref`]: catch fully-qualified calls to `Deref::deref` and `DerefMut::deref_mut`
 +  [#5933](https://github.com/rust-lang/rust-clippy/pull/5933)
 +
 +### False Positive Fixes
 +
 +* [`useless_attribute`]: permit allowing [`wildcard_imports`] and [`enum_glob_use`]
 +  [#5994](https://github.com/rust-lang/rust-clippy/pull/5994)
 +* [`transmute_ptr_to_ptr`]: avoid suggesting dereferencing raw pointers in const contexts
 +  [#5999](https://github.com/rust-lang/rust-clippy/pull/5999)
 +* [`redundant_closure_call`]: take into account usages of the closure in nested functions and closures
 +  [#5920](https://github.com/rust-lang/rust-clippy/pull/5920)
 +* Fix false positive in [`borrow_interior_mutable_const`] when referencing a field behind a pointer
 +  [#5949](https://github.com/rust-lang/rust-clippy/pull/5949)
 +* [`doc_markdown`]: allow using "GraphQL" without backticks
 +  [#5996](https://github.com/rust-lang/rust-clippy/pull/5996)
 +* `to_string_in_display`: avoid linting when calling `to_string()` on anything that is not `self`
 +  [#5971](https://github.com/rust-lang/rust-clippy/pull/5971)
 +* [`indexing_slicing`] and [`out_of_bounds_indexing`] treat references to arrays as arrays
 +  [#6034](https://github.com/rust-lang/rust-clippy/pull/6034)
 +* [`should_implement_trait`]: ignore methods with lifetime parameters
 +  [#5725](https://github.com/rust-lang/rust-clippy/pull/5725)
 +* [`needless_return`]: avoid linting if a temporary borrows a local variable
 +  [#5903](https://github.com/rust-lang/rust-clippy/pull/5903)
 +* Restrict [`unnecessary_sort_by`] to non-reference, Copy types
 +  [#6006](https://github.com/rust-lang/rust-clippy/pull/6006)
 +* Avoid suggesting `from_bits`/`to_bits` in const contexts in [`transmute_int_to_float`]
 +  [#5919](https://github.com/rust-lang/rust-clippy/pull/5919)
 +* [`declare_interior_mutable_const`] and [`borrow_interior_mutable_const`]: improve detection of interior mutable types
 +  [#6046](https://github.com/rust-lang/rust-clippy/pull/6046)
 +
 +### Suggestion Fixes/Improvements
 +
 +* [`let_and_return`]: add a cast to the suggestion when the return expression has adjustments
 +  [#5946](https://github.com/rust-lang/rust-clippy/pull/5946)
 +* [`useless_conversion`]: show the type in the error message
 +  [#6035](https://github.com/rust-lang/rust-clippy/pull/6035)
 +* [`unnecessary_mut_passed`]: discriminate between functions and methods in the error message
 +  [#5892](https://github.com/rust-lang/rust-clippy/pull/5892)
 +* [`float_cmp`] and [`float_cmp_const`]: change wording to make margin of error less ambiguous
 +  [#6043](https://github.com/rust-lang/rust-clippy/pull/6043)
 +* [`default_trait_access`]: do not use unnecessary type parameters in the suggestion
 +  [#5993](https://github.com/rust-lang/rust-clippy/pull/5993)
 +* [`collapsible_if`]: don't use expanded code in the suggestion
 +  [#5992](https://github.com/rust-lang/rust-clippy/pull/5992)
 +* Do not suggest empty format strings in [`print_with_newline`] and [`write_with_newline`]
 +  [#6042](https://github.com/rust-lang/rust-clippy/pull/6042)
 +* [`unit_arg`]: improve the readability of the suggestion
 +  [#5931](https://github.com/rust-lang/rust-clippy/pull/5931)
 +* [`stable_sort_primitive`]: print the type that is being sorted in the lint message
 +  [#5935](https://github.com/rust-lang/rust-clippy/pull/5935)
 +* Show line count and max lines in [`too_many_lines`] lint message
 +  [#6009](https://github.com/rust-lang/rust-clippy/pull/6009)
 +* Keep parentheses in the suggestion of [`useless_conversion`] where applicable
 +  [#5900](https://github.com/rust-lang/rust-clippy/pull/5900)
 +* [`option_map_unit_fn`] and [`result_map_unit_fn`]: print the unit type `()` explicitly
 +  [#6024](https://github.com/rust-lang/rust-clippy/pull/6024)
 +* [`redundant_allocation`]: suggest replacing `Rc<Box<T>>` with `Rc<T>`
 +  [#5899](https://github.com/rust-lang/rust-clippy/pull/5899)
 +* Make lint messages adhere to rustc dev guide conventions
 +  [#5893](https://github.com/rust-lang/rust-clippy/pull/5893)
 +
 +### ICE Fixes
 +
 +* Fix ICE in [`repeat_once`]
 +  [#5948](https://github.com/rust-lang/rust-clippy/pull/5948)
 +
 +### Documentation Improvements
 +
 +* [`mutable_key_type`]: explain potential for false positives when the interior mutable type is not accessed in the `Hash` implementation
 +  [#6019](https://github.com/rust-lang/rust-clippy/pull/6019)
 +* [`unnecessary_mut_passed`]: fix typo
 +  [#5913](https://github.com/rust-lang/rust-clippy/pull/5913)
 +* Add example of false positive to [`ptr_arg`] docs.
 +  [#5885](https://github.com/rust-lang/rust-clippy/pull/5885)
 +* [`box_vec`](https://rust-lang.github.io/rust-clippy/master/index.html#box_collection), [`vec_box`] and [`borrowed_box`]: add link to the documentation of `Box`
 +  [#6023](https://github.com/rust-lang/rust-clippy/pull/6023)
 +
 +## Rust 1.47
 +
 +Released 2020-10-08
 +
 +[c2c07fa...09bd400](https://github.com/rust-lang/rust-clippy/compare/c2c07fa...09bd400)
 +
 +### New lints
 +
 +* [`derive_ord_xor_partial_ord`] [#5848](https://github.com/rust-lang/rust-clippy/pull/5848)
 +* [`trait_duplication_in_bounds`] [#5852](https://github.com/rust-lang/rust-clippy/pull/5852)
 +* [`map_identity`] [#5694](https://github.com/rust-lang/rust-clippy/pull/5694)
 +* [`unit_return_expecting_ord`] [#5737](https://github.com/rust-lang/rust-clippy/pull/5737)
 +* [`pattern_type_mismatch`] [#4841](https://github.com/rust-lang/rust-clippy/pull/4841)
 +* [`repeat_once`] [#5773](https://github.com/rust-lang/rust-clippy/pull/5773)
 +* [`same_item_push`] [#5825](https://github.com/rust-lang/rust-clippy/pull/5825)
 +* [`needless_arbitrary_self_type`] [#5869](https://github.com/rust-lang/rust-clippy/pull/5869)
 +* [`match_like_matches_macro`] [#5769](https://github.com/rust-lang/rust-clippy/pull/5769)
 +* [`stable_sort_primitive`] [#5809](https://github.com/rust-lang/rust-clippy/pull/5809)
 +* [`blanket_clippy_restriction_lints`] [#5750](https://github.com/rust-lang/rust-clippy/pull/5750)
 +* [`option_if_let_else`] [#5301](https://github.com/rust-lang/rust-clippy/pull/5301)
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`regex_macro`] lint
 +  [#5760](https://github.com/rust-lang/rust-clippy/pull/5760)
 +* Move [`range_minus_one`] to `pedantic`
 +  [#5752](https://github.com/rust-lang/rust-clippy/pull/5752)
 +
 +### Enhancements
 +
 +* Improve [`needless_collect`] by catching `collect` calls followed by `iter` or `into_iter` calls
 +  [#5837](https://github.com/rust-lang/rust-clippy/pull/5837)
 +* [`panic`], [`todo`], [`unimplemented`] and [`unreachable`] now detect calls with formatting
 +  [#5811](https://github.com/rust-lang/rust-clippy/pull/5811)
 +* Detect more cases of [`suboptimal_flops`] and [`imprecise_flops`]
 +  [#5443](https://github.com/rust-lang/rust-clippy/pull/5443)
 +* Handle asymmetrical implementations of `PartialEq` in [`cmp_owned`]
 +  [#5701](https://github.com/rust-lang/rust-clippy/pull/5701)
 +* Make it possible to allow [`unsafe_derive_deserialize`]
 +  [#5870](https://github.com/rust-lang/rust-clippy/pull/5870)
 +* Catch `ord.min(a).max(b)` where a < b in [`min_max`]
 +  [#5871](https://github.com/rust-lang/rust-clippy/pull/5871)
 +* Make [`clone_on_copy`] suggestion machine applicable
 +  [#5745](https://github.com/rust-lang/rust-clippy/pull/5745)
 +* Enable [`len_zero`] on ranges now that `is_empty` is stable on them
 +  [#5961](https://github.com/rust-lang/rust-clippy/pull/5961)
 +
 +### False Positive Fixes
 +
 +* Avoid triggering [`or_fun_call`] with const fns that take no arguments
 +  [#5889](https://github.com/rust-lang/rust-clippy/pull/5889)
 +* Fix [`redundant_closure_call`] false positive for closures that have multiple calls
 +  [#5800](https://github.com/rust-lang/rust-clippy/pull/5800)
 +* Don't lint cases involving `ManuallyDrop` in [`redundant_clone`]
 +  [#5824](https://github.com/rust-lang/rust-clippy/pull/5824)
 +* Treat a single expression the same as a single statement in the 2nd arm of a match in [`single_match_else`]
 +  [#5771](https://github.com/rust-lang/rust-clippy/pull/5771)
 +* Don't trigger [`unnested_or_patterns`] if the feature `or_patterns` is not enabled
 +  [#5758](https://github.com/rust-lang/rust-clippy/pull/5758)
 +* Avoid linting if key borrows in [`unnecessary_sort_by`]
 +  [#5756](https://github.com/rust-lang/rust-clippy/pull/5756)
 +* Consider `Try` impl for `Poll` when generating suggestions in [`try_err`]
 +  [#5857](https://github.com/rust-lang/rust-clippy/pull/5857)
 +* Take input lifetimes into account in `manual_async_fn`
 +  [#5859](https://github.com/rust-lang/rust-clippy/pull/5859)
 +* Fix multiple false positives in [`type_repetition_in_bounds`] and add a configuration option
 +  [#5761](https://github.com/rust-lang/rust-clippy/pull/5761)
 +* Limit the [`suspicious_arithmetic_impl`] lint to one binary operation
 +  [#5820](https://github.com/rust-lang/rust-clippy/pull/5820)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Improve readability of [`shadow_unrelated`] suggestion by truncating the RHS snippet
 +  [#5788](https://github.com/rust-lang/rust-clippy/pull/5788)
 +* Suggest `filter_map` instead of `flat_map` when mapping to `Option` in [`map_flatten`]
 +  [#5846](https://github.com/rust-lang/rust-clippy/pull/5846)
 +* Ensure suggestion is shown correctly for long method call chains in [`iter_nth_zero`]
 +  [#5793](https://github.com/rust-lang/rust-clippy/pull/5793)
 +* Drop borrow operator in suggestions of [`redundant_pattern_matching`]
 +  [#5815](https://github.com/rust-lang/rust-clippy/pull/5815)
 +* Add suggestion for [`iter_skip_next`]
 +  [#5843](https://github.com/rust-lang/rust-clippy/pull/5843)
 +* Improve [`collapsible_if`] fix suggestion
 +  [#5732](https://github.com/rust-lang/rust-clippy/pull/5732)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused by [`needless_collect`]
 +  [#5877](https://github.com/rust-lang/rust-clippy/pull/5877)
 +* Fix ICE caused by [`unnested_or_patterns`]
 +  [#5784](https://github.com/rust-lang/rust-clippy/pull/5784)
 +
 +### Documentation Improvements
 +
 +* Fix grammar of [`await_holding_lock`] documentation
 +  [#5748](https://github.com/rust-lang/rust-clippy/pull/5748)
 +
 +### Others
 +
 +* Make lints adhere to the rustc dev guide
 +  [#5888](https://github.com/rust-lang/rust-clippy/pull/5888)
 +
 +## Rust 1.46
 +
 +Released 2020-08-27
 +
 +[7ea7cd1...c2c07fa](https://github.com/rust-lang/rust-clippy/compare/7ea7cd1...c2c07fa)
 +
 +### New lints
 +
 +* [`unnested_or_patterns`] [#5378](https://github.com/rust-lang/rust-clippy/pull/5378)
 +* [`iter_next_slice`] [#5597](https://github.com/rust-lang/rust-clippy/pull/5597)
 +* [`unnecessary_sort_by`] [#5623](https://github.com/rust-lang/rust-clippy/pull/5623)
 +* [`vec_resize_to_zero`] [#5637](https://github.com/rust-lang/rust-clippy/pull/5637)
 +
 +### Moves and Deprecations
 +
 +* Move [`cast_ptr_alignment`] to pedantic [#5667](https://github.com/rust-lang/rust-clippy/pull/5667)
 +
 +### Enhancements
 +
 +* Improve [`mem_replace_with_uninit`] lint [#5695](https://github.com/rust-lang/rust-clippy/pull/5695)
 +
 +### False Positive Fixes
 +
 +* [`len_zero`]: Avoid linting ranges when the `range_is_empty` feature is not enabled
 +  [#5656](https://github.com/rust-lang/rust-clippy/pull/5656)
 +* [`let_and_return`]: Don't lint if a temporary borrow is involved
 +  [#5680](https://github.com/rust-lang/rust-clippy/pull/5680)
 +* [`reversed_empty_ranges`]: Avoid linting `N..N` in for loop arguments in
 +  [#5692](https://github.com/rust-lang/rust-clippy/pull/5692)
 +* [`if_same_then_else`]: Don't assume multiplication is always commutative
 +  [#5702](https://github.com/rust-lang/rust-clippy/pull/5702)
 +* [`disallowed_names`]: Remove `bar` from the default configuration
 +  [#5712](https://github.com/rust-lang/rust-clippy/pull/5712)
 +* [`redundant_pattern_matching`]: Avoid suggesting non-`const fn` calls in const contexts
 +  [#5724](https://github.com/rust-lang/rust-clippy/pull/5724)
 +
 +### Suggestion Fixes/Improvements
 +
 +* Fix suggestion of [`unit_arg`] lint, so that it suggest semantic equivalent code
 +  [#4455](https://github.com/rust-lang/rust-clippy/pull/4455)
 +* Add auto applicable suggestion to [`macro_use_imports`]
 +  [#5279](https://github.com/rust-lang/rust-clippy/pull/5279)
 +
 +### ICE Fixes
 +
 +* Fix ICE in the `consts` module of Clippy [#5709](https://github.com/rust-lang/rust-clippy/pull/5709)
 +
 +### Documentation Improvements
 +
 +* Improve code examples across multiple lints [#5664](https://github.com/rust-lang/rust-clippy/pull/5664)
 +
 +### Others
 +
 +* Introduce a `--rustc` flag to `clippy-driver`, which turns `clippy-driver`
 +  into `rustc` and passes all the given arguments to `rustc`. This is especially
 +  useful for tools that need the `rustc` version Clippy was compiled with,
 +  instead of the Clippy version. E.g. `clippy-driver --rustc --version` will
 +  print the output of `rustc --version`.
 +  [#5178](https://github.com/rust-lang/rust-clippy/pull/5178)
 +* New issue templates now make it easier to complain if Clippy is too annoying
 +  or not annoying enough! [#5735](https://github.com/rust-lang/rust-clippy/pull/5735)
 +
 +## Rust 1.45
 +
 +Released 2020-07-16
 +
 +[891e1a8...7ea7cd1](https://github.com/rust-lang/rust-clippy/compare/891e1a8...7ea7cd1)
 +
 +### New lints
 +
 +* [`match_wildcard_for_single_variants`] [#5582](https://github.com/rust-lang/rust-clippy/pull/5582)
 +* [`unsafe_derive_deserialize`] [#5493](https://github.com/rust-lang/rust-clippy/pull/5493)
 +* [`if_let_mutex`] [#5332](https://github.com/rust-lang/rust-clippy/pull/5332)
 +* [`mismatched_target_os`] [#5506](https://github.com/rust-lang/rust-clippy/pull/5506)
 +* [`await_holding_lock`] [#5439](https://github.com/rust-lang/rust-clippy/pull/5439)
 +* [`match_on_vec_items`] [#5522](https://github.com/rust-lang/rust-clippy/pull/5522)
 +* [`manual_async_fn`] [#5576](https://github.com/rust-lang/rust-clippy/pull/5576)
 +* [`reversed_empty_ranges`] [#5583](https://github.com/rust-lang/rust-clippy/pull/5583)
 +* [`manual_non_exhaustive`] [#5550](https://github.com/rust-lang/rust-clippy/pull/5550)
 +
 +### Moves and Deprecations
 +
 +* Downgrade [`match_bool`] to pedantic [#5408](https://github.com/rust-lang/rust-clippy/pull/5408)
 +* Downgrade [`match_wild_err_arm`] to pedantic and update help messages. [#5622](https://github.com/rust-lang/rust-clippy/pull/5622)
 +* Downgrade [`useless_let_if_seq`] to nursery. [#5599](https://github.com/rust-lang/rust-clippy/pull/5599)
 +* Generalize `option_and_then_some` and rename to [`bind_instead_of_map`]. [#5529](https://github.com/rust-lang/rust-clippy/pull/5529)
 +* Rename `identity_conversion` to [`useless_conversion`]. [#5568](https://github.com/rust-lang/rust-clippy/pull/5568)
 +* Merge `block_in_if_condition_expr` and `block_in_if_condition_stmt` into [`blocks_in_if_conditions`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_map_unwrap_or`, `option_map_unwrap_or_else` and `result_map_unwrap_or_else` into [`map_unwrap_or`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_unwrap_used` and `result_unwrap_used` into [`unwrap_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `option_expect_used` and `result_expect_used` into [`expect_used`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +* Merge `for_loop_over_option` and `for_loop_over_result` into [`for_loops_over_fallibles`].
 +[#5563](https://github.com/rust-lang/rust-clippy/pull/5563)
 +
 +### Enhancements
 +
 +* Avoid running cargo lints when not enabled to improve performance. [#5505](https://github.com/rust-lang/rust-clippy/pull/5505)
 +* Extend [`useless_conversion`] with `TryFrom` and `TryInto`. [#5631](https://github.com/rust-lang/rust-clippy/pull/5631)
 +* Lint also in type parameters and where clauses in [`unused_unit`]. [#5592](https://github.com/rust-lang/rust-clippy/pull/5592)
 +* Do not suggest deriving `Default` in [`new_without_default`]. [#5616](https://github.com/rust-lang/rust-clippy/pull/5616)
 +
 +### False Positive Fixes
 +
 +* [`while_let_on_iterator`] [#5525](https://github.com/rust-lang/rust-clippy/pull/5525)
 +* [`empty_line_after_outer_attr`] [#5609](https://github.com/rust-lang/rust-clippy/pull/5609)
 +* [`unnecessary_unwrap`] [#5558](https://github.com/rust-lang/rust-clippy/pull/5558)
 +* [`comparison_chain`] [#5596](https://github.com/rust-lang/rust-clippy/pull/5596)
 +* Don't trigger [`used_underscore_binding`] in await desugaring. [#5535](https://github.com/rust-lang/rust-clippy/pull/5535)
 +* Don't trigger [`borrowed_box`] on mutable references. [#5491](https://github.com/rust-lang/rust-clippy/pull/5491)
 +* Allow `1 << 0` in [`identity_op`]. [#5602](https://github.com/rust-lang/rust-clippy/pull/5602)
 +* Allow `use super::*;` glob imports in [`wildcard_imports`]. [#5564](https://github.com/rust-lang/rust-clippy/pull/5564)
 +* Whitelist more words in [`doc_markdown`]. [#5611](https://github.com/rust-lang/rust-clippy/pull/5611)
 +* Skip dev and build deps in [`multiple_crate_versions`]. [#5636](https://github.com/rust-lang/rust-clippy/pull/5636)
 +* Honor `allow` attribute on arguments in [`ptr_arg`]. [#5647](https://github.com/rust-lang/rust-clippy/pull/5647)
 +* Honor lint level attributes for [`redundant_field_names`], [`just_underscores_and_digits`], [`many_single_char_names`]
 +and [`similar_names`]. [#5651](https://github.com/rust-lang/rust-clippy/pull/5651)
 +* Ignore calls to `len` in [`or_fun_call`]. [#4429](https://github.com/rust-lang/rust-clippy/pull/4429)
 +
 +### Suggestion Improvements
 +
 +* Simplify suggestions in [`manual_memcpy`]. [#5536](https://github.com/rust-lang/rust-clippy/pull/5536)
 +* Fix suggestion in [`redundant_pattern_matching`] for macros. [#5511](https://github.com/rust-lang/rust-clippy/pull/5511)
 +* Avoid suggesting `copied()` for mutable references in [`map_clone`]. [#5530](https://github.com/rust-lang/rust-clippy/pull/5530)
 +* Improve help message for [`clone_double_ref`]. [#5547](https://github.com/rust-lang/rust-clippy/pull/5547)
 +
 +### ICE Fixes
 +
 +* Fix ICE caused in unwrap module. [#5590](https://github.com/rust-lang/rust-clippy/pull/5590)
 +* Fix ICE on rustc test issue-69020-assoc-const-arith-overflow.rs [#5499](https://github.com/rust-lang/rust-clippy/pull/5499)
 +
 +### Documentation
 +
 +* Clarify the documentation of [`unnecessary_mut_passed`]. [#5639](https://github.com/rust-lang/rust-clippy/pull/5639)
 +* Extend example for [`unneeded_field_pattern`]. [#5541](https://github.com/rust-lang/rust-clippy/pull/5541)
 +
 +## Rust 1.44
 +
 +Released 2020-06-04
 +
 +[204bb9b...891e1a8](https://github.com/rust-lang/rust-clippy/compare/204bb9b...891e1a8)
 +
 +### New lints
 +
 +* [`explicit_deref_methods`] [#5226](https://github.com/rust-lang/rust-clippy/pull/5226)
 +* [`implicit_saturating_sub`] [#5427](https://github.com/rust-lang/rust-clippy/pull/5427)
 +* [`macro_use_imports`] [#5230](https://github.com/rust-lang/rust-clippy/pull/5230)
 +* [`verbose_file_reads`] [#5272](https://github.com/rust-lang/rust-clippy/pull/5272)
 +* [`future_not_send`] [#5423](https://github.com/rust-lang/rust-clippy/pull/5423)
 +* [`redundant_pub_crate`] [#5319](https://github.com/rust-lang/rust-clippy/pull/5319)
 +* [`large_const_arrays`] [#5248](https://github.com/rust-lang/rust-clippy/pull/5248)
 +* [`result_map_or_into_option`] [#5415](https://github.com/rust-lang/rust-clippy/pull/5415)
 +* [`redundant_allocation`] [#5349](https://github.com/rust-lang/rust-clippy/pull/5349)
 +* [`fn_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +* [`vtable_address_comparisons`] [#5294](https://github.com/rust-lang/rust-clippy/pull/5294)
 +
 +
 +### Moves and Deprecations
 +
 +* Deprecate [`replace_consts`] lint [#5380](https://github.com/rust-lang/rust-clippy/pull/5380)
 +* Move [`cognitive_complexity`] to nursery [#5428](https://github.com/rust-lang/rust-clippy/pull/5428)
 +* Move [`useless_transmute`] to nursery [#5364](https://github.com/rust-lang/rust-clippy/pull/5364)
 +* Downgrade [`inefficient_to_string`] to pedantic [#5412](https://github.com/rust-lang/rust-clippy/pull/5412)
 +* Downgrade [`option_option`] to pedantic [#5401](https://github.com/rust-lang/rust-clippy/pull/5401)
 +* Downgrade [`unreadable_literal`] to pedantic [#5419](https://github.com/rust-lang/rust-clippy/pull/5419)
 +* Downgrade [`let_unit_value`] to pedantic [#5409](https://github.com/rust-lang/rust-clippy/pull/5409)
 +* Downgrade [`trivially_copy_pass_by_ref`] to pedantic [#5410](https://github.com/rust-lang/rust-clippy/pull/5410)
 +* Downgrade [`implicit_hasher`] to pedantic [#5411](https://github.com/rust-lang/rust-clippy/pull/5411)
 +
 +### Enhancements
 +
 +* On _nightly_ you can now use `cargo clippy --fix -Z unstable-options` to
 +  auto-fix lints that support this [#5363](https://github.com/rust-lang/rust-clippy/pull/5363)
 +* Make [`redundant_clone`] also trigger on cases where the cloned value is not
 +  consumed. [#5304](https://github.com/rust-lang/rust-clippy/pull/5304)
 +* Expand [`integer_arithmetic`] to also disallow bit-shifting [#5430](https://github.com/rust-lang/rust-clippy/pull/5430)
 +* [`option_as_ref_deref`] now detects more deref cases [#5425](https://github.com/rust-lang/rust-clippy/pull/5425)
 +* [`large_enum_variant`] now report the sizes of the largest and second-largest variants [#5466](https://github.com/rust-lang/rust-clippy/pull/5466)
 +* [`bool_comparison`] now also checks for inequality comparisons that can be
 +  written more concisely [#5365](https://github.com/rust-lang/rust-clippy/pull/5365)
 +* Expand [`clone_on_copy`] to work in method call arguments as well [#5441](https://github.com/rust-lang/rust-clippy/pull/5441)
 +* [`redundant_pattern_matching`] now also handles `while let` [#5483](https://github.com/rust-lang/rust-clippy/pull/5483)
 +* [`integer_arithmetic`] now also lints references of integers [#5329](https://github.com/rust-lang/rust-clippy/pull/5329)
 +* Expand [`float_cmp_const`] to also work on arrays [#5345](https://github.com/rust-lang/rust-clippy/pull/5345)
 +* Trigger [`map_flatten`] when map is called on an `Option` [#5473](https://github.com/rust-lang/rust-clippy/pull/5473)
 +
 +### False Positive Fixes
 +
 +* [`many_single_char_names`] [#5468](https://github.com/rust-lang/rust-clippy/pull/5468)
 +* [`should_implement_trait`] [#5437](https://github.com/rust-lang/rust-clippy/pull/5437)
 +* [`unused_self`] [#5387](https://github.com/rust-lang/rust-clippy/pull/5387)
 +* [`redundant_clone`] [#5453](https://github.com/rust-lang/rust-clippy/pull/5453)
 +* [`precedence`] [#5445](https://github.com/rust-lang/rust-clippy/pull/5445)
 +* [`suspicious_op_assign_impl`] [#5424](https://github.com/rust-lang/rust-clippy/pull/5424)
 +* [`needless_lifetimes`] [#5293](https://github.com/rust-lang/rust-clippy/pull/5293)
 +* [`redundant_pattern`] [#5287](https://github.com/rust-lang/rust-clippy/pull/5287)
 +* [`inconsistent_digit_grouping`] [#5451](https://github.com/rust-lang/rust-clippy/pull/5451)
 +
 +
 +### Suggestion Improvements
 +
 +* Improved [`question_mark`] lint suggestion so that it doesn't add redundant `as_ref()` [#5481](https://github.com/rust-lang/rust-clippy/pull/5481)
 +* Improve the suggested placeholder in [`option_map_unit_fn`] [#5292](https://github.com/rust-lang/rust-clippy/pull/5292)
 +* Improve suggestion for [`match_single_binding`] when triggered inside a closure [#5350](https://github.com/rust-lang/rust-clippy/pull/5350)
 +
 +### ICE Fixes
 +
 +* Handle the unstable `trivial_bounds` feature [#5296](https://github.com/rust-lang/rust-clippy/pull/5296)
 +* `shadow_*` lints [#5297](https://github.com/rust-lang/rust-clippy/pull/5297)
 +
 +### Documentation
 +
 +* Fix documentation generation for configurable lints [#5353](https://github.com/rust-lang/rust-clippy/pull/5353)
 +* Update documentation for [`new_ret_no_self`] [#5448](https://github.com/rust-lang/rust-clippy/pull/5448)
 +* The documentation for [`option_option`] now suggest using a tri-state enum [#5403](https://github.com/rust-lang/rust-clippy/pull/5403)
 +* Fix bit mask example in [`verbose_bit_mask`] documentation [#5454](https://github.com/rust-lang/rust-clippy/pull/5454)
 +* [`wildcard_imports`] documentation now mentions that `use ...::prelude::*` is
 +  not linted [#5312](https://github.com/rust-lang/rust-clippy/pull/5312)
 +
 +## Rust 1.43
 +
 +Released 2020-04-23
 +
 +[4ee1206...204bb9b](https://github.com/rust-lang/rust-clippy/compare/4ee1206...204bb9b)
 +
 +### New lints
 +
 +* [`imprecise_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`suboptimal_flops`] [#4897](https://github.com/rust-lang/rust-clippy/pull/4897)
 +* [`wildcard_imports`] [#5029](https://github.com/rust-lang/rust-clippy/pull/5029)
 +* [`single_component_path_imports`] [#5058](https://github.com/rust-lang/rust-clippy/pull/5058)
 +* [`match_single_binding`] [#5061](https://github.com/rust-lang/rust-clippy/pull/5061)
 +* [`let_underscore_lock`] [#5101](https://github.com/rust-lang/rust-clippy/pull/5101)
 +* [`struct_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`fn_params_excessive_bools`] [#5125](https://github.com/rust-lang/rust-clippy/pull/5125)
 +* [`option_env_unwrap`] [#5148](https://github.com/rust-lang/rust-clippy/pull/5148)
 +* [`lossy_float_literal`] [#5202](https://github.com/rust-lang/rust-clippy/pull/5202)
 +* [`rest_pat_in_fully_bound_structs`] [#5258](https://github.com/rust-lang/rust-clippy/pull/5258)
 +
 +### Moves and Deprecations
 +
 +* Move [`unneeded_field_pattern`] to pedantic group [#5200](https://github.com/rust-lang/rust-clippy/pull/5200)
 +
 +### Enhancements
 +
 +* Make [`missing_errors_doc`] lint also trigger on `async` functions
 +  [#5181](https://github.com/rust-lang/rust-clippy/pull/5181)
 +* Add more constants to [`approx_constant`] [#5193](https://github.com/rust-lang/rust-clippy/pull/5193)
 +* Extend [`question_mark`] lint [#5266](https://github.com/rust-lang/rust-clippy/pull/5266)
 +
 +### False Positive Fixes
 +
 +* [`use_debug`] [#5047](https://github.com/rust-lang/rust-clippy/pull/5047)
 +* [`unnecessary_unwrap`] [#5132](https://github.com/rust-lang/rust-clippy/pull/5132)
 +* [`zero_prefixed_literal`] [#5170](https://github.com/rust-lang/rust-clippy/pull/5170)
 +* [`missing_const_for_fn`] [#5216](https://github.com/rust-lang/rust-clippy/pull/5216)
 +
 +### Suggestion Improvements
 +
 +* Improve suggestion when blocks of code are suggested [#5134](https://github.com/rust-lang/rust-clippy/pull/5134)
 +
 +### ICE Fixes
 +
 +* `misc_early` lints [#5129](https://github.com/rust-lang/rust-clippy/pull/5129)
 +* [`missing_errors_doc`] [#5213](https://github.com/rust-lang/rust-clippy/pull/5213)
 +* Fix ICE when evaluating `usize`s [#5256](https://github.com/rust-lang/rust-clippy/pull/5256)
 +
 +### Documentation
 +
 +* Improve documentation of [`iter_nth_zero`]
 +* Add documentation pages for stable releases [#5171](https://github.com/rust-lang/rust-clippy/pull/5171)
 +
 +### Others
 +
 +* Clippy now completely runs on GitHub Actions [#5190](https://github.com/rust-lang/rust-clippy/pull/5190)
 +
 +
 +## Rust 1.42
 +
 +Released 2020-03-12
 +
 +[69f99e7...4ee1206](https://github.com/rust-lang/rust-clippy/compare/69f99e7...4ee1206)
 +
 +### New lints
 +
 +* [`filetype_is_file`] [#4543](https://github.com/rust-lang/rust-clippy/pull/4543)
 +* [`let_underscore_must_use`] [#4823](https://github.com/rust-lang/rust-clippy/pull/4823)
 +* [`modulo_arithmetic`] [#4867](https://github.com/rust-lang/rust-clippy/pull/4867)
 +* [`mem_replace_with_default`] [#4881](https://github.com/rust-lang/rust-clippy/pull/4881)
 +* [`mutable_key_type`] [#4885](https://github.com/rust-lang/rust-clippy/pull/4885)
 +* [`option_as_ref_deref`] [#4945](https://github.com/rust-lang/rust-clippy/pull/4945)
 +* [`wildcard_in_or_patterns`] [#4960](https://github.com/rust-lang/rust-clippy/pull/4960)
 +* [`iter_nth_zero`] [#4966](https://github.com/rust-lang/rust-clippy/pull/4966)
 +* `invalid_atomic_ordering` [#4999](https://github.com/rust-lang/rust-clippy/pull/4999)
 +* [`skip_while_next`] [#5067](https://github.com/rust-lang/rust-clippy/pull/5067)
 +
 +### Moves and Deprecations
 +
 +* Move [`transmute_float_to_int`] from nursery to complexity group
 +  [#5015](https://github.com/rust-lang/rust-clippy/pull/5015)
 +* Move [`range_plus_one`] to pedantic group [#5057](https://github.com/rust-lang/rust-clippy/pull/5057)
 +* Move [`debug_assert_with_mut_call`] to nursery group [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Deprecate `unused_label` [#4930](https://github.com/rust-lang/rust-clippy/pull/4930)
 +
 +### Enhancements
 +
 +* Lint vectored IO in [`unused_io_amount`] [#5027](https://github.com/rust-lang/rust-clippy/pull/5027)
 +* Make [`vec_box`] configurable by adding a size threshold [#5081](https://github.com/rust-lang/rust-clippy/pull/5081)
 +* Also lint constants in [`cmp_nan`] [#4910](https://github.com/rust-lang/rust-clippy/pull/4910)
 +* Fix false negative in [`expect_fun_call`] [#4915](https://github.com/rust-lang/rust-clippy/pull/4915)
 +* Fix false negative in [`redundant_clone`] [#5017](https://github.com/rust-lang/rust-clippy/pull/5017)
 +
 +### False Positive Fixes
 +
 +* [`map_clone`] [#4937](https://github.com/rust-lang/rust-clippy/pull/4937)
 +* [`replace_consts`] [#4977](https://github.com/rust-lang/rust-clippy/pull/4977)
 +* [`let_and_return`] [#5008](https://github.com/rust-lang/rust-clippy/pull/5008)
 +* [`eq_op`] [#5079](https://github.com/rust-lang/rust-clippy/pull/5079)
 +* [`possible_missing_comma`] [#5083](https://github.com/rust-lang/rust-clippy/pull/5083)
 +* [`debug_assert_with_mut_call`] [#5106](https://github.com/rust-lang/rust-clippy/pull/5106)
 +* Don't trigger [`let_underscore_must_use`] in external macros
 +  [#5082](https://github.com/rust-lang/rust-clippy/pull/5082)
 +* Don't trigger [`empty_loop`] in `no_std` crates [#5086](https://github.com/rust-lang/rust-clippy/pull/5086)
 +
 +### Suggestion Improvements
 +
 +* `option_map_unwrap_or` [#4634](https://github.com/rust-lang/rust-clippy/pull/4634)
 +* [`wildcard_enum_match_arm`] [#4934](https://github.com/rust-lang/rust-clippy/pull/4934)
 +* [`cognitive_complexity`] [#4935](https://github.com/rust-lang/rust-clippy/pull/4935)
 +* [`decimal_literal_representation`] [#4956](https://github.com/rust-lang/rust-clippy/pull/4956)
 +* `unknown_clippy_lints` [#4963](https://github.com/rust-lang/rust-clippy/pull/4963)
 +* [`explicit_into_iter_loop`] [#4978](https://github.com/rust-lang/rust-clippy/pull/4978)
 +* [`useless_attribute`] [#5022](https://github.com/rust-lang/rust-clippy/pull/5022)
 +* `if_let_some_result` [#5032](https://github.com/rust-lang/rust-clippy/pull/5032)
 +
 +### ICE fixes
 +
 +* [`unsound_collection_transmute`] [#4975](https://github.com/rust-lang/rust-clippy/pull/4975)
 +
 +### Documentation
 +
 +* Improve documentation of [`empty_enum`], [`replace_consts`], [`redundant_clone`], and [`iterator_step_by_zero`]
 +
 +
 +## Rust 1.41
 +
 +Released 2020-01-30
 +
 +[c8e3cfb...69f99e7](https://github.com/rust-lang/rust-clippy/compare/c8e3cfb...69f99e7)
 +
 +* New Lints:
 +  * [`exit`] [#4697](https://github.com/rust-lang/rust-clippy/pull/4697)
 +  * [`to_digit_is_some`] [#4801](https://github.com/rust-lang/rust-clippy/pull/4801)
 +  * [`tabs_in_doc_comments`] [#4806](https://github.com/rust-lang/rust-clippy/pull/4806)
 +  * [`large_stack_arrays`] [#4807](https://github.com/rust-lang/rust-clippy/pull/4807)
 +  * [`same_functions_in_if_condition`] [#4814](https://github.com/rust-lang/rust-clippy/pull/4814)
 +  * [`zst_offset`] [#4816](https://github.com/rust-lang/rust-clippy/pull/4816)
 +  * [`as_conversions`] [#4821](https://github.com/rust-lang/rust-clippy/pull/4821)
 +  * [`missing_errors_doc`] [#4884](https://github.com/rust-lang/rust-clippy/pull/4884)
 +  * [`transmute_float_to_int`] [#4889](https://github.com/rust-lang/rust-clippy/pull/4889)
 +* Remove plugin interface, see
 +  [Inside Rust Blog](https://blog.rust-lang.org/inside-rust/2019/11/04/Clippy-removes-plugin-interface.html) for
 +  details [#4714](https://github.com/rust-lang/rust-clippy/pull/4714)
 +* Move [`use_self`] to nursery group [#4863](https://github.com/rust-lang/rust-clippy/pull/4863)
 +* Deprecate `into_iter_on_array` [#4788](https://github.com/rust-lang/rust-clippy/pull/4788)
 +* Expand [`string_lit_as_bytes`] to also trigger when literal has escapes
 +  [#4808](https://github.com/rust-lang/rust-clippy/pull/4808)
 +* Fix false positive in `comparison_chain` [#4842](https://github.com/rust-lang/rust-clippy/pull/4842)
 +* Fix false positive in `while_immutable_condition` [#4730](https://github.com/rust-lang/rust-clippy/pull/4730)
 +* Fix false positive in `explicit_counter_loop` [#4803](https://github.com/rust-lang/rust-clippy/pull/4803)
 +* Fix false positive in `must_use_candidate` [#4794](https://github.com/rust-lang/rust-clippy/pull/4794)
 +* Fix false positive in `print_with_newline` and `write_with_newline`
 +  [#4769](https://github.com/rust-lang/rust-clippy/pull/4769)
 +* Fix false positive in `derive_hash_xor_eq` [#4766](https://github.com/rust-lang/rust-clippy/pull/4766)
 +* Fix false positive in `missing_inline_in_public_items` [#4870](https://github.com/rust-lang/rust-clippy/pull/4870)
 +* Fix false positive in `string_add` [#4880](https://github.com/rust-lang/rust-clippy/pull/4880)
 +* Fix false positive in `float_arithmetic` [#4851](https://github.com/rust-lang/rust-clippy/pull/4851)
 +* Fix false positive in `cast_sign_loss` [#4883](https://github.com/rust-lang/rust-clippy/pull/4883)
 +* Fix false positive in `manual_swap` [#4877](https://github.com/rust-lang/rust-clippy/pull/4877)
 +* Fix ICEs occurring while checking some block expressions [#4772](https://github.com/rust-lang/rust-clippy/pull/4772)
 +* Fix ICE in `use_self` [#4776](https://github.com/rust-lang/rust-clippy/pull/4776)
 +* Fix ICEs related to `const_generics` [#4780](https://github.com/rust-lang/rust-clippy/pull/4780)
 +* Display help when running `clippy-driver` without arguments, instead of ICEing
 +  [#4810](https://github.com/rust-lang/rust-clippy/pull/4810)
 +* Clippy has its own ICE message now [#4588](https://github.com/rust-lang/rust-clippy/pull/4588)
 +* Show deprecated lints in the documentation again [#4757](https://github.com/rust-lang/rust-clippy/pull/4757)
 +* Improve Documentation by adding positive examples to some lints
 +  [#4832](https://github.com/rust-lang/rust-clippy/pull/4832)
 +
 +## Rust 1.40
 +
 +Released 2019-12-19
 +
 +[4e7e71b...c8e3cfb](https://github.com/rust-lang/rust-clippy/compare/4e7e71b...c8e3cfb)
 +
 +* New Lints:
 +  * [`unneeded_wildcard_pattern`] [#4537](https://github.com/rust-lang/rust-clippy/pull/4537)
 +  * [`needless_doctest_main`] [#4603](https://github.com/rust-lang/rust-clippy/pull/4603)
 +  * [`suspicious_unary_op_formatting`] [#4615](https://github.com/rust-lang/rust-clippy/pull/4615)
 +  * [`debug_assert_with_mut_call`] [#4680](https://github.com/rust-lang/rust-clippy/pull/4680)
 +  * [`unused_self`] [#4619](https://github.com/rust-lang/rust-clippy/pull/4619)
 +  * [`inefficient_to_string`] [#4683](https://github.com/rust-lang/rust-clippy/pull/4683)
 +  * [`must_use_unit`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`must_use_candidate`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`double_must_use`] [#4560](https://github.com/rust-lang/rust-clippy/pull/4560)
 +  * [`comparison_chain`] [#4569](https://github.com/rust-lang/rust-clippy/pull/4569)
 +  * [`unsound_collection_transmute`] [#4592](https://github.com/rust-lang/rust-clippy/pull/4592)
 +  * [`panic`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`unreachable`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * [`todo`] [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `option_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +  * `result_expect_used` [#4657](https://github.com/rust-lang/rust-clippy/pull/4657)
 +* Move `redundant_clone` to perf group [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Move `manual_mul_add` to nursery group [#4736](https://github.com/rust-lang/rust-clippy/pull/4736)
 +* Expand `unit_cmp` to also work with `assert_eq!`, `debug_assert_eq!`, `assert_ne!` and `debug_assert_ne!` [#4613](https://github.com/rust-lang/rust-clippy/pull/4613)
 +* Expand `integer_arithmetic` to also detect mutating arithmetic like `+=` [#4585](https://github.com/rust-lang/rust-clippy/pull/4585)
 +* Fix false positive in `nonminimal_bool` [#4568](https://github.com/rust-lang/rust-clippy/pull/4568)
 +* Fix false positive in `missing_safety_doc` [#4611](https://github.com/rust-lang/rust-clippy/pull/4611)
 +* Fix false positive in `cast_sign_loss` [#4614](https://github.com/rust-lang/rust-clippy/pull/4614)
 +* Fix false positive in `redundant_clone` [#4509](https://github.com/rust-lang/rust-clippy/pull/4509)
 +* Fix false positive in `try_err` [#4721](https://github.com/rust-lang/rust-clippy/pull/4721)
 +* Fix false positive in `toplevel_ref_arg` [#4570](https://github.com/rust-lang/rust-clippy/pull/4570)
 +* Fix false positive in `multiple_inherent_impl` [#4593](https://github.com/rust-lang/rust-clippy/pull/4593)
 +* Improve more suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4575](https://github.com/rust-lang/rust-clippy/pull/4575)
 +* Improve suggestion for `zero_ptr` [#4599](https://github.com/rust-lang/rust-clippy/pull/4599)
 +* Improve suggestion for `explicit_counter_loop` [#4691](https://github.com/rust-lang/rust-clippy/pull/4691)
 +* Improve suggestion for `mul_add` [#4602](https://github.com/rust-lang/rust-clippy/pull/4602)
 +* Improve suggestion for `assertions_on_constants` [#4635](https://github.com/rust-lang/rust-clippy/pull/4635)
 +* Fix ICE in `use_self` [#4671](https://github.com/rust-lang/rust-clippy/pull/4671)
 +* Fix ICE when encountering const casts [#4590](https://github.com/rust-lang/rust-clippy/pull/4590)
 +
 +## Rust 1.39
 +
 +Released 2019-11-07
 +
 +[3aea860...4e7e71b](https://github.com/rust-lang/rust-clippy/compare/3aea860...4e7e71b)
 +
 +* New Lints:
 +  * [`uninit_assumed_init`] [#4479](https://github.com/rust-lang/rust-clippy/pull/4479)
 +  * [`flat_map_identity`] [#4231](https://github.com/rust-lang/rust-clippy/pull/4231)
 +  * [`missing_safety_doc`] [#4535](https://github.com/rust-lang/rust-clippy/pull/4535)
 +  * [`mem_replace_with_uninit`] [#4511](https://github.com/rust-lang/rust-clippy/pull/4511)
 +  * [`suspicious_map`] [#4394](https://github.com/rust-lang/rust-clippy/pull/4394)
 +  * `option_and_then_some` [#4386](https://github.com/rust-lang/rust-clippy/pull/4386)
 +  * [`manual_saturating_arithmetic`] [#4498](https://github.com/rust-lang/rust-clippy/pull/4498)
 +* Deprecate `unused_collect` lint. This is fully covered by rustc's `#[must_use]` on `collect` [#4348](https://github.com/rust-lang/rust-clippy/pull/4348)
 +* Move `type_repetition_in_bounds` to pedantic group [#4403](https://github.com/rust-lang/rust-clippy/pull/4403)
 +* Move `cast_lossless` to pedantic group [#4539](https://github.com/rust-lang/rust-clippy/pull/4539)
 +* `temporary_cstring_as_ptr` now catches more cases [#4425](https://github.com/rust-lang/rust-clippy/pull/4425)
 +* `use_self` now works in constructors, too [#4525](https://github.com/rust-lang/rust-clippy/pull/4525)
 +* `cargo_common_metadata` now checks for license files [#4518](https://github.com/rust-lang/rust-clippy/pull/4518)
 +* `cognitive_complexity` now includes the measured complexity in the warning message [#4469](https://github.com/rust-lang/rust-clippy/pull/4469)
 +* Fix false positives in `block_in_if_*` lints [#4458](https://github.com/rust-lang/rust-clippy/pull/4458)
 +* Fix false positive in `cast_lossless` [#4473](https://github.com/rust-lang/rust-clippy/pull/4473)
 +* Fix false positive in `clone_on_copy` [#4411](https://github.com/rust-lang/rust-clippy/pull/4411)
 +* Fix false positive in `deref_addrof` [#4487](https://github.com/rust-lang/rust-clippy/pull/4487)
 +* Fix false positive in `too_many_lines` [#4490](https://github.com/rust-lang/rust-clippy/pull/4490)
 +* Fix false positive in `new_ret_no_self` [#4365](https://github.com/rust-lang/rust-clippy/pull/4365)
 +* Fix false positive in `manual_swap` [#4478](https://github.com/rust-lang/rust-clippy/pull/4478)
 +* Fix false positive in `missing_const_for_fn` [#4450](https://github.com/rust-lang/rust-clippy/pull/4450)
 +* Fix false positive in `extra_unused_lifetimes` [#4477](https://github.com/rust-lang/rust-clippy/pull/4477)
 +* Fix false positive in `inherent_to_string` [#4460](https://github.com/rust-lang/rust-clippy/pull/4460)
 +* Fix false positive in `map_entry` [#4495](https://github.com/rust-lang/rust-clippy/pull/4495)
 +* Fix false positive in `unused_unit` [#4445](https://github.com/rust-lang/rust-clippy/pull/4445)
 +* Fix false positive in `redundant_pattern` [#4489](https://github.com/rust-lang/rust-clippy/pull/4489)
 +* Fix false positive in `wrong_self_convention` [#4369](https://github.com/rust-lang/rust-clippy/pull/4369)
 +* Improve various suggestions and tests in preparation for the unstable `cargo fix --clippy` [#4558](https://github.com/rust-lang/rust-clippy/pull/4558)
 +* Improve suggestions for `redundant_pattern_matching` [#4352](https://github.com/rust-lang/rust-clippy/pull/4352)
 +* Improve suggestions for `explicit_write` [#4544](https://github.com/rust-lang/rust-clippy/pull/4544)
 +* Improve suggestion for `or_fun_call` [#4522](https://github.com/rust-lang/rust-clippy/pull/4522)
 +* Improve suggestion for `match_as_ref` [#4446](https://github.com/rust-lang/rust-clippy/pull/4446)
 +* Improve suggestion for `unnecessary_fold_span` [#4382](https://github.com/rust-lang/rust-clippy/pull/4382)
 +* Add suggestions for `unseparated_literal_suffix` [#4401](https://github.com/rust-lang/rust-clippy/pull/4401)
 +* Add suggestions for `char_lit_as_u8` [#4418](https://github.com/rust-lang/rust-clippy/pull/4418)
 +
 +## Rust 1.38
 +
 +Released 2019-09-26
 +
 +[e3cb40e...3aea860](https://github.com/rust-lang/rust-clippy/compare/e3cb40e...3aea860)
 +
 +* New Lints:
 +  * [`main_recursion`] [#4203](https://github.com/rust-lang/rust-clippy/pull/4203)
 +  * [`inherent_to_string`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
 +  * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
 +  * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
 +* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 +* Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 +* Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 +* Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
 +* Fix false positive in `pub_enum_variant_names` and `enum_variant_names` [#4345](https://github.com/rust-lang/rust-clippy/pull/4345)
 +* Fix false positive in `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Fix false positive in `string_lit_as_bytes` [#4233](https://github.com/rust-lang/rust-clippy/pull/4233)
 +* Fix false positive in `needless_lifetimes` [#4266](https://github.com/rust-lang/rust-clippy/pull/4266)
 +* Fix false positive in `float_cmp` [#4275](https://github.com/rust-lang/rust-clippy/pull/4275)
 +* Fix false positives in `needless_return` [#4274](https://github.com/rust-lang/rust-clippy/pull/4274)
 +* Fix false negative in `match_same_arms` [#4246](https://github.com/rust-lang/rust-clippy/pull/4246)
 +* Fix incorrect suggestion for `needless_bool` [#4335](https://github.com/rust-lang/rust-clippy/pull/4335)
 +* Improve suggestion for `cast_ptr_alignment` [#4257](https://github.com/rust-lang/rust-clippy/pull/4257)
 +* Improve suggestion for `single_char_literal` [#4361](https://github.com/rust-lang/rust-clippy/pull/4361)
 +* Improve suggestion for `len_zero` [#4314](https://github.com/rust-lang/rust-clippy/pull/4314)
 +* Fix ICE in `implicit_hasher` [#4268](https://github.com/rust-lang/rust-clippy/pull/4268)
 +* Fix allow bug in `trivially_copy_pass_by_ref` [#4250](https://github.com/rust-lang/rust-clippy/pull/4250)
 +
 +## Rust 1.37
 +
 +Released 2019-08-15
 +
 +[082cfa7...e3cb40e](https://github.com/rust-lang/rust-clippy/compare/082cfa7...e3cb40e)
 +
 +* New Lints:
 +  * [`checked_conversions`] [#4088](https://github.com/rust-lang/rust-clippy/pull/4088)
 +  * [`get_last_with_len`] [#3832](https://github.com/rust-lang/rust-clippy/pull/3832)
 +  * [`integer_division`] [#4195](https://github.com/rust-lang/rust-clippy/pull/4195)
 +* Renamed Lint: `const_static_lifetime` is now called [`redundant_static_lifetimes`].
 +  The lint now covers statics in addition to consts [#4162](https://github.com/rust-lang/rust-clippy/pull/4162)
 +* [`match_same_arms`] now warns for all identical arms, instead of only the first one [#4102](https://github.com/rust-lang/rust-clippy/pull/4102)
 +* [`needless_return`] now works with void functions [#4220](https://github.com/rust-lang/rust-clippy/pull/4220)
 +* Fix false positive in [`redundant_closure`] [#4190](https://github.com/rust-lang/rust-clippy/pull/4190)
 +* Fix false positive in [`useless_attribute`] [#4107](https://github.com/rust-lang/rust-clippy/pull/4107)
 +* Fix incorrect suggestion for [`float_cmp`] [#4214](https://github.com/rust-lang/rust-clippy/pull/4214)
 +* Add suggestions for [`print_with_newline`] and [`write_with_newline`] [#4136](https://github.com/rust-lang/rust-clippy/pull/4136)
 +* Improve suggestions for `option_map_unwrap_or_else` and `result_map_unwrap_or_else` [#4164](https://github.com/rust-lang/rust-clippy/pull/4164)
 +* Improve suggestions for [`non_ascii_literal`] [#4119](https://github.com/rust-lang/rust-clippy/pull/4119)
 +* Improve diagnostics for [`let_and_return`] [#4137](https://github.com/rust-lang/rust-clippy/pull/4137)
 +* Improve diagnostics for [`trivially_copy_pass_by_ref`] [#4071](https://github.com/rust-lang/rust-clippy/pull/4071)
 +* Add macro check for [`unreadable_literal`] [#4099](https://github.com/rust-lang/rust-clippy/pull/4099)
 +
 +## Rust 1.36
 +
 +Released 2019-07-04
 +
 +[eb9f9b1...082cfa7](https://github.com/rust-lang/rust-clippy/compare/eb9f9b1...082cfa7)
 +
 +* New lints: [`find_map`], [`filter_map_next`] [#4039](https://github.com/rust-lang/rust-clippy/pull/4039)
 +* New lint: [`path_buf_push_overwrite`] [#3954](https://github.com/rust-lang/rust-clippy/pull/3954)
 +* Move `path_buf_push_overwrite` to the nursery [#4013](https://github.com/rust-lang/rust-clippy/pull/4013)
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Allow allowing of [`toplevel_ref_arg`] lint [#4007](https://github.com/rust-lang/rust-clippy/pull/4007)
 +* Fix false negative in [`or_fun_call`] pertaining to nested constructors [#4084](https://github.com/rust-lang/rust-clippy/pull/4084)
 +* Fix false positive in [`or_fun_call`] pertaining to enum variant constructors [#4018](https://github.com/rust-lang/rust-clippy/pull/4018)
 +* Fix false positive in [`useless_let_if_seq`] pertaining to interior mutability [#4035](https://github.com/rust-lang/rust-clippy/pull/4035)
 +* Fix false positive in [`redundant_closure`] pertaining to non-function types [#4008](https://github.com/rust-lang/rust-clippy/pull/4008)
 +* Fix false positive in [`let_and_return`] pertaining to attributes on `let`s [#4024](https://github.com/rust-lang/rust-clippy/pull/4024)
 +* Fix false positive in [`module_name_repetitions`] lint pertaining to attributes [#4006](https://github.com/rust-lang/rust-clippy/pull/4006)
 +* Fix false positive on [`assertions_on_constants`] pertaining to `debug_assert!` [#3989](https://github.com/rust-lang/rust-clippy/pull/3989)
 +* Improve suggestion in [`map_clone`] to suggest `.copied()` where applicable  [#3970](https://github.com/rust-lang/rust-clippy/pull/3970) [#4043](https://github.com/rust-lang/rust-clippy/pull/4043)
 +* Improve suggestion for [`search_is_some`] [#4049](https://github.com/rust-lang/rust-clippy/pull/4049)
 +* Improve suggestion applicability for [`naive_bytecount`] [#3984](https://github.com/rust-lang/rust-clippy/pull/3984)
 +* Improve suggestion applicability for [`while_let_loop`] [#3975](https://github.com/rust-lang/rust-clippy/pull/3975)
 +* Improve diagnostics for [`too_many_arguments`] [#4053](https://github.com/rust-lang/rust-clippy/pull/4053)
 +* Improve diagnostics for [`cast_lossless`] [#4021](https://github.com/rust-lang/rust-clippy/pull/4021)
 +* Deal with macro checks in desugarings better [#4082](https://github.com/rust-lang/rust-clippy/pull/4082)
 +* Add macro check for [`unnecessary_cast`]  [#4026](https://github.com/rust-lang/rust-clippy/pull/4026)
 +* Remove [`approx_constant`]'s documentation's "Known problems" section. [#4027](https://github.com/rust-lang/rust-clippy/pull/4027)
 +* Fix ICE in [`suspicious_else_formatting`] [#3960](https://github.com/rust-lang/rust-clippy/pull/3960)
 +* Fix ICE in [`decimal_literal_representation`] [#3931](https://github.com/rust-lang/rust-clippy/pull/3931)
 +
 +
 +## Rust 1.35
 +
 +Released 2019-05-20
 +
 +[1fac380..37f5c1e](https://github.com/rust-lang/rust-clippy/compare/1fac380...37f5c1e)
 +
 +* New lint: `drop_bounds` to detect `T: Drop` bounds
 +* Split [`redundant_closure`] into [`redundant_closure`] and [`redundant_closure_for_method_calls`] [#4110](https://github.com/rust-lang/rust-clippy/pull/4101)
 +* Rename `cyclomatic_complexity` to [`cognitive_complexity`], start work on making lint more practical for Rust code
 +* Move [`get_unwrap`] to the restriction category
 +* Improve suggestions for [`iter_cloned_collect`]
 +* Improve suggestions for [`cast_lossless`] to suggest suffixed literals
 +* Fix false positives in [`print_with_newline`] and [`write_with_newline`] pertaining to raw strings
 +* Fix false positive in [`needless_range_loop`] pertaining to structs without a `.iter()`
 +* Fix false positive in [`bool_comparison`] pertaining to non-bool types
 +* Fix false positive in [`redundant_closure`] pertaining to differences in borrows
 +* Fix false positive in `option_map_unwrap_or` on non-copy types
 +* Fix false positives in [`missing_const_for_fn`] pertaining to macros and trait method impls
 +* Fix false positive in [`needless_pass_by_value`] pertaining to procedural macros
 +* Fix false positive in [`needless_continue`] pertaining to loop labels
 +* Fix false positive for [`boxed_local`] pertaining to arguments moved into closures
 +* Fix false positive for [`use_self`] in nested functions
 +* Fix suggestion for [`expect_fun_call`] (https://github.com/rust-lang/rust-clippy/pull/3846)
 +* Fix suggestion for [`explicit_counter_loop`] to deal with parenthesizing range variables
 +* Fix suggestion for [`single_char_pattern`] to correctly escape single quotes
 +* Avoid triggering [`redundant_closure`] in macros
 +* ICE fixes: [#3805](https://github.com/rust-lang/rust-clippy/pull/3805), [#3772](https://github.com/rust-lang/rust-clippy/pull/3772), [#3741](https://github.com/rust-lang/rust-clippy/pull/3741)
 +
 +## Rust 1.34
 +
 +Released 2019-04-10
 +
 +[1b89724...1fac380](https://github.com/rust-lang/rust-clippy/compare/1b89724...1fac380)
 +
 +* New lint: [`assertions_on_constants`] to detect for example `assert!(true)`
 +* New lint: [`dbg_macro`] to detect uses of the `dbg!` macro
 +* New lint: [`missing_const_for_fn`] that can suggest functions to be made `const`
 +* New lint: [`too_many_lines`] to detect functions with excessive LOC. It can be
 +  configured using the `too-many-lines-threshold` configuration.
 +* New lint: [`wildcard_enum_match_arm`] to check for wildcard enum matches using `_`
 +* Expand `redundant_closure` to also work for methods (not only functions)
 +* Fix ICEs in `vec_box`, `needless_pass_by_value` and `implicit_hasher`
 +* Fix false positive in `cast_sign_loss`
 +* Fix false positive in `integer_arithmetic`
 +* Fix false positive in `unit_arg`
 +* Fix false positives in `implicit_return`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `cast_lossless`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `needless_bool`
 +* Fix incorrect suggestion for `needless_range_loop`
 +* Fix incorrect suggestion for `use_self`
 +* Fix incorrect suggestion for `while_let_on_iterator`
 +* Clippy is now slightly easier to invoke in non-cargo contexts. See
 +  [#3665][pull3665] for more details.
 +* We now have [improved documentation][adding_lints] on how to add new lints
 +
 +## Rust 1.33
 +
 +Released 2019-02-26
 +
 +[b2601be...1b89724](https://github.com/rust-lang/rust-clippy/compare/b2601be...1b89724)
 +
 +* New lints: [`implicit_return`], [`vec_box`], [`cast_ref_to_mut`]
 +* The `rust-clippy` repository is now part of the `rust-lang` org.
 +* Rename `stutter` to `module_name_repetitions`
 +* Merge `new_without_default_derive` into `new_without_default` lint
 +* Move `large_digit_groups` from `style` group to `pedantic`
 +* Expand `bool_comparison` to check for `<`, `<=`, `>`, `>=`, and `!=`
 +  comparisons against booleans
 +* Expand `no_effect` to detect writes to constants such as `A_CONST.field = 2`
 +* Expand `redundant_clone` to work on struct fields
 +* Expand `suspicious_else_formatting` to detect `if .. {..} {..}`
 +* Expand `use_self` to work on tuple structs and also in local macros
 +* Fix ICE in `result_map_unit_fn` and `option_map_unit_fn`
 +* Fix false positives in `implicit_return`
 +* Fix false positives in `use_self`
 +* Fix false negative in `clone_on_copy`
 +* Fix false positive in `doc_markdown`
 +* Fix false positive in `empty_loop`
 +* Fix false positive in `if_same_then_else`
 +* Fix false positive in `infinite_iter`
 +* Fix false positive in `question_mark`
 +* Fix false positive in `useless_asref`
 +* Fix false positive in `wildcard_dependencies`
 +* Fix false positive in `write_with_newline`
 +* Add suggestion to `explicit_write`
 +* Improve suggestions for `question_mark` lint
 +* Fix incorrect suggestion for `get_unwrap`
 +
 +## Rust 1.32
 +
 +Released 2019-01-17
 +
 +[2e26fdc2...b2601be](https://github.com/rust-lang/rust-clippy/compare/2e26fdc2...b2601be)
 +
 +* New lints: [`slow_vector_initialization`], `mem_discriminant_non_enum`,
 +  [`redundant_clone`], [`wildcard_dependencies`],
 +  [`into_iter_on_ref`], `into_iter_on_array`, [`deprecated_cfg_attr`],
 +  [`cargo_common_metadata`]
 +* Add support for `u128` and `i128` to integer related lints
 +* Add float support to `mistyped_literal_suffixes`
 +* Fix false positives in `use_self`
 +* Fix false positives in `missing_comma`
 +* Fix false positives in `new_ret_no_self`
 +* Fix false positives in `possible_missing_comma`
 +* Fix false positive in `integer_arithmetic` in constant items
 +* Fix false positive in `needless_borrow`
 +* Fix false positive in `out_of_bounds_indexing`
 +* Fix false positive in `new_without_default_derive`
 +* Fix false positive in `string_lit_as_bytes`
 +* Fix false negative in `out_of_bounds_indexing`
 +* Fix false negative in `use_self`. It will now also check existential types
 +* Fix incorrect suggestion for `redundant_closure_call`
 +* Fix various suggestions that contained expanded macros
 +* Fix `bool_comparison` triggering 3 times on on on the same code
 +* Expand `trivially_copy_pass_by_ref` to work on trait methods
 +* Improve suggestion for `needless_range_loop`
 +* Move `needless_pass_by_value` from `pedantic` group to `style`
 +
 +## Rust 1.31
 +
 +Released 2018-12-06
 +
 +[125907ad..2e26fdc2](https://github.com/rust-lang/rust-clippy/compare/125907ad..2e26fdc2)
 +
 +* Clippy has been relicensed under a dual MIT / Apache license.
 +  See [#3093](https://github.com/rust-lang/rust-clippy/issues/3093) for more
 +  information.
 +* With Rust 1.31, Clippy is no longer available via crates.io. The recommended
 +  installation method is via `rustup component add clippy`.
 +* New lints: [`redundant_pattern_matching`], [`unnecessary_filter_map`],
 +  [`unused_unit`], [`map_flatten`], [`mem_replace_option_with_none`]
 +* Fix ICE in `if_let_redundant_pattern_matching`
 +* Fix ICE in `needless_pass_by_value` when encountering a generic function
 +  argument with a lifetime parameter
 +* Fix ICE in `needless_range_loop`
 +* Fix ICE in `single_char_pattern` when encountering a constant value
 +* Fix false positive in `assign_op_pattern`
 +* Fix false positive in `boxed_local` on trait implementations
 +* Fix false positive in `cmp_owned`
 +* Fix false positive in `collapsible_if` when conditionals have comments
 +* Fix false positive in `double_parens`
 +* Fix false positive in `excessive_precision`
 +* Fix false positive in `explicit_counter_loop`
 +* Fix false positive in `fn_to_numeric_cast_with_truncation`
 +* Fix false positive in `map_clone`
 +* Fix false positive in `new_ret_no_self`
 +* Fix false positive in `new_without_default` when `new` is unsafe
 +* Fix false positive in `type_complexity` when using extern types
 +* Fix false positive in `useless_format`
 +* Fix false positive in `wrong_self_convention`
 +* Fix incorrect suggestion for `excessive_precision`
 +* Fix incorrect suggestion for `expect_fun_call`
 +* Fix incorrect suggestion for `get_unwrap`
 +* Fix incorrect suggestion for `useless_format`
 +* `fn_to_numeric_cast_with_truncation` lint can be disabled again
 +* Improve suggestions for `manual_memcpy`
 +* Improve help message for `needless_lifetimes`
 +
 +## Rust 1.30
 +
 +Released 2018-10-25
 +
 +[14207503...125907ad](https://github.com/rust-lang/rust-clippy/compare/14207503...125907ad)
 +
 +* Deprecate `assign_ops` lint
 +* New lints: [`mistyped_literal_suffixes`], [`ptr_offset_with_cast`],
 +  [`needless_collect`], [`copy_iterator`]
 +* `cargo clippy -V` now includes the Clippy commit hash of the Rust
 +  Clippy component
 +* Fix ICE in `implicit_hasher`
 +* Fix ICE when encountering `println!("{}" a);`
 +* Fix ICE when encountering a macro call in match statements
 +* Fix false positive in `default_trait_access`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `similar_names`
 +* Fix false positive in `redundant_field_name`
 +* Fix false positive in `expect_fun_call`
 +* Fix false negative in `identity_conversion`
 +* Fix false negative in `explicit_counter_loop`
 +* Fix `range_plus_one` suggestion and false negative
 +* `print_with_newline` / `write_with_newline`: don't warn about string with several `\n`s in them
 +* Fix `useless_attribute` to also whitelist `unused_extern_crates`
 +* Fix incorrect suggestion for `single_char_pattern`
 +* Improve suggestion for `identity_conversion` lint
 +* Move `explicit_iter_loop` and `explicit_into_iter_loop` from `style` group to `pedantic`
 +* Move `range_plus_one` and `range_minus_one` from `nursery` group to `complexity`
 +* Move `shadow_unrelated` from `restriction` group to `pedantic`
 +* Move `indexing_slicing` from `pedantic` group to `restriction`
 +
 +## Rust 1.29
 +
 +Released 2018-09-13
 +
 +[v0.0.212...14207503](https://github.com/rust-lang/rust-clippy/compare/v0.0.212...14207503)
 +
 +* :tada: :tada: **Rust 1.29 is the first stable Rust that includes a bundled Clippy** :tada:
 +  :tada:
 +  You can now run `rustup component add clippy-preview` and then `cargo
 +  clippy` to run Clippy. This should put an end to the continuous nightly
 +  upgrades for Clippy users.
 +* Clippy now follows the Rust versioning scheme instead of its own
 +* Fix ICE when encountering a `while let (..) = x.iter()` construct
 +* Fix false positives in `use_self`
 +* Fix false positive in `trivially_copy_pass_by_ref`
 +* Fix false positive in `useless_attribute` lint
 +* Fix false positive in `print_literal`
 +* Fix `use_self` regressions
 +* Improve lint message for `neg_cmp_op_on_partial_ord`
 +* Improve suggestion highlight for `single_char_pattern`
 +* Improve suggestions for various print/write macro lints
 +* Improve website header
 +
 +## 0.0.212 (2018-07-10)
 +* Rustup to *rustc 1.29.0-nightly (e06c87544 2018-07-06)*
 +
 +## 0.0.211
 +* Rustup to *rustc 1.28.0-nightly (e3bf634e0 2018-06-28)*
 +
 +## 0.0.210
 +* Rustup to *rustc 1.28.0-nightly (01cc982e9 2018-06-24)*
 +
 +## 0.0.209
 +* Rustup to *rustc 1.28.0-nightly (523097979 2018-06-18)*
 +
 +## 0.0.208
 +* Rustup to *rustc 1.28.0-nightly (86a8f1a63 2018-06-17)*
 +
 +## 0.0.207
 +* Rustup to *rustc 1.28.0-nightly (2a0062974 2018-06-09)*
 +
 +## 0.0.206
 +* Rustup to *rustc 1.28.0-nightly (5bf68db6e 2018-05-28)*
 +
 +## 0.0.205
 +* Rustup to *rustc 1.28.0-nightly (990d8aa74 2018-05-25)*
 +* Rename `unused_lifetimes` to `extra_unused_lifetimes` because of naming conflict with new rustc lint
 +
 +## 0.0.204
 +* Rustup to *rustc 1.28.0-nightly (71e87be38 2018-05-22)*
 +
 +## 0.0.203
 +* Rustup to *rustc 1.28.0-nightly (a3085756e 2018-05-19)*
 +* Clippy attributes are now of the form `clippy::cyclomatic_complexity` instead of `clippy(cyclomatic_complexity)`
 +
 +## 0.0.202
 +* Rustup to *rustc 1.28.0-nightly (952f344cd 2018-05-18)*
 +
 +## 0.0.201
 +* Rustup to *rustc 1.27.0-nightly (2f2a11dfc 2018-05-16)*
 +
 +## 0.0.200
 +* Rustup to *rustc 1.27.0-nightly (9fae15374 2018-05-13)*
 +
 +## 0.0.199
 +* Rustup to *rustc 1.27.0-nightly (ff2ac35db 2018-05-12)*
 +
 +## 0.0.198
 +* Rustup to *rustc 1.27.0-nightly (acd3871ba 2018-05-10)*
 +
 +## 0.0.197
 +* Rustup to *rustc 1.27.0-nightly (428ea5f6b 2018-05-06)*
 +
 +## 0.0.196
 +* Rustup to *rustc 1.27.0-nightly (e82261dfb 2018-05-03)*
 +
 +## 0.0.195
 +* Rustup to *rustc 1.27.0-nightly (ac3c2288f 2018-04-18)*
 +
 +## 0.0.194
 +* Rustup to *rustc 1.27.0-nightly (bd40cbbe1 2018-04-14)*
 +* New lints: [`cast_ptr_alignment`], [`transmute_ptr_to_ptr`], [`write_literal`], [`write_with_newline`], [`writeln_empty_string`]
 +
 +## 0.0.193
 +* Rustup to *rustc 1.27.0-nightly (eeea94c11 2018-04-06)*
 +
 +## 0.0.192
 +* Rustup to *rustc 1.27.0-nightly (fb44b4c0e 2018-04-04)*
 +* New lint: [`print_literal`]
 +
 +## 0.0.191
 +* Rustup to *rustc 1.26.0-nightly (ae544ee1c 2018-03-29)*
 +* Lint audit; categorize lints as style, correctness, complexity, pedantic, nursery, restriction.
 +
 +## 0.0.190
 +* Fix a bunch of intermittent cargo bugs
 +
 +## 0.0.189
 +* Rustup to *rustc 1.26.0-nightly (5508b2714 2018-03-18)*
 +
 +## 0.0.188
 +* Rustup to *rustc 1.26.0-nightly (392645394 2018-03-15)*
 +* New lint: [`while_immutable_condition`]
 +
 +## 0.0.187
 +* Rustup to *rustc 1.26.0-nightly (322d7f7b9 2018-02-25)*
 +* New lints: [`redundant_field_names`], [`suspicious_arithmetic_impl`], [`suspicious_op_assign_impl`]
 +
 +## 0.0.186
 +* Rustup to *rustc 1.25.0-nightly (0c6091fbd 2018-02-04)*
 +* Various false positive fixes
 +
 +## 0.0.185
 +* Rustup to *rustc 1.25.0-nightly (56733bc9f 2018-02-01)*
 +* New lint: [`question_mark`]
 +
 +## 0.0.184
 +* Rustup to *rustc 1.25.0-nightly (90eb44a58 2018-01-29)*
 +* New lints: [`double_comparisons`], [`empty_line_after_outer_attr`]
 +
 +## 0.0.183
 +* Rustup to *rustc 1.25.0-nightly (21882aad7 2018-01-28)*
 +* New lint: [`misaligned_transmute`]
 +
 +## 0.0.182
 +* Rustup to *rustc 1.25.0-nightly (a0dcecff9 2018-01-24)*
 +* New lint: [`decimal_literal_representation`]
 +
 +## 0.0.181
 +* Rustup to *rustc 1.25.0-nightly (97520ccb1 2018-01-21)*
 +* New lints: [`else_if_without_else`], [`option_option`], [`unit_arg`], [`unnecessary_fold`]
 +* Removed `unit_expr`
 +* Various false positive fixes for [`needless_pass_by_value`]
 +
 +## 0.0.180
 +* Rustup to *rustc 1.25.0-nightly (3f92e8d89 2018-01-14)*
 +
 +## 0.0.179
 +* Rustup to *rustc 1.25.0-nightly (61452e506 2018-01-09)*
 +
 +## 0.0.178
 +* Rustup to *rustc 1.25.0-nightly (ee220daca 2018-01-07)*
 +
 +## 0.0.177
 +* Rustup to *rustc 1.24.0-nightly (250b49205 2017-12-21)*
 +* New lint: [`match_as_ref`]
 +
 +## 0.0.176
 +* Rustup to *rustc 1.24.0-nightly (0077d128d 2017-12-14)*
 +
 +## 0.0.175
 +* Rustup to *rustc 1.24.0-nightly (bb42071f6 2017-12-01)*
 +
 +## 0.0.174
 +* Rustup to *rustc 1.23.0-nightly (63739ab7b 2017-11-21)*
 +
 +## 0.0.173
 +* Rustup to *rustc 1.23.0-nightly (33374fa9d 2017-11-20)*
 +
 +## 0.0.172
 +* Rustup to *rustc 1.23.0-nightly (d0f8e2913 2017-11-16)*
 +
 +## 0.0.171
 +* Rustup to *rustc 1.23.0-nightly (ff0f5de3b 2017-11-14)*
 +
 +## 0.0.170
 +* Rustup to *rustc 1.23.0-nightly (d6b06c63a 2017-11-09)*
 +
 +## 0.0.169
 +* Rustup to *rustc 1.23.0-nightly (3b82e4c74 2017-11-05)*
 +* New lints: [`just_underscores_and_digits`], `result_map_unwrap_or_else`, [`transmute_bytes_to_str`]
 +
 +## 0.0.168
 +* Rustup to *rustc 1.23.0-nightly (f0fe716db 2017-10-30)*
 +
 +## 0.0.167
 +* Rustup to *rustc 1.23.0-nightly (90ef3372e 2017-10-29)*
 +* New lints: `const_static_lifetime`, [`erasing_op`], [`fallible_impl_from`], [`println_empty_string`], [`useless_asref`]
 +
 +## 0.0.166
 +* Rustup to *rustc 1.22.0-nightly (b7960878b 2017-10-18)*
 +* New lints: [`explicit_write`], `identity_conversion`, [`implicit_hasher`], `invalid_ref`, [`option_map_or_none`],
 +  [`range_minus_one`], [`range_plus_one`], [`transmute_int_to_bool`], [`transmute_int_to_char`],
 +  [`transmute_int_to_float`]
 +
 +## 0.0.165
 +* Rust upgrade to rustc 1.22.0-nightly (0e6f4cf51 2017-09-27)
 +* New lint: [`mut_range_bound`]
 +
 +## 0.0.164
 +* Update to *rustc 1.22.0-nightly (6c476ce46 2017-09-25)*
 +* New lint: [`int_plus_one`]
 +
 +## 0.0.163
 +* Update to *rustc 1.22.0-nightly (14039a42a 2017-09-22)*
 +
 +## 0.0.162
 +* Update to *rustc 1.22.0-nightly (0701b37d9 2017-09-18)*
 +* New lint: [`chars_last_cmp`]
 +* Improved suggestions for [`needless_borrow`], [`ptr_arg`],
 +
 +## 0.0.161
 +* Update to *rustc 1.22.0-nightly (539f2083d 2017-09-13)*
 +
 +## 0.0.160
 +* Update to *rustc 1.22.0-nightly (dd08c3070 2017-09-12)*
 +
 +## 0.0.159
 +* Update to *rustc 1.22.0-nightly (eba374fb2 2017-09-11)*
 +* New lint: [`clone_on_ref_ptr`]
 +
 +## 0.0.158
 +* New lint: [`manual_memcpy`]
 +* [`cast_lossless`] no longer has redundant parentheses in its suggestions
 +* Update to *rustc 1.22.0-nightly (dead08cb3 2017-09-08)*
 +
 +## 0.0.157 - 2017-09-04
 +* Update to *rustc 1.22.0-nightly (981ce7d8d 2017-09-03)*
 +* New lint: `unit_expr`
 +
 +## 0.0.156 - 2017-09-03
 +* Update to *rustc 1.22.0-nightly (744dd6c1d 2017-09-02)*
 +
 +## 0.0.155
 +* Update to *rustc 1.21.0-nightly (c11f689d2 2017-08-29)*
 +* New lint: [`infinite_iter`], [`maybe_infinite_iter`], [`cast_lossless`]
 +
 +## 0.0.154
 +* Update to *rustc 1.21.0-nightly (2c0558f63 2017-08-24)*
 +* Fix [`use_self`] triggering inside derives
 +* Add support for linting an entire workspace with `cargo clippy --all`
 +* New lint: [`naive_bytecount`]
 +
 +## 0.0.153
 +* Update to *rustc 1.21.0-nightly (8c303ed87 2017-08-20)*
 +* New lint: [`use_self`]
 +
 +## 0.0.152
 +* Update to *rustc 1.21.0-nightly (df511d554 2017-08-14)*
 +
 +## 0.0.151
 +* Update to *rustc 1.21.0-nightly (13d94d5fa 2017-08-10)*
 +
 +## 0.0.150
 +* Update to *rustc 1.21.0-nightly (215e0b10e 2017-08-08)*
 +
 +## 0.0.148
 +* Update to *rustc 1.21.0-nightly (37c7d0ebb 2017-07-31)*
 +* New lints: [`unreadable_literal`], [`inconsistent_digit_grouping`], [`large_digit_groups`]
 +
 +## 0.0.147
 +* Update to *rustc 1.21.0-nightly (aac223f4f 2017-07-30)*
 +
 +## 0.0.146
 +* Update to *rustc 1.21.0-nightly (52a330969 2017-07-27)*
 +* Fixes false positives in `inline_always`
 +* Fixes false negatives in `panic_params`
 +
 +## 0.0.145
 +* Update to *rustc 1.20.0-nightly (afe145d22 2017-07-23)*
 +
 +## 0.0.144
 +* Update to *rustc 1.20.0-nightly (086eaa78e 2017-07-15)*
 +
 +## 0.0.143
 +* Update to *rustc 1.20.0-nightly (d84693b93 2017-07-09)*
 +* Fix `cargo clippy` crashing on `dylib` projects
 +* Fix false positives around `nested_while_let` and `never_loop`
 +
 +## 0.0.142
 +* Update to *rustc 1.20.0-nightly (067971139 2017-07-02)*
 +
 +## 0.0.141
 +* Rewrite of the `doc_markdown` lint.
 +* Deprecated [`range_step_by_zero`]
 +* New lint: [`iterator_step_by_zero`]
 +* New lint: [`needless_borrowed_reference`]
 +* Update to *rustc 1.20.0-nightly (69c65d296 2017-06-28)*
 +
 +## 0.0.140 - 2017-06-16
 +* Update to *rustc 1.19.0-nightly (258ae6dd9 2017-06-15)*
 +
 +## 0.0.139 — 2017-06-10
 +* Update to *rustc 1.19.0-nightly (4bf5c99af 2017-06-10)*
 +* Fix bugs with for loop desugaring
 +* Check for [`AsRef`]/[`AsMut`] arguments in [`wrong_self_convention`]
 +
 +## 0.0.138 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (0418fa9d3 2017-06-04)*
 +
 +## 0.0.137 — 2017-06-05
 +* Update to *rustc 1.19.0-nightly (6684d176c 2017-06-03)*
 +
 +## 0.0.136 — 2017—05—26
 +* Update to *rustc 1.19.0-nightly (557967766 2017-05-26)*
 +
 +## 0.0.135 — 2017—05—24
 +* Update to *rustc 1.19.0-nightly (5b13bff52 2017-05-23)*
 +
 +## 0.0.134 — 2017—05—19
 +* Update to *rustc 1.19.0-nightly (0ed1ec9f9 2017-05-18)*
 +
 +## 0.0.133 — 2017—05—14
 +* Update to *rustc 1.19.0-nightly (826d8f385 2017-05-13)*
 +
 +## 0.0.132 — 2017—05—05
 +* Fix various bugs and some ices
 +
 +## 0.0.131 — 2017—05—04
 +* Update to *rustc 1.19.0-nightly (2d4ed8e0c 2017-05-03)*
 +
 +## 0.0.130 — 2017—05—03
 +* Update to *rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)*
 +
 +## 0.0.129 — 2017-05-01
 +* Update to *rustc 1.19.0-nightly (06fb4d256 2017-04-30)*
 +
 +## 0.0.128 — 2017-04-28
 +* Update to *rustc 1.18.0-nightly (94e884b63 2017-04-27)*
 +
 +## 0.0.127 — 2017-04-27
 +* Update to *rustc 1.18.0-nightly (036983201 2017-04-26)*
 +* New lint: [`needless_continue`]
 +
 +## 0.0.126 — 2017-04-24
 +* Update to *rustc 1.18.0-nightly (2bd4b5c6d 2017-04-23)*
 +
 +## 0.0.125 — 2017-04-19
 +* Update to *rustc 1.18.0-nightly (9f2abadca 2017-04-18)*
 +
 +## 0.0.124 — 2017-04-16
 +* Update to *rustc 1.18.0-nightly (d5cf1cb64 2017-04-15)*
 +
 +## 0.0.123 — 2017-04-07
 +* Fix various false positives
 +
 +## 0.0.122 — 2017-04-07
 +* Rustup to *rustc 1.18.0-nightly (91ae22a01 2017-04-05)*
 +* New lint: [`op_ref`]
 +
 +## 0.0.121 — 2017-03-21
 +* Rustup to *rustc 1.17.0-nightly (134c4a0f0 2017-03-20)*
 +
 +## 0.0.120 — 2017-03-17
 +* Rustup to *rustc 1.17.0-nightly (0aeb9c129 2017-03-15)*
 +
 +## 0.0.119 — 2017-03-13
 +* Rustup to *rustc 1.17.0-nightly (824c9ebbd 2017-03-12)*
 +
 +## 0.0.118 — 2017-03-05
 +* Rustup to *rustc 1.17.0-nightly (b1e31766d 2017-03-03)*
 +
 +## 0.0.117 — 2017-03-01
 +* Rustup to *rustc 1.17.0-nightly (be760566c 2017-02-28)*
 +
 +## 0.0.116 — 2017-02-28
 +* Fix `cargo clippy` on 64 bit windows systems
 +
 +## 0.0.115 — 2017-02-27
 +* Rustup to *rustc 1.17.0-nightly (60a0edc6c 2017-02-26)*
 +* New lints: [`zero_ptr`], [`never_loop`], [`mut_from_ref`]
 +
 +## 0.0.114 — 2017-02-08
 +* Rustup to *rustc 1.17.0-nightly (c49d10207 2017-02-07)*
 +* Tests are now ui tests (testing the exact output of rustc)
 +
 +## 0.0.113 — 2017-02-04
 +* Rustup to *rustc 1.16.0-nightly (eedaa94e3 2017-02-02)*
 +* New lint: [`large_enum_variant`]
 +* `explicit_into_iter_loop` provides suggestions
 +
 +## 0.0.112 — 2017-01-27
 +* Rustup to *rustc 1.16.0-nightly (df8debf6d 2017-01-25)*
 +
 +## 0.0.111 — 2017-01-21
 +* Rustup to *rustc 1.16.0-nightly (a52da95ce 2017-01-20)*
 +
 +## 0.0.110 — 2017-01-20
 +* Add badges and categories to `Cargo.toml`
 +
 +## 0.0.109 — 2017-01-19
 +* Update to *rustc 1.16.0-nightly (c07a6ae77 2017-01-17)*
 +
 +## 0.0.108 — 2017-01-12
 +* Update to *rustc 1.16.0-nightly (2782e8f8f 2017-01-12)*
 +
 +## 0.0.107 — 2017-01-11
 +* Update regex dependency
 +* Fix FP when matching `&&mut` by `&ref`
 +* Reintroduce `for (_, x) in &mut hash_map` -> `for x in hash_map.values_mut()`
 +* New lints: [`unused_io_amount`], [`forget_ref`], [`short_circuit_statement`]
 +
 +## 0.0.106 — 2017-01-04
 +* Fix FP introduced by rustup in [`wrong_self_convention`]
 +
 +## 0.0.105 — 2017-01-04
 +* Update to *rustc 1.16.0-nightly (468227129 2017-01-03)*
 +* New lints: [`deref_addrof`], [`double_parens`], [`pub_enum_variant_names`]
 +* Fix suggestion in [`new_without_default`]
 +* FP fix in [`absurd_extreme_comparisons`]
 +
 +## 0.0.104 — 2016-12-15
 +* Update to *rustc 1.15.0-nightly (8f02c429a 2016-12-15)*
 +
 +## 0.0.103 — 2016-11-25
 +* Update to *rustc 1.15.0-nightly (d5814b03e 2016-11-23)*
 +
 +## 0.0.102 — 2016-11-24
 +* Update to *rustc 1.15.0-nightly (3bf2be9ce 2016-11-22)*
 +
 +## 0.0.101 — 2016-11-23
 +* Update to *rustc 1.15.0-nightly (7b3eeea22 2016-11-21)*
 +* New lint: [`string_extend_chars`]
 +
 +## 0.0.100 — 2016-11-20
 +* Update to *rustc 1.15.0-nightly (ac635aa95 2016-11-18)*
 +
 +## 0.0.99 — 2016-11-18
 +* Update to rustc 1.15.0-nightly (0ed951993 2016-11-14)
 +* New lint: [`get_unwrap`]
 +
 +## 0.0.98 — 2016-11-08
 +* Fixes an issue due to a change in how cargo handles `--sysroot`, which broke `cargo clippy`
 +
 +## 0.0.97 — 2016-11-03
 +* For convenience, `cargo clippy` defines a `cargo-clippy` feature. This was
 +  previously added for a short time under the name `clippy` but removed for
 +  compatibility.
 +* `cargo clippy --help` is more helping (and less helpful :smile:)
 +* Rustup to *rustc 1.14.0-nightly (5665bdf3e 2016-11-02)*
 +* New lints: [`if_let_redundant_pattern_matching`], [`partialeq_ne_impl`]
 +
 +## 0.0.96 — 2016-10-22
 +* Rustup to *rustc 1.14.0-nightly (f09420685 2016-10-20)*
 +* New lint: [`iter_skip_next`]
 +
 +## 0.0.95 — 2016-10-06
 +* Rustup to *rustc 1.14.0-nightly (3210fd5c2 2016-10-05)*
 +
 +## 0.0.94 — 2016-10-04
 +* Fixes bustage on Windows due to forbidden directory name
 +
 +## 0.0.93 — 2016-10-03
 +* Rustup to *rustc 1.14.0-nightly (144af3e97 2016-10-02)*
 +* `option_map_unwrap_or` and `option_map_unwrap_or_else` are now
 +  allowed by default.
 +* New lint: [`explicit_into_iter_loop`]
 +
 +## 0.0.92 — 2016-09-30
 +* Rustup to *rustc 1.14.0-nightly (289f3a4ca 2016-09-29)*
 +
 +## 0.0.91 — 2016-09-28
 +* Rustup to *rustc 1.13.0-nightly (d0623cf7b 2016-09-26)*
 +
 +## 0.0.90 — 2016-09-09
 +* Rustup to *rustc 1.13.0-nightly (f1f40f850 2016-09-09)*
 +
 +## 0.0.89 — 2016-09-06
 +* Rustup to *rustc 1.13.0-nightly (cbe4de78e 2016-09-05)*
 +
 +## 0.0.88 — 2016-09-04
 +* Rustup to *rustc 1.13.0-nightly (70598e04f 2016-09-03)*
 +* The following lints are not new but were only usable through the `clippy`
 +  lint groups: [`filter_next`], `for_loop_over_option`,
 +  `for_loop_over_result` and [`match_overlapping_arm`]. You should now be
 +  able to `#[allow/deny]` them individually and they are available directly
 +  through `cargo clippy`.
 +
 +## 0.0.87 — 2016-08-31
 +* Rustup to *rustc 1.13.0-nightly (eac41469d 2016-08-30)*
 +* New lints: [`builtin_type_shadow`]
 +* Fix FP in [`zero_prefixed_literal`] and `0b`/`0o`
 +
 +## 0.0.86 — 2016-08-28
 +* Rustup to *rustc 1.13.0-nightly (a23064af5 2016-08-27)*
 +* New lints: [`missing_docs_in_private_items`], [`zero_prefixed_literal`]
 +
 +## 0.0.85 — 2016-08-19
 +* Fix ICE with [`useless_attribute`]
 +* [`useless_attribute`] ignores `unused_imports` on `use` statements
 +
 +## 0.0.84 — 2016-08-18
 +* Rustup to *rustc 1.13.0-nightly (aef6971ca 2016-08-17)*
 +
 +## 0.0.83 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (1bf5fa326 2016-08-16)*
 +* New lints: [`print_with_newline`], [`useless_attribute`]
 +
 +## 0.0.82 — 2016-08-17
 +* Rustup to *rustc 1.12.0-nightly (197be89f3 2016-08-15)*
 +* New lint: [`module_inception`]
 +
 +## 0.0.81 — 2016-08-14
 +* Rustup to *rustc 1.12.0-nightly (1deb02ea6 2016-08-12)*
 +* New lints: [`eval_order_dependence`], [`mixed_case_hex_literals`], [`unseparated_literal_suffix`]
 +* False positive fix in [`too_many_arguments`]
 +* Addition of functionality to [`needless_borrow`]
 +* Suggestions for [`clone_on_copy`]
 +* Bug fix in [`wrong_self_convention`]
 +* Doc improvements
 +
 +## 0.0.80 — 2016-07-31
 +* Rustup to *rustc 1.12.0-nightly (1225e122f 2016-07-30)*
 +* New lints: [`misrefactored_assign_op`], [`serde_api_misuse`]
 +
 +## 0.0.79 — 2016-07-10
 +* Rustup to *rustc 1.12.0-nightly (f93aaf84c 2016-07-09)*
 +* Major suggestions refactoring
 +
 +## 0.0.78 — 2016-07-02
 +* Rustup to *rustc 1.11.0-nightly (01411937f 2016-07-01)*
 +* New lints: [`wrong_transmute`], [`double_neg`], [`filter_map`]
 +* For compatibility, `cargo clippy` does not defines the `clippy` feature
 +  introduced in 0.0.76 anymore
 +* [`collapsible_if`] now considers `if let`
 +
 +## 0.0.77 — 2016-06-21
 +* Rustup to *rustc 1.11.0-nightly (5522e678b 2016-06-20)*
 +* New lints: `stutter` and [`iter_nth`]
 +
 +## 0.0.76 — 2016-06-10
 +* Rustup to *rustc 1.11.0-nightly (7d2f75a95 2016-06-09)*
 +* `cargo clippy` now automatically defines the `clippy` feature
 +* New lint: [`not_unsafe_ptr_arg_deref`]
 +
 +## 0.0.75 — 2016-06-08
 +* Rustup to *rustc 1.11.0-nightly (763f9234b 2016-06-06)*
 +
 +## 0.0.74 — 2016-06-07
 +* Fix bug with `cargo-clippy` JSON parsing
 +* Add the `CLIPPY_DISABLE_DOCS_LINKS` environment variable to deactivate the
 +  “for further information visit *lint-link*” message.
 +
 +## 0.0.73 — 2016-06-05
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.72 — 2016-06-04
 +* Fix false positives in [`useless_let_if_seq`]
 +
 +## 0.0.71 — 2016-05-31
 +* Rustup to *rustc 1.11.0-nightly (a967611d8 2016-05-30)*
 +* New lint: [`useless_let_if_seq`]
 +
 +## 0.0.70 — 2016-05-28
 +* Rustup to *rustc 1.10.0-nightly (7bddce693 2016-05-27)*
 +* [`invalid_regex`] and [`trivial_regex`] can now warn on `RegexSet::new`,
 +  `RegexBuilder::new` and byte regexes
 +
 +## 0.0.69 — 2016-05-20
 +* Rustup to *rustc 1.10.0-nightly (476fe6eef 2016-05-21)*
 +* [`used_underscore_binding`] has been made `Allow` temporarily
 +
 +## 0.0.68 — 2016-05-17
 +* Rustup to *rustc 1.10.0-nightly (cd6a40017 2016-05-16)*
 +* New lint: [`unnecessary_operation`]
 +
 +## 0.0.67 — 2016-05-12
 +* Rustup to *rustc 1.10.0-nightly (22ac88f1a 2016-05-11)*
 +
 +## 0.0.66 — 2016-05-11
 +* New `cargo clippy` subcommand
 +* New lints: [`assign_op_pattern`], [`assign_ops`], [`needless_borrow`]
 +
 +## 0.0.65 — 2016-05-08
 +* Rustup to *rustc 1.10.0-nightly (62e2b2fb7 2016-05-06)*
 +* New lints: [`float_arithmetic`], [`integer_arithmetic`]
 +
 +## 0.0.64 — 2016-04-26
 +* Rustup to *rustc 1.10.0-nightly (645dd013a 2016-04-24)*
 +* New lints: `temporary_cstring_as_ptr`, [`unsafe_removed_from_name`], and [`mem_forget`]
 +
 +## 0.0.63 — 2016-04-08
 +* Rustup to *rustc 1.9.0-nightly (7979dd608 2016-04-07)*
 +
 +## 0.0.62 — 2016-04-07
 +* Rustup to *rustc 1.9.0-nightly (bf5da36f1 2016-04-06)*
 +
 +## 0.0.61 — 2016-04-03
 +* Rustup to *rustc 1.9.0-nightly (5ab11d72c 2016-04-02)*
 +* New lint: [`invalid_upcast_comparisons`]
 +
 +## 0.0.60 — 2016-04-01
 +* Rustup to *rustc 1.9.0-nightly (e1195c24b 2016-03-31)*
 +
 +## 0.0.59 — 2016-03-31
 +* Rustup to *rustc 1.9.0-nightly (30a3849f2 2016-03-30)*
 +* New lints: [`logic_bug`], [`nonminimal_bool`]
 +* Fixed: [`match_same_arms`] now ignores arms with guards
 +* Improved: [`useless_vec`] now warns on `for … in vec![…]`
 +
 +## 0.0.58 — 2016-03-27
 +* Rustup to *rustc 1.9.0-nightly (d5a91e695 2016-03-26)*
 +* New lint: [`doc_markdown`]
 +
 +## 0.0.57 — 2016-03-27
 +* Update to *rustc 1.9.0-nightly (a1e29daf1 2016-03-25)*
 +* Deprecated lints: [`str_to_string`], [`string_to_string`], [`unstable_as_slice`], [`unstable_as_mut_slice`]
 +* New lint: [`crosspointer_transmute`]
 +
 +## 0.0.56 — 2016-03-23
 +* Update to *rustc 1.9.0-nightly (0dcc413e4 2016-03-22)*
 +* New lints: [`many_single_char_names`] and [`similar_names`]
 +
 +## 0.0.55 — 2016-03-21
 +* Update to *rustc 1.9.0-nightly (02310fd31 2016-03-19)*
 +
 +## 0.0.54 — 2016-03-16
 +* Update to *rustc 1.9.0-nightly (c66d2380a 2016-03-15)*
 +
 +## 0.0.53 — 2016-03-15
 +* Add a [configuration file]
 +
 +## ~~0.0.52~~
 +
 +## 0.0.51 — 2016-03-13
 +* Add `str` to types considered by [`len_zero`]
 +* New lints: [`indexing_slicing`]
 +
 +## 0.0.50 — 2016-03-11
 +* Update to *rustc 1.9.0-nightly (c9629d61c 2016-03-10)*
 +
 +## 0.0.49 — 2016-03-09
 +* Update to *rustc 1.9.0-nightly (eabfc160f 2016-03-08)*
 +* New lints: [`overflow_check_conditional`], `unused_label`, [`new_without_default`]
 +
 +## 0.0.48 — 2016-03-07
 +* Fixed: ICE in [`needless_range_loop`] with globals
 +
 +## 0.0.47 — 2016-03-07
 +* Update to *rustc 1.9.0-nightly (998a6720b 2016-03-07)*
 +* New lint: [`redundant_closure_call`]
 +
 +[`AsMut`]: https://doc.rust-lang.org/std/convert/trait.AsMut.html
 +[`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 +[configuration file]: ./rust-clippy#configuration
 +[pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
 +[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
 +[`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 +
 +<!-- lint disable no-unused-definitions -->
 +<!-- begin autogenerated links to lint list -->
 +[`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
 +[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
 +[`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
 +[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
 +[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 +[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
 +[`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic
 +[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
 +[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
 +[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
 +[`assertions_on_result_states`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_result_states
 +[`assign_op_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_op_pattern
 +[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
 +[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
 +[`await_holding_invalid_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_invalid_type
 +[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
 +[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
 +[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
 +[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
 +[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
 +[`blanket_clippy_restriction_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#blanket_clippy_restriction_lints
 +[`block_in_if_condition_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_expr
 +[`block_in_if_condition_stmt`]: https://rust-lang.github.io/rust-clippy/master/index.html#block_in_if_condition_stmt
 +[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
 +[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
 +[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
 +[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
 +[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
 +[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
 +[`borrowed_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrowed_box
 +[`box_collection`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_collection
 +[`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec
 +[`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 +[`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 +[`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
 +[`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 +[`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 +[`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 +[`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
 +[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 +[`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 +[`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 +[`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
 +[`cast_possible_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_truncation
 +[`cast_possible_wrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_possible_wrap
 +[`cast_precision_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_precision_loss
 +[`cast_ptr_alignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ptr_alignment
 +[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
 +[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 +[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
++[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
 +[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 +[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 +[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
 +[`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions
 +[`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref
 +[`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy
 +[`clone_on_ref_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_ref_ptr
 +[`cloned_instead_of_copied`]: https://rust-lang.github.io/rust-clippy/master/index.html#cloned_instead_of_copied
 +[`cmp_nan`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_nan
 +[`cmp_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_null
 +[`cmp_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cmp_owned
 +[`cognitive_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity
 +[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
 +[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
 +[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
++[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
 +[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 +[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 +[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
 +[`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
 +[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
 +[`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 +[`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 +[`cyclomatic_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#cyclomatic_complexity
 +[`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
 +[`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 +[`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 +[`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
 +[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 +[`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 +[`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 +[`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
 +[`deprecated_cfg_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_cfg_attr
 +[`deprecated_semver`]: https://rust-lang.github.io/rust-clippy/master/index.html#deprecated_semver
 +[`deref_addrof`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_addrof
 +[`deref_by_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#deref_by_slicing
 +[`derivable_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#derivable_impls
 +[`derive_hash_xor_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_hash_xor_eq
 +[`derive_ord_xor_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_ord_xor_partial_ord
 +[`derive_partial_eq_without_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#derive_partial_eq_without_eq
 +[`disallowed_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_method
 +[`disallowed_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods
 +[`disallowed_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names
 +[`disallowed_script_idents`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_script_idents
 +[`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 +[`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 +[`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
 +[`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 +[`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 +[`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
 +[`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use
 +[`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 +[`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 +[`drop_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_bounds
 +[`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
 +[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
 +[`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 +[`duplicate_mod`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_mod
 +[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 +[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
 +[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
 +[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
 +[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 +[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 +[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
 +[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 +[`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 +[`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 +[`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 +[`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 +[`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 +[`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
 +[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 +[`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 +[`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 +[`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
 +[`exhaustive_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_structs
 +[`exit`]: https://rust-lang.github.io/rust-clippy/master/index.html#exit
 +[`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 +[`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 +[`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
 +[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
 +[`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 +[`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 +[`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
 +[`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop
 +[`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write
 +[`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice
 +[`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain
 +[`extra_unused_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#extra_unused_lifetimes
 +[`fallible_impl_from`]: https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from
 +[`field_reassign_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
 +[`filetype_is_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#filetype_is_file
 +[`filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map
 +[`filter_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_identity
 +[`filter_map_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next
 +[`filter_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#filter_next
 +[`find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#find_map
 +[`flat_map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_identity
 +[`flat_map_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#flat_map_option
 +[`float_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_arithmetic
 +[`float_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp
 +[`float_cmp_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_cmp_const
 +[`float_equality_without_abs`]: https://rust-lang.github.io/rust-clippy/master/index.html#float_equality_without_abs
 +[`fn_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_address_comparisons
 +[`fn_params_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_params_excessive_bools
 +[`fn_to_numeric_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast
 +[`fn_to_numeric_cast_any`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_any
 +[`fn_to_numeric_cast_with_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#fn_to_numeric_cast_with_truncation
 +[`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 +[`for_loop_over_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_option
 +[`for_loop_over_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loop_over_result
 +[`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 +[`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
 +[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 +[`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 +[`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
 +[`format_push_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string
 +[`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
 +[`from_over_into`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into
 +[`from_str_radix_10`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_str_radix_10
 +[`future_not_send`]: https://rust-lang.github.io/rust-clippy/master/index.html#future_not_send
 +[`get_first`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_first
 +[`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len
 +[`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap
 +[`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion
 +[`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op
 +[`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex
 +[`if_let_redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_redundant_pattern_matching
 +[`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 +[`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 +[`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
 +[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 +[`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 +[`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 +[`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
 +[`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return
 +[`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub
 +[`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
 +[`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 +[`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 +[`index_refutable_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice
 +[`indexing_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#indexing_slicing
 +[`ineffective_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#ineffective_bit_mask
 +[`inefficient_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string
 +[`infallible_destructuring_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#infallible_destructuring_match
 +[`infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#infinite_iter
 +[`inherent_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string
 +[`inherent_to_string_shadow_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#inherent_to_string_shadow_display
 +[`init_numbered_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#init_numbered_fields
 +[`inline_always`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_always
 +[`inline_asm_x86_att_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_att_syntax
 +[`inline_asm_x86_intel_syntax`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_asm_x86_intel_syntax
 +[`inline_fn_without_body`]: https://rust-lang.github.io/rust-clippy/master/index.html#inline_fn_without_body
 +[`inspect_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#inspect_for_each
 +[`int_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#int_plus_one
 +[`integer_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_arithmetic
 +[`integer_division`]: https://rust-lang.github.io/rust-clippy/master/index.html#integer_division
 +[`into_iter_on_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_array
 +[`into_iter_on_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref
 +[`invalid_atomic_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_atomic_ordering
 +[`invalid_null_ptr_usage`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_null_ptr_usage
 +[`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
 +[`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 +[`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
 +[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
 +[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 +[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 +[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
 +[`iter_cloned_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_cloned_collect
 +[`iter_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_count
 +[`iter_next_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_loop
 +[`iter_next_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_next_slice
 +[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
 +[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
 +[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
++[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
++[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
 +[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
 +[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
 +[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
 +[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
 +[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
 +[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
 +[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
 +[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
 +[`large_include_file`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_include_file
 +[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
 +[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
 +[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
 +[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
 +[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
 +[`let_underscore_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_drop
 +[`let_underscore_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_lock
 +[`let_underscore_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_underscore_must_use
 +[`let_unit_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
 +[`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
 +[`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 +[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
 +[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 +[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 +[`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 +[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 +[`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 +[`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
 +[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
 +[`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 +[`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 +[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
 +[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
 +[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
 +[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 +[`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 +[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
 +[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
 +[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
 +[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 +[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 +[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
++[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
 +[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
 +[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
 +[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
 +[`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
 +[`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 +[`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 +[`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
 +[`map_err_ignore`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_err_ignore
 +[`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 +[`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 +[`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
 +[`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 +[`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 +[`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
 +[`match_on_vec_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_on_vec_items
 +[`match_overlapping_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_overlapping_arm
 +[`match_ref_pats`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_ref_pats
 +[`match_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_result_ok
 +[`match_same_arms`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_same_arms
 +[`match_single_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_single_binding
 +[`match_str_case_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_str_case_mismatch
 +[`match_wild_err_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wild_err_arm
 +[`match_wildcard_for_single_variants`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
 +[`maybe_infinite_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#maybe_infinite_iter
 +[`mem_discriminant_non_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_discriminant_non_enum
 +[`mem_forget`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_forget
 +[`mem_replace_option_with_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_option_with_none
 +[`mem_replace_with_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default
 +[`mem_replace_with_uninit`]: https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_uninit
 +[`min_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#min_max
 +[`misaligned_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#misaligned_transmute
 +[`mismatched_target_os`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatched_target_os
 +[`mismatching_type_param_order`]: https://rust-lang.github.io/rust-clippy/master/index.html#mismatching_type_param_order
 +[`misrefactored_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#misrefactored_assign_op
 +[`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
 +[`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 +[`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 +[`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
 +[`missing_inline_in_public_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_inline_in_public_items
 +[`missing_panics_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_panics_doc
 +[`missing_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_safety_doc
 +[`missing_spin_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_spin_loop
 +[`mistyped_literal_suffixes`]: https://rust-lang.github.io/rust-clippy/master/index.html#mistyped_literal_suffixes
 +[`mixed_case_hex_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_case_hex_literals
 +[`mixed_read_write_in_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#mixed_read_write_in_expression
 +[`mod_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#mod_module_files
 +[`module_inception`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_inception
 +[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
 +[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
 +[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
++[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
 +[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
 +[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
 +[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
 +[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
 +[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
 +[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
 +[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
 +[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
 +[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
 +[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
 +[`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 +[`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 +[`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
 +[`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 +[`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 +[`needless_borrow`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow
 +[`needless_borrowed_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
 +[`needless_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_collect
 +[`needless_continue`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_continue
 +[`needless_doctest_main`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_doctest_main
 +[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
 +[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
 +[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
 +[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
 +[`needless_option_as_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_as_deref
 +[`needless_option_take`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_option_take
 +[`needless_parens_on_range_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_parens_on_range_literals
 +[`needless_pass_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_value
 +[`needless_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_question_mark
 +[`needless_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_range_loop
 +[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
 +[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
 +[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
 +[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
 +[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
 +[`negative_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#negative_feature_names
 +[`never_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#never_loop
 +[`new_ret_no_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_ret_no_self
 +[`new_without_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default
 +[`new_without_default_derive`]: https://rust-lang.github.io/rust-clippy/master/index.html#new_without_default_derive
 +[`no_effect`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect
 +[`no_effect_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_replace
 +[`no_effect_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#no_effect_underscore_binding
 +[`non_ascii_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_ascii_literal
 +[`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions
 +[`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty
 +[`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool
 +[`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options
 +[`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces
 +[`not_unsafe_ptr_arg_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#not_unsafe_ptr_arg_deref
 +[`obfuscated_if_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#obfuscated_if_else
 +[`octal_escapes`]: https://rust-lang.github.io/rust-clippy/master/index.html#octal_escapes
 +[`ok_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#ok_expect
 +[`only_used_in_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#only_used_in_recursion
 +[`op_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#op_ref
 +[`option_and_then_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_and_then_some
 +[`option_as_ref_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref
 +[`option_env_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_env_unwrap
 +[`option_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_expect_used
 +[`option_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_filter_map
 +[`option_if_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
 +[`option_map_or_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_or_none
 +[`option_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unit_fn
 +[`option_map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or
 +[`option_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or_else
 +[`option_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_option
 +[`option_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#option_unwrap_used
 +[`or_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call
 +[`or_then_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#or_then_unwrap
 +[`out_of_bounds_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#out_of_bounds_indexing
 +[`overflow_check_conditional`]: https://rust-lang.github.io/rust-clippy/master/index.html#overflow_check_conditional
 +[`overly_complex_bool_expr`]: https://rust-lang.github.io/rust-clippy/master/index.html#overly_complex_bool_expr
 +[`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 +[`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 +[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
 +[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 +[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
 +[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
 +[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
 +[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
++[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
 +[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
 +[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
 +[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
 +[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal
 +[`print_stderr`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stderr
 +[`print_stdout`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_stdout
 +[`print_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_with_newline
 +[`println_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#println_empty_string
 +[`ptr_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_arg
 +[`ptr_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr
 +[`ptr_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_eq
 +[`ptr_offset_with_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast
 +[`pub_enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_enum_variant_names
 +[`pub_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#pub_use
 +[`question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#question_mark
 +[`range_minus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one
 +[`range_plus_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one
 +[`range_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_step_by_zero
 +[`range_zip_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#range_zip_with_len
 +[`rc_buffer`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer
 +[`rc_clone_in_vec_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_clone_in_vec_init
 +[`rc_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex
 +[`read_zero_byte_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#read_zero_byte_vec
 +[`recursive_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#recursive_format_impl
 +[`redundant_allocation`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_allocation
 +[`redundant_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_clone
 +[`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
 +[`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call
 +[`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls
 +[`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else
 +[`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 +[`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 +[`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 +[`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
 +[`redundant_pub_crate`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pub_crate
 +[`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 +[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 +[`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 +[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 +[`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 +[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
 +[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 +[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
 +[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
 +[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
++[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
 +[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
 +[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
 +[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
 +[`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 +[`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
 +[`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
 +[`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 +[`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 +[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
 +[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
 +[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
 +[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
 +[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
 +[`self_named_module_files`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_module_files
 +[`semicolon_if_nothing_returned`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_if_nothing_returned
 +[`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
 +[`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
 +[`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 +[`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 +[`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
 +[`short_circuit_statement`]: https://rust-lang.github.io/rust-clippy/master/index.html#short_circuit_statement
 +[`should_assert_eq`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_assert_eq
 +[`should_implement_trait`]: https://rust-lang.github.io/rust-clippy/master/index.html#should_implement_trait
 +[`significant_drop_in_scrutinee`]: https://rust-lang.github.io/rust-clippy/master/index.html#significant_drop_in_scrutinee
 +[`similar_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#similar_names
 +[`single_char_add_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_add_str
 +[`single_char_lifetime_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_lifetime_names
 +[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
 +[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
 +[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
 +[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
 +[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
 +[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
 +[`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count
 +[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 +[`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 +[`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
 +[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
 +[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
 +[`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 +[`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 +[`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
 +[`string_extend_chars`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_extend_chars
 +[`string_from_utf8_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_from_utf8_as_bytes
 +[`string_lit_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_lit_as_bytes
 +[`string_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_slice
 +[`string_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_to_string
 +[`strlen_on_c_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#strlen_on_c_strings
 +[`struct_excessive_bools`]: https://rust-lang.github.io/rust-clippy/master/index.html#struct_excessive_bools
 +[`stutter`]: https://rust-lang.github.io/rust-clippy/master/index.html#stutter
 +[`suboptimal_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#suboptimal_flops
 +[`suspicious_arithmetic_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl
 +[`suspicious_assignment_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_assignment_formatting
 +[`suspicious_else_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_else_formatting
 +[`suspicious_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_map
 +[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
 +[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
 +[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
++[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
 +[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
 +[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
 +[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
 +[`temporary_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_assignment
 +[`temporary_cstring_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#temporary_cstring_as_ptr
 +[`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 +[`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 +[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 +[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 +[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 +[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 +[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
 +[`trailing_empty_array`]: https://rust-lang.github.io/rust-clippy/master/index.html#trailing_empty_array
 +[`trait_duplication_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#trait_duplication_in_bounds
 +[`transmute_bytes_to_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_bytes_to_str
 +[`transmute_float_to_int`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_float_to_int
 +[`transmute_int_to_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_bool
 +[`transmute_int_to_char`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_char
 +[`transmute_int_to_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_int_to_float
 +[`transmute_num_to_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_num_to_bytes
 +[`transmute_ptr_to_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ptr
 +[`transmute_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref
 +[`transmute_undefined_repr`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmute_undefined_repr
 +[`transmutes_expressible_as_ptr_casts`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmutes_expressible_as_ptr_casts
 +[`transmuting_null`]: https://rust-lang.github.io/rust-clippy/master/index.html#transmuting_null
 +[`trim_split_whitespace`]: https://rust-lang.github.io/rust-clippy/master/index.html#trim_split_whitespace
 +[`trivial_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivial_regex
 +[`trivially_copy_pass_by_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref
 +[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
 +[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
 +[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
 +[`undocumented_unsafe_blocks`]: https://rust-lang.github.io/rust-clippy/master/index.html#undocumented_unsafe_blocks
 +[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
 +[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
 +[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
 +[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
 +[`uninit_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_vec
 +[`unit_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg
 +[`unit_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_cmp
 +[`unit_hash`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_hash
 +[`unit_return_expecting_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#unit_return_expecting_ord
 +[`unknown_clippy_lints`]: https://rust-lang.github.io/rust-clippy/master/index.html#unknown_clippy_lints
 +[`unnecessary_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_cast
 +[`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map
 +[`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map
 +[`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold
 +[`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join
 +[`unnecessary_lazy_evaluations`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations
 +[`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 +[`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 +[`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
 +[`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
 +[`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by
 +[`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned
 +[`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
 +[`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps
 +[`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern
 +[`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern
 +[`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns
 +[`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable
 +[`unreadable_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreadable_literal
 +[`unsafe_derive_deserialize`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_derive_deserialize
 +[`unsafe_removed_from_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_removed_from_name
 +[`unsafe_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsafe_vector_initialization
 +[`unseparated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#unseparated_literal_suffix
 +[`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute
 +[`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice
 +[`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice
 +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async
 +[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
 +[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 +[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
++[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
 +[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 +[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 +[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
 +[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
 +[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
 +[`unwrap_or_else_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_or_else_default
 +[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
 +[`upper_case_acronyms`]: https://rust-lang.github.io/rust-clippy/master/index.html#upper_case_acronyms
 +[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
 +[`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self
 +[`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding
 +[`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref
 +[`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute
 +[`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion
 +[`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format
 +[`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq
 +[`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute
 +[`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec
 +[`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box
 +[`vec_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_init_then_push
 +[`vec_resize_to_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_resize_to_zero
 +[`verbose_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_bit_mask
 +[`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 +[`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 +[`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 +[`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 +[`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
 +[`wildcard_dependencies`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_dependencies
 +[`wildcard_enum_match_arm`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_enum_match_arm
 +[`wildcard_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports
 +[`wildcard_in_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_in_or_patterns
 +[`write_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_literal
 +[`write_with_newline`]: https://rust-lang.github.io/rust-clippy/master/index.html#write_with_newline
 +[`writeln_empty_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#writeln_empty_string
 +[`wrong_pub_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_pub_self_convention
 +[`wrong_self_convention`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_self_convention
 +[`wrong_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#wrong_transmute
 +[`zero_divided_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_divided_by_zero
 +[`zero_prefixed_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_prefixed_literal
 +[`zero_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr
 +[`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values
 +[`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space
 +[`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset
 +<!-- end autogenerated links to lint list -->
index f5c51b9474fcd878cf631441cc03a194d239301b,0000000000000000000000000000000000000000..92b2771f3fe73aeccc27b51c1edd243108f0e35d
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,60 @@@
-     let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
 +//! `bless` updates the reference files in the repo with changed output files
 +//! from the last test run.
 +
 +use crate::cargo_clippy_path;
 +use std::ffi::OsStr;
 +use std::fs;
 +use std::path::{Path, PathBuf};
 +use std::sync::LazyLock;
 +use walkdir::{DirEntry, WalkDir};
 +
 +static CLIPPY_BUILD_TIME: LazyLock<Option<std::time::SystemTime>> =
 +    LazyLock::new(|| cargo_clippy_path().metadata().ok()?.modified().ok());
 +
 +/// # Panics
 +///
 +/// Panics if the path to a test file is broken
 +pub fn bless(ignore_timestamp: bool) {
 +    let extensions = ["stdout", "stderr", "fixed"].map(OsStr::new);
 +
 +    WalkDir::new(build_dir())
 +        .into_iter()
 +        .map(Result::unwrap)
 +        .filter(|entry| entry.path().extension().map_or(false, |ext| extensions.contains(&ext)))
 +        .for_each(|entry| update_reference_file(&entry, ignore_timestamp));
 +}
 +
 +fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
 +    let test_output_path = test_output_entry.path();
 +
 +    let reference_file_name = test_output_entry.file_name().to_str().unwrap().replace(".stage-id", "");
 +    let reference_file_path = Path::new("tests")
 +        .join(test_output_path.strip_prefix(build_dir()).unwrap())
 +        .with_file_name(reference_file_name);
 +
 +    // If the test output was not updated since the last clippy build, it may be outdated
 +    if !ignore_timestamp && !updated_since_clippy_build(test_output_entry).unwrap_or(true) {
 +        return;
 +    }
 +
++    let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
 +    let reference_file = fs::read(&reference_file_path).unwrap_or_default();
 +
 +    if test_output_file != reference_file {
 +        // If a test run caused an output file to change, update the reference file
 +        println!("updating {}", reference_file_path.display());
 +        fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");
 +    }
 +}
 +
 +fn updated_since_clippy_build(entry: &DirEntry) -> Option<bool> {
 +    let clippy_build_time = (*CLIPPY_BUILD_TIME)?;
 +    let modified = entry.metadata().ok()?.modified().ok()?;
 +    Some(modified >= clippy_build_time)
 +}
 +
 +fn build_dir() -> PathBuf {
 +    let mut path = std::env::current_exe().unwrap();
 +    path.set_file_name("test");
 +    path
 +}
index 3b27f061eb0b4f3fc57964044297bc26e8c3a413,0000000000000000000000000000000000000000..357cf6fc43aadada9baabbce3ac08cd70c5d0f93
mode 100644,000000..100644
--- /dev/null
@@@ -1,226 -1,0 +1,226 @@@
-             .contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
 +use crate::clippy_project_root;
 +use itertools::Itertools;
 +use shell_escape::escape;
 +use std::ffi::{OsStr, OsString};
 +use std::path::Path;
 +use std::process::{self, Command, Stdio};
 +use std::{fs, io};
 +use walkdir::WalkDir;
 +
 +#[derive(Debug)]
 +pub enum CliError {
 +    CommandFailed(String, String),
 +    IoError(io::Error),
 +    RustfmtNotInstalled,
 +    WalkDirError(walkdir::Error),
 +    IntellijSetupActive,
 +}
 +
 +impl From<io::Error> for CliError {
 +    fn from(error: io::Error) -> Self {
 +        Self::IoError(error)
 +    }
 +}
 +
 +impl From<walkdir::Error> for CliError {
 +    fn from(error: walkdir::Error) -> Self {
 +        Self::WalkDirError(error)
 +    }
 +}
 +
 +struct FmtContext {
 +    check: bool,
 +    verbose: bool,
 +    rustfmt_path: String,
 +}
 +
 +// the "main" function of cargo dev fmt
 +pub fn run(check: bool, verbose: bool) {
 +    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
 +        let mut success = true;
 +
 +        let project_root = clippy_project_root();
 +
 +        // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
 +        // format because rustfmt would also format the entire rustc repo as it is a local
 +        // dependency
 +        if fs::read_to_string(project_root.join("Cargo.toml"))
 +            .expect("Failed to read clippy Cargo.toml")
-         println!("{}", format_command(&program, &dir, args));
++            .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
 +        {
 +            return Err(CliError::IntellijSetupActive);
 +        }
 +
 +        rustfmt_test(context)?;
 +
 +        success &= cargo_fmt(context, project_root.as_path())?;
 +        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
 +        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
 +        success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
 +
 +        let chunks = WalkDir::new(project_root.join("tests"))
 +            .into_iter()
 +            .filter_map(|entry| {
 +                let entry = entry.expect("failed to find tests");
 +                let path = entry.path();
 +
 +                if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
 +                    None
 +                } else {
 +                    Some(entry.into_path().into_os_string())
 +                }
 +            })
 +            .chunks(250);
 +
 +        for chunk in &chunks {
 +            success &= rustfmt(context, chunk)?;
 +        }
 +
 +        Ok(success)
 +    }
 +
 +    fn output_err(err: CliError) {
 +        match err {
 +            CliError::CommandFailed(command, stderr) => {
 +                eprintln!("error: A command failed! `{}`\nstderr: {}", command, stderr);
 +            },
 +            CliError::IoError(err) => {
 +                eprintln!("error: {}", err);
 +            },
 +            CliError::RustfmtNotInstalled => {
 +                eprintln!("error: rustfmt nightly is not installed.");
 +            },
 +            CliError::WalkDirError(err) => {
 +                eprintln!("error: {}", err);
 +            },
 +            CliError::IntellijSetupActive => {
 +                eprintln!(
 +                    "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
 +Not formatting because that would format the local repo as well!
 +Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
 +                );
 +            },
 +        }
 +    }
 +
 +    let output = Command::new("rustup")
 +        .args(["which", "rustfmt"])
 +        .stderr(Stdio::inherit())
 +        .output()
 +        .expect("error running `rustup which rustfmt`");
 +    if !output.status.success() {
 +        eprintln!("`rustup which rustfmt` did not execute successfully");
 +        process::exit(1);
 +    }
 +    let mut rustfmt_path = String::from_utf8(output.stdout).expect("invalid rustfmt path");
 +    rustfmt_path.truncate(rustfmt_path.trim_end().len());
 +
 +    let context = FmtContext {
 +        check,
 +        verbose,
 +        rustfmt_path,
 +    };
 +    let result = try_run(&context);
 +    let code = match result {
 +        Ok(true) => 0,
 +        Ok(false) => {
 +            eprintln!();
 +            eprintln!("Formatting check failed.");
 +            eprintln!("Run `cargo dev fmt` to update formatting.");
 +            1
 +        },
 +        Err(err) => {
 +            output_err(err);
 +            1
 +        },
 +    };
 +    process::exit(code);
 +}
 +
 +fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
 +    let arg_display: Vec<_> = args.iter().map(|a| escape(a.as_ref().to_string_lossy())).collect();
 +
 +    format!(
 +        "cd {} && {} {}",
 +        escape(dir.as_ref().to_string_lossy()),
 +        escape(program.as_ref().to_string_lossy()),
 +        arg_display.join(" ")
 +    )
 +}
 +
 +fn exec(
 +    context: &FmtContext,
 +    program: impl AsRef<OsStr>,
 +    dir: impl AsRef<Path>,
 +    args: &[impl AsRef<OsStr>],
 +) -> Result<bool, CliError> {
 +    if context.verbose {
 +        println!("{}", format_command(&program, &dir, args));
 +    }
 +
 +    let output = Command::new(&program)
 +        .env("RUSTFMT", &context.rustfmt_path)
 +        .current_dir(&dir)
 +        .args(args.iter())
 +        .output()
 +        .unwrap();
 +    let success = output.status.success();
 +
 +    if !context.check && !success {
 +        let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
 +        return Err(CliError::CommandFailed(
 +            format_command(&program, &dir, args),
 +            String::from(stderr),
 +        ));
 +    }
 +
 +    Ok(success)
 +}
 +
 +fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
 +    let mut args = vec!["fmt", "--all"];
 +    if context.check {
 +        args.push("--check");
 +    }
 +    let success = exec(context, "cargo", path, &args)?;
 +
 +    Ok(success)
 +}
 +
 +fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
 +    let program = "rustfmt";
 +    let dir = std::env::current_dir()?;
 +    let args = &["--version"];
 +
 +    if context.verbose {
-     let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
++        println!("{}", format_command(program, &dir, args));
 +    }
 +
-             format_command(&program, &dir, args),
++    let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
 +
 +    if output.status.success() {
 +        Ok(())
 +    } else if std::str::from_utf8(&output.stderr)
 +        .unwrap_or("")
 +        .starts_with("error: 'rustfmt' is not installed")
 +    {
 +        Err(CliError::RustfmtNotInstalled)
 +    } else {
 +        Err(CliError::CommandFailed(
++            format_command(program, &dir, args),
 +            std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
 +        ))
 +    }
 +}
 +
 +fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> {
 +    let mut args = Vec::new();
 +    if context.check {
 +        args.push(OsString::from("--check"));
 +    }
 +    args.extend(paths);
 +
 +    let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?;
 +
 +    Ok(success)
 +}
index 10a8f31f4573f790d63a4c786037abff9ce40004,0000000000000000000000000000000000000000..be05e67d724dfc120acfec0d1385dda0cd3df741
mode 100644,000000..100644
--- /dev/null
@@@ -1,575 -1,0 +1,575 @@@
-                 String::from("")
 +use crate::clippy_project_root;
 +use indoc::{indoc, writedoc};
 +use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::io::{self, ErrorKind};
 +use std::path::{Path, PathBuf};
 +
 +struct LintData<'a> {
 +    pass: &'a str,
 +    name: &'a str,
 +    category: &'a str,
 +    ty: Option<&'a str>,
 +    project_root: PathBuf,
 +}
 +
 +trait Context {
 +    fn context<C: AsRef<str>>(self, text: C) -> Self;
 +}
 +
 +impl<T> Context for io::Result<T> {
 +    fn context<C: AsRef<str>>(self, text: C) -> Self {
 +        match self {
 +            Ok(t) => Ok(t),
 +            Err(e) => {
 +                let message = format!("{}: {}", 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.
 +pub fn create(
 +    pass: Option<&String>,
 +    lint_name: Option<&String>,
 +    category: Option<&str>,
 +    mut ty: Option<&str>,
 +    msrv: bool,
 +) -> io::Result<()> {
 +    if category == Some("cargo") && ty.is_none() {
 +        // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
 +        ty = Some("cargo");
 +    }
 +
 +    let lint = LintData {
 +        pass: pass.map_or("", String::as_str),
 +        name: lint_name.expect("`name` argument is validated by clap"),
 +        category: category.expect("`category` argument is validated by clap"),
 +        ty,
 +        project_root: clippy_project_root(),
 +    };
 +
 +    create_lint(&lint, msrv).context("Unable to create lint implementation")?;
 +    create_test(&lint).context("Unable to create a test for the new lint")?;
 +
 +    if lint.ty.is_none() {
 +        add_lint(&lint, msrv).context("Unable to add lint to clippy_lints/src/lib.rs")?;
 +    }
 +
 +    Ok(())
 +}
 +
 +fn create_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 +    if let Some(ty) = lint.ty {
 +        create_lint_for_ty(lint, enable_msrv, ty)
 +    } else {
 +        let lint_contents = get_lint_file_contents(lint, enable_msrv);
 +        let lint_path = format!("clippy_lints/src/{}.rs", lint.name);
 +        write_file(lint.project_root.join(&lint_path), lint_contents.as_bytes())?;
 +        println!("Generated lint file: `{}`", lint_path);
 +
 +        Ok(())
 +    }
 +}
 +
 +fn create_test(lint: &LintData<'_>) -> io::Result<()> {
 +    fn create_project_layout<P: Into<PathBuf>>(lint_name: &str, location: P, case: &str, hint: &str) -> io::Result<()> {
 +        let mut path = location.into().join(case);
 +        fs::create_dir(&path)?;
 +        write_file(path.join("Cargo.toml"), get_manifest_contents(lint_name, hint))?;
 +
 +        path.push("src");
 +        fs::create_dir(&path)?;
 +        let header = format!("// compile-flags: --crate-name={}", lint_name);
 +        write_file(path.join("main.rs"), get_test_file_contents(lint_name, Some(&header)))?;
 +
 +        Ok(())
 +    }
 +
 +    if lint.category == "cargo" {
 +        let relative_test_dir = format!("tests/ui-cargo/{}", lint.name);
 +        let test_dir = lint.project_root.join(&relative_test_dir);
 +        fs::create_dir(&test_dir)?;
 +
 +        create_project_layout(lint.name, &test_dir, "fail", "Content that triggers the lint goes here")?;
 +        create_project_layout(lint.name, &test_dir, "pass", "This file should not trigger the lint")?;
 +
 +        println!("Generated test directories: `{relative_test_dir}/pass`, `{relative_test_dir}/fail`");
 +    } else {
 +        let test_path = format!("tests/ui/{}.rs", lint.name);
 +        let test_contents = get_test_file_contents(lint.name, None);
 +        write_file(lint.project_root.join(&test_path), test_contents)?;
 +
 +        println!("Generated test file: `{}`", test_path);
 +    }
 +
 +    Ok(())
 +}
 +
 +fn add_lint(lint: &LintData<'_>, enable_msrv: bool) -> io::Result<()> {
 +    let path = "clippy_lints/src/lib.rs";
 +    let mut lib_rs = fs::read_to_string(path).context("reading")?;
 +
 +    let comment_start = lib_rs.find("// add lints here,").expect("Couldn't find comment");
 +
 +    let new_lint = if enable_msrv {
 +        format!(
 +            "store.register_{lint_pass}_pass(move || Box::new({module_name}::{camel_name}::new(msrv)));\n    ",
 +            lint_pass = lint.pass,
 +            module_name = lint.name,
 +            camel_name = to_camel_case(lint.name),
 +        )
 +    } else {
 +        format!(
 +            "store.register_{lint_pass}_pass(|| Box::new({module_name}::{camel_name}));\n    ",
 +            lint_pass = lint.pass,
 +            module_name = lint.name,
 +            camel_name = to_camel_case(lint.name),
 +        )
 +    };
 +
 +    lib_rs.insert_str(comment_start, &new_lint);
 +
 +    fs::write(path, lib_rs).context("writing")
 +}
 +
 +fn write_file<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
 +    fn inner(path: &Path, contents: &[u8]) -> io::Result<()> {
 +        OpenOptions::new()
 +            .write(true)
 +            .create_new(true)
 +            .open(path)?
 +            .write_all(contents)
 +    }
 +
 +    inner(path.as_ref(), contents.as_ref()).context(format!("writing to file: {}", path.as_ref().display()))
 +}
 +
 +fn to_camel_case(name: &str) -> String {
 +    name.split('_')
 +        .map(|s| {
 +            if s.is_empty() {
++                String::new()
 +            } else {
 +                [&s[0..1].to_uppercase(), &s[1..]].concat()
 +            }
 +        })
 +        .collect()
 +}
 +
 +pub(crate) fn get_stabilization_version() -> String {
 +    fn parse_manifest(contents: &str) -> Option<String> {
 +        let version = contents
 +            .lines()
 +            .filter_map(|l| l.split_once('='))
 +            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
 +        let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
 +            return None;
 +        };
 +        let (minor, patch) = version.split_once('.')?;
 +        Some(format!(
 +            "{}.{}.0",
 +            minor.parse::<u32>().ok()?,
 +            patch.parse::<u32>().ok()?
 +        ))
 +    }
 +    let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
 +    parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
 +}
 +
 +fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
 +    let mut contents = format!(
 +        indoc! {"
 +            #![warn(clippy::{})]
 +
 +            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!(
 +        indoc! {r#"
 +            # {}
 +
 +            [package]
 +            name = "{}"
 +            version = "0.1.0"
 +            publish = false
 +
 +            [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 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
 +        )
 +    });
 +
 +    let _ = write!(result, "{}", get_lint_declaration(&name_upper, 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: 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,
 +            name_upper = name_upper,
 +            name_camel = name_camel,
 +            context_import = context_import,
 +        )
 +    } else {
 +        format!(
 +            indoc! {"
 +                declare_lint_pass!({name_camel} => [{name_upper}]);
 +
 +                impl {pass_type}{pass_lifetimes} for {name_camel} {{}}
 +            "},
 +            pass_type = pass_type,
 +            pass_lifetimes = pass_lifetimes,
 +            name_upper = name_upper,
 +            name_camel = name_camel,
 +        )
 +    });
 +
 +    result
 +}
 +
 +fn get_lint_declaration(name_upper: &str, category: &str) -> String {
 +    format!(
 +        indoc! {r#"
 +            declare_clippy_lint! {{
 +                /// ### What it does
 +                ///
 +                /// ### Why is this bad?
 +                ///
 +                /// ### Example
 +                /// ```rust
 +                /// // example code where clippy issues a warning
 +                /// ```
 +                /// Use instead:
 +                /// ```rust
 +                /// // example code which does not raise clippy warning
 +                /// ```
 +                #[clippy::version = "{version}"]
 +                pub {name_upper},
 +                {category},
 +                "default lint description"
 +            }}
 +        "#},
 +        version = get_stabilization_version(),
 +        name_upper = name_upper,
 +        category = category,
 +    )
 +}
 +
 +fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::Result<()> {
 +    match ty {
 +        "cargo" => assert_eq!(
 +            lint.category, "cargo",
 +            "Lints of type `cargo` must have the `cargo` category"
 +        ),
 +        _ if lint.category == "cargo" => panic!("Lints of category `cargo` must have the `cargo` type"),
 +        _ => {},
 +    }
 +
 +    let ty_dir = lint.project_root.join(format!("clippy_lints/src/{}", ty));
 +    assert!(
 +        ty_dir.exists() && ty_dir.is_dir(),
 +        "Directory `{}` does not exist!",
 +        ty_dir.display()
 +    );
 +
 +    let lint_file_path = ty_dir.join(format!("{}.rs", lint.name));
 +    assert!(
 +        !lint_file_path.exists(),
 +        "File `{}` already exists",
 +        lint_file_path.display()
 +    );
 +
 +    let mod_file_path = ty_dir.join("mod.rs");
 +    let context_import = setup_mod_file(&mod_file_path, lint)?;
 +
 +    let name_upper = lint.name.to_uppercase();
 +    let mut lint_file_contents = String::new();
 +
 +    if enable_msrv {
 +        let _ = writedoc!(
 +            lint_file_contents,
 +            r#"
 +                use clippy_utils::{{meets_msrv, msrvs}};
 +                use rustc_lint::{{{context_import}, LintContext}};
 +                use rustc_semver::RustcVersion;
 +
 +                use super::{name_upper};
 +
 +                // TODO: Adjust the parameters as necessary
 +                pub(super) fn check(cx: &{context_import}, msrv: Option<RustcVersion>) {{
 +                    if !meets_msrv(msrv, todo!("Add a new entry in `clippy_utils/src/msrvs`")) {{
 +                        return;
 +                    }}
 +                    todo!();
 +                }}
 +           "#,
 +            context_import = context_import,
 +            name_upper = name_upper,
 +        );
 +    } else {
 +        let _ = writedoc!(
 +            lint_file_contents,
 +            r#"
 +                use rustc_lint::{{{context_import}, LintContext}};
 +
 +                use super::{name_upper};
 +
 +                // TODO: Adjust the parameters as necessary
 +                pub(super) fn check(cx: &{context_import}) {{
 +                    todo!();
 +                }}
 +           "#,
 +            context_import = context_import,
 +            name_upper = name_upper,
 +        );
 +    }
 +
 +    write_file(lint_file_path.as_path(), lint_file_contents)?;
 +    println!("Generated lint file: `clippy_lints/src/{}/{}.rs`", ty, lint.name);
 +    println!(
 +        "Be sure to add a call to `{}::check` in `clippy_lints/src/{}/mod.rs`!",
 +        lint.name, ty
 +    );
 +
 +    Ok(())
 +}
 +
 +#[allow(clippy::too_many_lines)]
 +fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> {
 +    use super::update_lints::{match_tokens, LintDeclSearchResult};
 +    use rustc_lexer::TokenKind;
 +
 +    let lint_name_upper = lint.name.to_uppercase();
 +
 +    let mut file_contents = fs::read_to_string(path)?;
 +    assert!(
 +        !file_contents.contains(&lint_name_upper),
 +        "Lint `{}` already defined in `{}`",
 +        lint.name,
 +        path.display()
 +    );
 +
 +    let mut offset = 0usize;
 +    let mut last_decl_curly_offset = None;
 +    let mut lint_context = None;
 +
 +    let mut iter = rustc_lexer::tokenize(&file_contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &file_contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
 +    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
 +        let mut iter = iter
 +            .by_ref()
 +            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
 +
 +        match content {
 +            "declare_clippy_lint" => {
 +                // matches `!{`
 +                match_tokens!(iter, Bang OpenBrace);
 +                if let Some(LintDeclSearchResult { range, .. }) =
 +                    iter.find(|result| result.token_kind == TokenKind::CloseBrace)
 +                {
 +                    last_decl_curly_offset = Some(range.end);
 +                }
 +            },
 +            "impl" => {
 +                let mut token = iter.next();
 +                match token {
 +                    // matches <'foo>
 +                    Some(LintDeclSearchResult {
 +                        token_kind: TokenKind::Lt,
 +                        ..
 +                    }) => {
 +                        match_tokens!(iter, Lifetime { .. } Gt);
 +                        token = iter.next();
 +                    },
 +                    None => break,
 +                    _ => {},
 +                }
 +
 +                if let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::Ident,
 +                    content,
 +                    ..
 +                }) = token
 +                {
 +                    // Get the appropriate lint context struct
 +                    lint_context = match content {
 +                        "LateLintPass" => Some("LateContext"),
 +                        "EarlyLintPass" => Some("EarlyContext"),
 +                        _ => continue,
 +                    };
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +
 +    drop(iter);
 +
 +    let last_decl_curly_offset =
 +        last_decl_curly_offset.unwrap_or_else(|| panic!("No lint declarations found in `{}`", path.display()));
 +    let lint_context =
 +        lint_context.unwrap_or_else(|| panic!("No lint pass implementation found in `{}`", path.display()));
 +
 +    // Add the lint declaration to `mod.rs`
 +    file_contents.replace_range(
 +        // Remove the trailing newline, which should always be present
 +        last_decl_curly_offset..=last_decl_curly_offset,
 +        &format!("\n\n{}", get_lint_declaration(&lint_name_upper, lint.category)),
 +    );
 +
 +    // Add the lint to `impl_lint_pass`/`declare_lint_pass`
 +    let impl_lint_pass_start = file_contents.find("impl_lint_pass!").unwrap_or_else(|| {
 +        file_contents
 +            .find("declare_lint_pass!")
 +            .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`/`declare_lint_pass`"))
 +    });
 +
 +    let mut arr_start = file_contents[impl_lint_pass_start..].find('[').unwrap_or_else(|| {
 +        panic!("malformed `impl_lint_pass`/`declare_lint_pass`");
 +    });
 +
 +    arr_start += impl_lint_pass_start;
 +
 +    let mut arr_end = file_contents[arr_start..]
 +        .find(']')
 +        .expect("failed to find `impl_lint_pass` terminator");
 +
 +    arr_end += arr_start;
 +
 +    let mut arr_content = file_contents[arr_start + 1..arr_end].to_string();
 +    arr_content.retain(|c| !c.is_whitespace());
 +
 +    let mut new_arr_content = String::new();
 +    for ident in arr_content
 +        .split(',')
 +        .chain(std::iter::once(&*lint_name_upper))
 +        .filter(|s| !s.is_empty())
 +    {
 +        let _ = write!(new_arr_content, "\n    {},", ident);
 +    }
 +    new_arr_content.push('\n');
 +
 +    file_contents.replace_range(arr_start + 1..arr_end, &new_arr_content);
 +
 +    // Just add the mod declaration at the top, it'll be fixed by rustfmt
 +    file_contents.insert_str(0, &format!("mod {};\n", &lint.name));
 +
 +    let mut file = OpenOptions::new()
 +        .write(true)
 +        .truncate(true)
 +        .open(path)
 +        .context(format!("trying to open: `{}`", path.display()))?;
 +    file.write_all(file_contents.as_bytes())
 +        .context(format!("writing to file: `{}`", path.display()))?;
 +
 +    Ok(lint_context)
 +}
 +
 +#[test]
 +fn test_camel_case() {
 +    let s = "a_lint";
 +    let s2 = to_camel_case(s);
 +    assert_eq!(s2, "ALint");
 +
 +    let name = "a_really_long_new_lint";
 +    let name2 = to_camel_case(name);
 +    assert_eq!(name2, "AReallyLongNewLint");
 +
 +    let name3 = "lint__name";
 +    let name4 = to_camel_case(name3);
 +    assert_eq!(name4, "LintName");
 +}
index 05e79a241884f43bb9175afd3b01b483e6596959,0000000000000000000000000000000000000000..c503142e5e4552b1cdc28877dca9ab3734ba2083
mode 100644,000000..100644
--- /dev/null
@@@ -1,1277 -1,0 +1,1277 @@@
-         if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
 +use crate::clippy_project_root;
 +use aho_corasick::AhoCorasickBuilder;
 +use indoc::writedoc;
 +use itertools::Itertools;
 +use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 +use std::collections::{HashMap, HashSet};
 +use std::ffi::OsStr;
 +use std::fmt::Write;
 +use std::fs::{self, OpenOptions};
 +use std::io::{self, Read, Seek, SeekFrom, Write as _};
 +use std::ops::Range;
 +use std::path::{Path, PathBuf};
 +use walkdir::{DirEntry, WalkDir};
 +
 +const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
 +     // Use that command to update this file and do not edit by hand.\n\
 +     // Manual edits will be overwritten.\n\n";
 +
 +const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 +
 +#[derive(Clone, Copy, PartialEq, Eq)]
 +pub enum UpdateMode {
 +    Check,
 +    Change,
 +}
 +
 +/// Runs the `update_lints` command.
 +///
 +/// This updates various generated values from the lint source code.
 +///
 +/// `update_mode` indicates if the files should be updated or if updates should be checked for.
 +///
 +/// # Panics
 +///
 +/// Panics if a file path could not read from or then written to
 +pub fn update(update_mode: UpdateMode) {
 +    let (lints, deprecated_lints, renamed_lints) = gather_all();
 +    generate_lint_files(update_mode, &lints, &deprecated_lints, &renamed_lints);
 +}
 +
 +fn generate_lint_files(
 +    update_mode: UpdateMode,
 +    lints: &[Lint],
 +    deprecated_lints: &[DeprecatedLint],
 +    renamed_lints: &[RenamedLint],
 +) {
 +    let internal_lints = Lint::internal_lints(lints);
 +    let usable_lints = Lint::usable_lints(lints);
 +    let mut sorted_usable_lints = usable_lints.clone();
 +    sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("book/src/README.md"),
 +        "[There are over ",
 +        " lints included in this crate!]",
 +        |res| {
 +            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
 +        },
 +    );
 +
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("CHANGELOG.md"),
 +        "<!-- begin autogenerated links to lint list -->\n",
 +        "<!-- end autogenerated links to lint list -->",
 +        |res| {
 +            for lint in usable_lints
 +                .iter()
 +                .map(|l| &*l.name)
 +                .chain(deprecated_lints.iter().map(|l| &*l.name))
 +                .chain(
 +                    renamed_lints
 +                        .iter()
 +                        .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
 +                )
 +                .sorted()
 +            {
 +                writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap();
 +            }
 +        },
 +    );
 +
 +    // This has to be in lib.rs, otherwise rustfmt doesn't work
 +    replace_region_in_file(
 +        update_mode,
 +        Path::new("clippy_lints/src/lib.rs"),
 +        "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
 +        "// end lints modules, do not remove this comment, it’s used in `update_lints`",
 +        |res| {
 +            for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
 +                writeln!(res, "mod {};", lint_mod).unwrap();
 +            }
 +        },
 +    );
 +
 +    process_file(
 +        "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),
 +    );
 +
 +    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,
 +        );
 +    }
 +
 +    let content = gen_deprecated_lints_test(deprecated_lints);
 +    process_file("tests/ui/deprecated.rs", update_mode, &content);
 +
 +    let content = gen_renamed_lints_test(renamed_lints);
 +    process_file("tests/ui/rename.rs", update_mode, &content);
 +}
 +
 +pub fn print_lints() {
 +    let (lint_list, _, _) = gather_all();
 +    let usable_lints = Lint::usable_lints(&lint_list);
 +    let usable_lint_count = usable_lints.len();
 +    let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 +
 +    for (lint_group, mut lints) in grouped_by_lint_group {
 +        println!("\n## {}", lint_group);
 +
 +        lints.sort_by_key(|l| l.name.clone());
 +
 +        for lint in lints {
 +            println!("* [{}]({}#{}) ({})", lint.name, DOCS_LINK, lint.name, lint.desc);
 +        }
 +    }
 +
 +    println!("there are {} lints", usable_lint_count);
 +}
 +
 +/// Runs the `rename_lint` command.
 +///
 +/// This does the following:
 +/// * Adds an entry to `renamed_lints.rs`.
 +/// * Renames all lint attributes to the new name (e.g. `#[allow(clippy::lint_name)]`).
 +/// * Renames the lint struct to the new name.
 +/// * Renames the module containing the lint struct to the new name if it shares a name with the
 +///   lint.
 +///
 +/// # Panics
 +/// Panics for the following conditions:
 +/// * If a file path could not read from or then written to
 +/// * If either lint name has a prefix
 +/// * If `old_name` doesn't name an existing lint.
 +/// * If `old_name` names a deprecated or renamed lint.
 +#[allow(clippy::too_many_lines)]
 +pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
 +    if let Some((prefix, _)) = old_name.split_once("::") {
 +        panic!("`{}` should not contain the `{}` prefix", old_name, prefix);
 +    }
 +    if let Some((prefix, _)) = new_name.split_once("::") {
 +        panic!("`{}` should not contain the `{}` prefix", new_name, prefix);
 +    }
 +
 +    let (mut lints, deprecated_lints, mut renamed_lints) = gather_all();
 +    let mut old_lint_index = None;
 +    let mut found_new_name = false;
 +    for (i, lint) in lints.iter().enumerate() {
 +        if lint.name == old_name {
 +            old_lint_index = Some(i);
 +        } else if lint.name == new_name {
 +            found_new_name = true;
 +        }
 +    }
 +    let old_lint_index = old_lint_index.unwrap_or_else(|| panic!("could not find lint `{}`", old_name));
 +
 +    let lint = RenamedLint {
 +        old_name: format!("clippy::{}", old_name),
 +        new_name: if uplift {
 +            new_name.into()
 +        } else {
 +            format!("clippy::{}", new_name)
 +        },
 +    };
 +
 +    // Renamed lints and deprecated lints shouldn't have been found in the lint list, but check just in
 +    // case.
 +    assert!(
 +        !renamed_lints.iter().any(|l| lint.old_name == l.old_name),
 +        "`{}` has already been renamed",
 +        old_name
 +    );
 +    assert!(
 +        !deprecated_lints.iter().any(|l| lint.old_name == l.name),
 +        "`{}` has already been deprecated",
 +        old_name
 +    );
 +
 +    // Update all lint level attributes. (`clippy::lint_name`)
 +    for file in WalkDir::new(clippy_project_root())
 +        .into_iter()
 +        .map(Result::unwrap)
 +        .filter(|f| {
 +            let name = f.path().file_name();
 +            let ext = f.path().extension();
 +            (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
 +                && name != Some(OsStr::new("rename.rs"))
 +                && name != Some(OsStr::new("renamed_lints.rs"))
 +        })
 +    {
 +        rewrite_file(file.path(), |s| {
 +            replace_ident_like(s, &[(&lint.old_name, &lint.new_name)])
 +        });
 +    }
 +
 +    renamed_lints.push(lint);
 +    renamed_lints.sort_by(|lhs, rhs| {
 +        lhs.new_name
 +            .starts_with("clippy::")
 +            .cmp(&rhs.new_name.starts_with("clippy::"))
 +            .reverse()
 +            .then_with(|| lhs.old_name.cmp(&rhs.old_name))
 +    });
 +
 +    write_file(
 +        Path::new("clippy_lints/src/renamed_lints.rs"),
 +        &gen_renamed_lints_list(&renamed_lints),
 +    );
 +
 +    if uplift {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{}` has be uplifted. All the code inside `clippy_lints` related to it needs to be removed manually.",
 +            old_name
 +        );
 +    } else if found_new_name {
 +        write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
 +        println!(
 +            "`{}` is already defined. The old linting code inside `clippy_lints` needs to be updated/removed manually.",
 +            new_name
 +        );
 +    } else {
 +        // Rename the lint struct and source files sharing a name with the lint.
 +        let lint = &mut lints[old_lint_index];
 +        let old_name_upper = old_name.to_uppercase();
 +        let new_name_upper = new_name.to_uppercase();
 +        lint.name = new_name.into();
 +
 +        // Rename test files. only rename `.stderr` and `.fixed` files if the new test name doesn't exist.
 +        if try_rename_file(
 +            Path::new(&format!("tests/ui/{}.rs", old_name)),
 +            Path::new(&format!("tests/ui/{}.rs", new_name)),
 +        ) {
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{}.stderr", old_name)),
 +                Path::new(&format!("tests/ui/{}.stderr", new_name)),
 +            );
 +            try_rename_file(
 +                Path::new(&format!("tests/ui/{}.fixed", old_name)),
 +                Path::new(&format!("tests/ui/{}.fixed", new_name)),
 +            );
 +        }
 +
 +        // Try to rename the file containing the lint if the file name matches the lint's name.
 +        let replacements;
 +        let replacements = if lint.module == old_name
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}.rs", old_name)),
 +                Path::new(&format!("clippy_lints/src/{}.rs", new_name)),
 +            ) {
 +            // Edit the module name in the lint list. Note there could be multiple lints.
 +            for lint in lints.iter_mut().filter(|l| l.module == old_name) {
 +                lint.module = new_name.into();
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else if !lint.module.contains("::")
 +            // Catch cases like `methods/lint_name.rs` where the lint is stored in `methods/mod.rs`
 +            && try_rename_file(
 +                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, old_name)),
 +                Path::new(&format!("clippy_lints/src/{}/{}.rs", lint.module, new_name)),
 +            )
 +        {
 +            // Edit the module name in the lint list. Note there could be multiple lints, or none.
 +            let renamed_mod = format!("{}::{}", lint.module, old_name);
 +            for lint in lints.iter_mut().filter(|l| l.module == renamed_mod) {
 +                lint.module = format!("{}::{}", lint.module, new_name);
 +            }
 +            replacements = [(&*old_name_upper, &*new_name_upper), (old_name, new_name)];
 +            replacements.as_slice()
 +        } else {
 +            replacements = [(&*old_name_upper, &*new_name_upper), ("", "")];
 +            &replacements[0..1]
 +        };
 +
 +        // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
 +        // renamed.
 +        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
 +            rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
 +        }
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("{} has been successfully renamed", old_name);
 +    }
 +
 +    println!("note: `cargo uitest` still needs to be run to update the test results");
 +}
 +
 +const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 +/// Runs the `deprecate` command
 +///
 +/// This does the following:
 +/// * Adds an entry to `deprecated_lints.rs`.
 +/// * Removes the lint declaration (and the entire file if applicable)
 +///
 +/// # Panics
 +///
 +/// If a file path could not read from or written to
 +pub fn deprecate(name: &str, reason: Option<&String>) {
 +    fn finish(
 +        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
 +        name: &str,
 +        reason: &str,
 +    ) {
 +        deprecated_lints.push(DeprecatedLint {
 +            name: name.to_string(),
 +            reason: reason.to_string(),
 +            declaration_range: Range::default(),
 +        });
 +
 +        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
 +        println!("info: `{}` has successfully been deprecated", name);
 +
 +        if reason == DEFAULT_DEPRECATION_REASON {
 +            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
 +        }
 +        println!("note: you must run `cargo uitest` to update the test results");
 +    }
 +
 +    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
 +    let name_lower = name.to_lowercase();
 +    let name_upper = name.to_uppercase();
 +
 +    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
 +    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; };
 +
 +    let mod_path = {
 +        let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
 +        if mod_path.is_dir() {
 +            mod_path = mod_path.join("mod");
 +        }
 +
 +        mod_path.set_extension("rs");
 +        mod_path
 +    };
 +
 +    let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
 +
 +    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
 +        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
 +        finish((lints, deprecated_lints, renamed_lints), name, reason);
 +        return;
 +    }
 +
 +    eprintln!("error: lint not found");
 +}
 +
 +fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
 +    fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
 +        lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
 +    }
 +
 +    fn remove_test_assets(name: &str) {
 +        let test_file_stem = format!("tests/ui/{}", name);
 +        let path = Path::new(&test_file_stem);
 +
 +        // Some lints have their own directories, delete them
 +        if path.is_dir() {
 +            fs::remove_dir_all(path).ok();
 +            return;
 +        }
 +
 +        // Remove all related test files
 +        fs::remove_file(path.with_extension("rs")).ok();
 +        fs::remove_file(path.with_extension("stderr")).ok();
 +        fs::remove_file(path.with_extension("fixed")).ok();
 +    }
 +
 +    fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
 +        let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
 +            content
 +                .find("declare_lint_pass!")
 +                .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
 +        });
 +        let mut impl_lint_pass_end = content[impl_lint_pass_start..]
 +            .find(']')
 +            .expect("failed to find `impl_lint_pass` terminator");
 +
 +        impl_lint_pass_end += impl_lint_pass_start;
-                     fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
++        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
 +            let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
 +            for c in content[lint_name_end..impl_lint_pass_end].chars() {
 +                // Remove trailing whitespace
 +                if c == ',' || c.is_whitespace() {
 +                    lint_name_end += 1;
 +                } else {
 +                    break;
 +                }
 +            }
 +
 +            content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
 +        }
 +    }
 +
 +    if path.exists() {
 +        if let Some(lint) = lints.iter().find(|l| l.name == name) {
 +            if lint.module == name {
 +                // The lint name is the same as the file, we can just delete the entire file
 +                fs::remove_file(path)?;
 +            } else {
 +                // We can't delete the entire file, just remove the declaration
 +
 +                if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
 +                    // Remove clippy_lints/src/some_mod/some_lint.rs
 +                    let mut lint_mod_path = path.to_path_buf();
 +                    lint_mod_path.set_file_name(name);
 +                    lint_mod_path.set_extension("rs");
 +
 +                    fs::remove_file(lint_mod_path).ok();
 +                }
 +
 +                let mut content =
++                    fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
 +
 +                eprintln!(
 +                    "warn: you will have to manually remove any code related to `{}` from `{}`",
 +                    name,
 +                    path.display()
 +                );
 +
 +                assert!(
 +                    content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
 +                    "error: `{}` does not contain lint `{}`'s declaration",
 +                    path.display(),
 +                    lint.name
 +                );
 +
 +                // Remove lint declaration (declare_clippy_lint!)
 +                content.replace_range(lint.declaration_range.clone(), "");
 +
 +                // Remove the module declaration (mod xyz;)
 +                let mod_decl = format!("\nmod {};", name);
 +                content = content.replacen(&mod_decl, "", 1);
 +
 +                remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
 +                fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
 +            }
 +
 +            remove_test_assets(name);
 +            remove_lint(name, lints);
 +            return Ok(true);
 +        }
 +    }
 +
 +    Ok(false)
 +}
 +
 +fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
 +    let mut file = OpenOptions::new().write(true).open(path)?;
 +
 +    file.seek(SeekFrom::End(0))?;
 +
 +    let version = crate::new_lint::get_stabilization_version();
 +    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
 +        "TODO"
 +    } else {
 +        reason
 +    };
 +
 +    writedoc!(
 +        file,
 +        "
 +
 +        declare_deprecated_lint! {{
 +            /// ### What it does
 +            /// Nothing. This lint has been deprecated.
 +            ///
 +            /// ### Deprecation reason
 +            /// {}
 +            #[clippy::version = \"{}\"]
 +            pub {},
 +            \"{}\"
 +        }}
 +
 +        ",
 +        deprecation_reason,
 +        version,
 +        name,
 +        reason,
 +    )
 +}
 +
 +/// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 +/// were no replacements.
 +fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
 +    fn is_ident_char(c: u8) -> bool {
 +        matches!(c, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_')
 +    }
 +
 +    let searcher = AhoCorasickBuilder::new()
 +        .dfa(true)
 +        .match_kind(aho_corasick::MatchKind::LeftmostLongest)
 +        .build_with_size::<u16, _, _>(replacements.iter().map(|&(x, _)| x.as_bytes()))
 +        .unwrap();
 +
 +    let mut result = String::with_capacity(contents.len() + 1024);
 +    let mut pos = 0;
 +    let mut edited = false;
 +    for m in searcher.find_iter(contents) {
 +        let (old, new) = replacements[m.pattern()];
 +        result.push_str(&contents[pos..m.start()]);
 +        result.push_str(
 +            if !is_ident_char(contents.as_bytes().get(m.start().wrapping_sub(1)).copied().unwrap_or(0))
 +                && !is_ident_char(contents.as_bytes().get(m.end()).copied().unwrap_or(0))
 +            {
 +                edited = true;
 +                new
 +            } else {
 +                old
 +            },
 +        );
 +        pos = m.end();
 +    }
 +    result.push_str(&contents[pos..]);
 +    edited.then_some(result)
 +}
 +
 +fn round_to_fifty(count: usize) -> usize {
 +    count / 50 * 50
 +}
 +
 +fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str) {
 +    if update_mode == UpdateMode::Check {
 +        let old_content =
 +            fs::read_to_string(&path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", 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, Eq, Debug)]
 +struct Lint {
 +    name: String,
 +    group: String,
 +    desc: String,
 +    module: String,
 +    declaration_range: Range<usize>,
 +}
 +
 +impl Lint {
 +    #[must_use]
 +    fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            group: group.into(),
 +            desc: remove_line_splices(desc),
 +            module: module.into(),
 +            declaration_range,
 +        }
 +    }
 +
 +    /// Returns all non-deprecated lints and non-internal lints
 +    #[must_use]
 +    fn usable_lints(lints: &[Self]) -> Vec<Self> {
 +        lints
 +            .iter()
 +            .filter(|l| !l.group.starts_with("internal"))
 +            .cloned()
 +            .collect()
 +    }
 +
 +    /// Returns all internal lints (not `internal_warn` lints)
 +    #[must_use]
 +    fn internal_lints(lints: &[Self]) -> Vec<Self> {
 +        lints.iter().filter(|l| l.group == "internal").cloned().collect()
 +    }
 +
 +    /// Returns the lints in a `HashMap`, grouped by the different lint groups
 +    #[must_use]
 +    fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
 +        lints.map(|lint| (lint.group.to_string(), lint)).into_group_map()
 +    }
 +}
 +
 +#[derive(Clone, PartialEq, Eq, Debug)]
 +struct DeprecatedLint {
 +    name: String,
 +    reason: String,
 +    declaration_range: Range<usize>,
 +}
 +impl DeprecatedLint {
 +    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
 +        Self {
 +            name: name.to_lowercase(),
 +            reason: remove_line_splices(reason),
 +            declaration_range,
 +        }
 +    }
 +}
 +
 +struct RenamedLint {
 +    old_name: String,
 +    new_name: String,
 +}
 +impl RenamedLint {
 +    fn new(old_name: &str, new_name: &str) -> Self {
 +        Self {
 +            old_name: remove_line_splices(old_name),
 +            new_name: remove_line_splices(new_name),
 +        }
 +    }
 +}
 +
 +/// Generates the code for registering a group
 +fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
 +    let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
 +    details.sort_unstable();
 +
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +
 +    let _ = writeln!(
 +        output,
 +        "store.register_group(true, \"clippy::{0}\", Some(\"clippy_{0}\"), vec![",
 +        group_name
 +    );
 +    for (module, name) in details {
 +        let _ = writeln!(output, "    LintId::of({}::{}),", module, name);
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +/// Generates the `register_removed` code
 +#[must_use]
 +fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
 +    let mut output = GENERATED_FILE_COMMENT.to_string();
 +    output.push_str("{\n");
 +    for lint in lints {
 +        let _ = write!(
 +            output,
 +            concat!(
 +                "    store.register_removed(\n",
 +                "        \"clippy::{}\",\n",
 +                "        \"{}\",\n",
 +                "    );\n"
 +            ),
 +            lint.name, lint.reason,
 +        );
 +    }
 +    output.push_str("}\n");
 +
 +    output
 +}
 +
 +/// Generates the code for registering lints
 +#[must_use]
 +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\")]\n");
 +        }
 +        let _ = writeln!(output, "    {}::{},", module_name, lint_name);
 +    }
 +    output.push_str("])\n");
 +
 +    output
 +}
 +
 +fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    for lint in lints {
 +        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
 +    let mut seen_lints = HashSet::new();
 +    let mut res: String = GENERATED_FILE_COMMENT.into();
 +    res.push_str("// run-rustfix\n\n");
 +    for lint in lints {
 +        if seen_lints.insert(&lint.new_name) {
 +            writeln!(res, "#![allow({})]", lint.new_name).unwrap();
 +        }
 +    }
 +    seen_lints.clear();
 +    for lint in lints {
 +        if seen_lints.insert(&lint.old_name) {
 +            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
 +        }
 +    }
 +    res.push_str("\nfn main() {}\n");
 +    res
 +}
 +
 +fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
 +    const HEADER: &str = "\
 +        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
 +        #[rustfmt::skip]\n\
 +        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
 +
 +    let mut res = String::from(HEADER);
 +    for lint in lints {
 +        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
 +    }
 +    res.push_str("];\n");
 +    res
 +}
 +
 +/// Gathers all lints defined in `clippy_lints/src`
 +fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
 +    let mut lints = Vec::with_capacity(1000);
 +    let mut deprecated_lints = Vec::with_capacity(50);
 +    let mut renamed_lints = Vec::with_capacity(50);
 +
 +    for (rel_path, file) in clippy_lints_src_files() {
 +        let path = file.path();
 +        let contents =
 +            fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
 +        let module = rel_path
 +            .components()
 +            .map(|c| c.as_os_str().to_str().unwrap())
 +            .collect::<Vec<_>>()
 +            .join("::");
 +
 +        // If the lints are stored in mod.rs, we get the module name from
 +        // the containing directory:
 +        let module = if let Some(module) = module.strip_suffix("::mod.rs") {
 +            module
 +        } else {
 +            module.strip_suffix(".rs").unwrap_or(&module)
 +        };
 +
 +        match module {
 +            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
 +            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
 +            _ => parse_contents(&contents, module, &mut lints),
 +        }
 +    }
 +    (lints, deprecated_lints, renamed_lints)
 +}
 +
 +fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
 +    let root_path = clippy_project_root().join("clippy_lints/src");
 +    let iter = WalkDir::new(&root_path).into_iter();
 +    iter.map(Result::unwrap)
 +        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
 +        .map(move |f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
 +}
 +
 +macro_rules! match_tokens {
 +    ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
 +         {
 +            $($(let $capture =)? if let Some(LintDeclSearchResult {
 +                    token_kind: TokenKind::$token $({$($fields)*})?,
 +                    content: _x,
 +                    ..
 +            }) = $iter.next() {
 +                _x
 +            } else {
 +                continue;
 +            };)*
 +            #[allow(clippy::unused_unit)]
 +            { ($($($capture,)?)*) }
 +        }
 +    }
 +}
 +
 +pub(crate) use match_tokens;
 +
 +pub(crate) struct LintDeclSearchResult<'a> {
 +    pub token_kind: TokenKind,
 +    pub content: &'a str,
 +    pub range: Range<usize>,
 +}
 +
 +/// Parse a source file looking for `declare_clippy_lint` macro invocations.
 +fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
 +    ) {
 +        let start = range.start;
 +
 +        let mut iter = iter
 +            .by_ref()
 +            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
 +        // matches `!{`
 +        match_tokens!(iter, Bang OpenBrace);
 +        match iter.next() {
 +            // #[clippy::version = "version"] pub
 +            Some(LintDeclSearchResult {
 +                token_kind: TokenKind::Pound,
 +                ..
 +            }) => {
 +                match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
 +            },
 +            // pub
 +            Some(LintDeclSearchResult {
 +                token_kind: TokenKind::Ident,
 +                ..
 +            }) => (),
 +            _ => continue,
 +        }
 +
 +        let (name, group, desc) = match_tokens!(
 +            iter,
 +            // LINT_NAME
 +            Ident(name) Comma
 +            // group,
 +            Ident(group) Comma
 +            // "description"
 +            Literal{..}(desc)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(Lint::new(name, group, desc, module, start..range.end));
 +        }
 +    }
 +}
 +
 +/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
 +fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
 +    let mut offset = 0usize;
 +    let mut iter = tokenize(contents).map(|t| {
 +        let range = offset..offset + t.len as usize;
 +        offset = range.end;
 +
 +        LintDeclSearchResult {
 +            token_kind: t.kind,
 +            content: &contents[range.clone()],
 +            range,
 +        }
 +    });
 +
 +    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
 +        |LintDeclSearchResult {
 +             token_kind, content, ..
 +         }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
 +    ) {
 +        let start = range.start;
 +
 +        let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
 +            !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
 +        });
 +        let (name, reason) = match_tokens!(
 +            iter,
 +            // !{
 +            Bang OpenBrace
 +            // #[clippy::version = "version"]
 +            Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
 +            // pub LINT_NAME,
 +            Ident Ident(name) Comma
 +            // "description"
 +            Literal{kind: LiteralKind::Str{..},..}(reason)
 +        );
 +
 +        if let Some(LintDeclSearchResult {
 +            token_kind: TokenKind::CloseBrace,
 +            range,
 +            ..
 +        }) = iter.next()
 +        {
 +            lints.push(DeprecatedLint::new(name, reason, start..range.end));
 +        }
 +    }
 +}
 +
 +fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
 +    for line in contents.lines() {
 +        let mut offset = 0usize;
 +        let mut iter = tokenize(line).map(|t| {
 +            let range = offset..offset + t.len as usize;
 +            offset = range.end;
 +
 +            LintDeclSearchResult {
 +                token_kind: t.kind,
 +                content: &line[range.clone()],
 +                range,
 +            }
 +        });
 +
 +        let (old_name, new_name) = match_tokens!(
 +            iter,
 +            // ("old_name",
 +            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(old_name) Comma
 +            // "new_name"),
 +            Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
 +        );
 +        lints.push(RenamedLint::new(old_name, new_name));
 +    }
 +}
 +
 +/// Removes the line splices and surrounding quotes from a string literal
 +fn remove_line_splices(s: &str) -> String {
 +    let s = s
 +        .strip_prefix('r')
 +        .unwrap_or(s)
 +        .trim_matches('#')
 +        .strip_prefix('"')
 +        .and_then(|s| s.strip_suffix('"'))
 +        .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
 +    let mut res = String::with_capacity(s.len());
 +    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
 +    res
 +}
 +
 +/// Replaces a region in a file delimited by two lines matching regexes.
 +///
 +/// `path` is the relative path to the file on which you want to perform the replacement.
 +///
 +/// See `replace_region_in_text` for documentation of the other options.
 +///
 +/// # Panics
 +///
 +/// Panics if the path could not read or then written
 +fn replace_region_in_file(
 +    update_mode: UpdateMode,
 +    path: &Path,
 +    start: &str,
 +    end: &str,
 +    write_replacement: impl FnMut(&mut String),
 +) {
 +    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
 +    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
 +        Ok(x) => x,
 +        Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()),
 +    };
 +
 +    match update_mode {
 +        UpdateMode::Check if contents != new_contents => exit_with_failure(),
 +        UpdateMode::Check => (),
 +        UpdateMode::Change => {
 +            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
 +                panic!("Cannot write to `{}`: {}", path.display(), e);
 +            }
 +        },
 +    }
 +}
 +
 +/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
 +/// were found, or the missing delimiter if not.
 +fn replace_region_in_text<'a>(
 +    text: &str,
 +    start: &'a str,
 +    end: &'a str,
 +    mut write_replacement: impl FnMut(&mut String),
 +) -> Result<String, &'a str> {
 +    let (text_start, rest) = text.split_once(start).ok_or(start)?;
 +    let (_, text_end) = rest.split_once(end).ok_or(end)?;
 +
 +    let mut res = String::with_capacity(text.len() + 4096);
 +    res.push_str(text_start);
 +    res.push_str(start);
 +    write_replacement(&mut res);
 +    res.push_str(end);
 +    res.push_str(text_end);
 +
 +    Ok(res)
 +}
 +
 +fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
 +    match fs::OpenOptions::new().create_new(true).write(true).open(new_name) {
 +        Ok(file) => drop(file),
 +        Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false,
 +        Err(e) => panic_file(e, new_name, "create"),
 +    };
 +    match fs::rename(old_name, new_name) {
 +        Ok(()) => true,
 +        Err(e) => {
 +            drop(fs::remove_file(new_name));
 +            if e.kind() == io::ErrorKind::NotFound {
 +                false
 +            } else {
 +                panic_file(e, old_name, "rename");
 +            }
 +        },
 +    }
 +}
 +
 +#[allow(clippy::needless_pass_by_value)]
 +fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
 +    panic!("failed to {} file `{}`: {}", action, name.display(), error)
 +}
 +
 +fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
 +    let mut file = fs::OpenOptions::new()
 +        .write(true)
 +        .read(true)
 +        .open(path)
 +        .unwrap_or_else(|e| panic_file(e, path, "open"));
 +    let mut buf = String::new();
 +    file.read_to_string(&mut buf)
 +        .unwrap_or_else(|e| panic_file(e, path, "read"));
 +    if let Some(new_contents) = f(&buf) {
 +        file.rewind().unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.write_all(new_contents.as_bytes())
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +        file.set_len(new_contents.len() as u64)
 +            .unwrap_or_else(|e| panic_file(e, path, "write"));
 +    }
 +}
 +
 +fn write_file(path: &Path, contents: &str) {
 +    fs::write(path, contents).unwrap_or_else(|e| panic_file(e, path, "write"));
 +}
 +
 +#[cfg(test)]
 +mod tests {
 +    use super::*;
 +
 +    #[test]
 +    fn test_parse_contents() {
 +        static CONTENTS: &str = r#"
 +            declare_clippy_lint! {
 +                #[clippy::version = "Hello Clippy!"]
 +                pub PTR_ARG,
 +                style,
 +                "really long \
 +                text"
 +            }
 +
 +            declare_clippy_lint!{
 +                #[clippy::version = "Test version"]
 +                pub DOC_MARKDOWN,
 +                pedantic,
 +                "single line"
 +            }
 +        "#;
 +        let mut result = Vec::new();
 +        parse_contents(CONTENTS, "module_name", &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![
 +            Lint::new(
 +                "ptr_arg",
 +                "style",
 +                "\"really long text\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new(
 +                "doc_markdown",
 +                "pedantic",
 +                "\"single line\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +        ];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_parse_deprecated_contents() {
 +        static DEPRECATED_CONTENTS: &str = r#"
 +            /// some doc comment
 +            declare_deprecated_lint! {
 +                #[clippy::version = "I'm a version"]
 +                pub SHOULD_ASSERT_EQ,
 +                "`assert!()` will be more flexible with RFC 2011"
 +            }
 +        "#;
 +
 +        let mut result = Vec::new();
 +        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
 +        for r in &mut result {
 +            r.declaration_range = Range::default();
 +        }
 +
 +        let expected = vec![DeprecatedLint::new(
 +            "should_assert_eq",
 +            "\"`assert!()` will be more flexible with RFC 2011\"",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, result);
 +    }
 +
 +    #[test]
 +    fn test_usable_lints() {
 +        let lints = vec![
 +            Lint::new(
 +                "should_assert_eq2",
 +                "Not Deprecated",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "internal_style",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +        ];
 +        let expected = vec![Lint::new(
 +            "should_assert_eq2",
 +            "Not Deprecated",
 +            "\"abc\"",
 +            "module_name",
 +            Range::default(),
 +        )];
 +        assert_eq!(expected, Lint::usable_lints(&lints));
 +    }
 +
 +    #[test]
 +    fn test_by_lint_group() {
 +        let lints = vec![
 +            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            ),
 +            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +        ];
 +        let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
 +        expected.insert(
 +            "group1".to_string(),
 +            vec![
 +                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
 +            ],
 +        );
 +        expected.insert(
 +            "group2".to_string(),
 +            vec![Lint::new(
 +                "should_assert_eq2",
 +                "group2",
 +                "\"abc\"",
 +                "module_name",
 +                Range::default(),
 +            )],
 +        );
 +        assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
 +    }
 +
 +    #[test]
 +    fn test_gen_deprecated() {
 +        let lints = vec![
 +            DeprecatedLint::new(
 +                "should_assert_eq",
 +                "\"has been superseded by should_assert_eq2\"",
 +                Range::default(),
 +            ),
 +            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
 +        ];
 +
 +        let expected = GENERATED_FILE_COMMENT.to_string()
 +            + &[
 +                "{",
 +                "    store.register_removed(",
 +                "        \"clippy::should_assert_eq\",",
 +                "        \"has been superseded by should_assert_eq2\",",
 +                "    );",
 +                "    store.register_removed(",
 +                "        \"clippy::another_deprecated\",",
 +                "        \"will be removed\",",
 +                "    );",
 +                "}",
 +            ]
 +            .join("\n")
 +            + "\n";
 +
 +        assert_eq!(expected, gen_deprecated(&lints));
 +    }
 +
 +    #[test]
 +    fn test_gen_lint_group_list() {
 +        let lints = vec![
 +            Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
 +            Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
 +        ];
 +        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 937765b66147912aa6c9172a2b61b4369f91cbec,0000000000000000000000000000000000000000..c4520d003928e3832053fe749045c643407ed2c1
mode 100644,000000..100644
--- /dev/null
@@@ -1,121 -1,0 +1,116 @@@
-     /// fn foo(_x: &str) {}
-     ///
 +use crate::reference::DEREF_ADDROF;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::implements_trait;
 +use clippy_utils::{get_parent_expr, is_lint_allowed};
 +use rustc_errors::Applicability;
 +use rustc_hir::{ExprKind, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `&*(&T)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing and then borrowing a reference value has no effect in most cases.
 +    ///
 +    /// ### Known problems
 +    /// False negative on such code:
 +    /// ```
 +    /// let x = &12;
 +    /// let addr_x = &x as *const _ as usize;
 +    /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
 +    ///                                         // But if we fix it, assert will fail.
 +    /// assert_ne!(addr_x, addr_y);
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust
-     /// foo(&*s);
 +    /// let s = &String::new();
 +    ///
 +    /// let a: &String = &* s;
-     /// # fn foo(_x: &str) {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
-     /// foo(&**s);
 +    /// # let s = &String::new();
 +    /// let a: &String = s;
-     #[clippy::version = "1.59.0"]
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub BORROW_DEREF_REF,
 +    complexity,
 +    "deref on an immutable reference returns the same type as itself"
 +}
 +
 +declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
 +
 +impl LateLintPass<'_> for BorrowDerefRef {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, e: &rustc_hir::Expr<'_>) {
 +        if_chain! {
 +            if !e.span.from_expansion();
 +            if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind;
 +            if !addrof_target.span.from_expansion();
 +            if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind;
 +            if !deref_target.span.from_expansion();
 +            if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) );
 +            let ref_ty = cx.typeck_results().expr_ty(deref_target);
 +            if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind();
 +            then{
 +
 +                if let Some(parent_expr) = get_parent_expr(cx, e){
 +                    if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) &&
 +                       !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
 +                        return;
 +                    }
 +
 +                    // modification to `&mut &*x` is different from `&mut x`
 +                    if matches!(deref_target.kind, ExprKind::Path(..)
 +                                             | ExprKind::Field(..)
 +                                             | ExprKind::Index(..)
 +                                             | ExprKind::Unary(UnOp::Deref, ..))
 +                     && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) {
 +                       return;
 +                    }
 +                }
 +
 +                span_lint_and_then(
 +                    cx,
 +                    BORROW_DEREF_REF,
 +                    e.span,
 +                    "deref on an immutable reference",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            e.span,
 +                            "if you would like to reborrow, try removing `&*`",
 +                            snippet_opt(cx, deref_target.span).unwrap(),
 +                            Applicability::MachineApplicable
 +                        );
 +
 +                        // has deref trait -> give 2 help
 +                        // doesn't have deref trait -> give 1 help
 +                        if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){
 +                            if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
 +                                return;
 +                            }
 +                        }
 +
 +                        diag.span_suggestion(
 +                            e.span,
 +                            "if you would like to deref, try using `&**`",
 +                            format!(
 +                                "&**{}",
 +                                &snippet_opt(cx, deref_target.span).unwrap(),
 +                             ),
 +                            Applicability::MaybeIncorrect
 +                        );
 +
 +                    }
 +                );
 +
 +            }
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..56e894c6261eeb27884db1f098eea1e145aca589
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,25 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, Ty, TyKind};
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++
++use super::AS_UNDERSCORE;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
++    if matches!(ty.kind, TyKind::Infer) {
++        span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
++            let ty_resolved = cx.typeck_results().expr_ty(expr);
++            if let ty::Error(_) = ty_resolved.kind() {
++                diag.help("consider giving the type explicitly");
++            } else {
++                diag.span_suggestion(
++                    ty.span,
++                    "consider giving the type explicitly",
++                    ty_resolved,
++                    Applicability::MachineApplicable,
++                );
++            }
++        });
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6e1f8cd64f077936122944ef2fa61792a356cbf2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::is_no_std_crate;
++use clippy_utils::source::snippet_with_context;
++use rustc_errors::Applicability;
++use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
++use rustc_lint::LateContext;
++
++use super::BORROW_AS_PTR;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    cast_expr: &'tcx Expr<'_>,
++    cast_to: &'tcx Ty<'_>,
++) {
++    if matches!(cast_to.kind, TyKind::Ptr(_))
++        && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
++    {
++        let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
++        let macro_name = match mutability {
++            Mutability::Not => "addr_of",
++            Mutability::Mut => "addr_of_mut",
++        };
++        let mut app = Applicability::MachineApplicable;
++        let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
++
++        span_lint_and_sugg(
++            cx,
++            BORROW_AS_PTR,
++            expr.span,
++            "borrow as raw pointer",
++            "try",
++            format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
++            Applicability::MachineApplicable,
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..284ef165998a7f46cbfaa7b9060cb81dc42fe9a7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{def_id::DefId, Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, Ty};
++use rustc_semver::RustcVersion;
++
++use super::CAST_SLICE_FROM_RAW_PARTS;
++
++enum RawPartsKind {
++    Immutable,
++    Mutable,
++}
++
++fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
++    if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
++        Some(RawPartsKind::Immutable)
++    } else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
++        Some(RawPartsKind::Mutable)
++    } else {
++        None
++    }
++}
++
++pub(super) fn check(
++    cx: &LateContext<'_>,
++    expr: &Expr<'_>,
++    cast_expr: &Expr<'_>,
++    cast_to: Ty<'_>,
++    msrv: Option<RustcVersion>,
++) {
++    if_chain! {
++        if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
++        if let ty::RawPtr(ptrty) = cast_to.kind();
++        if let ty::Slice(_) = ptrty.ty.kind();
++        if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
++        if let ExprKind::Path(ref qpath) = fun.kind;
++        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
++        if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
++        then {
++            let func = match rpk {
++                RawPartsKind::Immutable => "from_raw_parts",
++                RawPartsKind::Mutable => "from_raw_parts_mut"
++            };
++            let span = expr.span;
++            let mut applicability = Applicability::MachineApplicable;
++            let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
++            let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
++            span_lint_and_sugg(
++                cx,
++                CAST_SLICE_FROM_RAW_PARTS,
++                span,
++                &format!("casting the result of `{func}` to {cast_to}"),
++                "replace with",
++                format!("core::ptr::slice_{func}({ptr}, {len})"),
++                applicability
++            );
++        }
++    }
++}
index af3798a0cc8c068520c2efad604a4947fb1c3079,0000000000000000000000000000000000000000..cc5d346b954e3d393e71fa6090c4bcc0839bafa7
mode 100644,000000..100644
--- /dev/null
@@@ -1,588 -1,0 +1,687 @@@
- use clippy_utils::is_hir_ty_cfg_dependant;
++mod as_underscore;
++mod borrow_as_ptr;
 +mod cast_abs_to_unsigned;
 +mod cast_enum_constructor;
 +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 cast_slice_different_sizes;
++mod cast_slice_from_raw_parts;
 +mod char_lit_as_u8;
 +mod fn_to_numeric_cast;
 +mod fn_to_numeric_cast_any;
 +mod fn_to_numeric_cast_with_truncation;
 +mod ptr_as_ptr;
 +mod unnecessary_cast;
 +mod utils;
 +
-     CAST_ABS_TO_UNSIGNED
++use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from any numerical to a float type where
 +    /// the receiving type cannot store all values from the original type without
 +    /// rounding errors. This possible rounding is to be expected, so this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// Basically, this warns on casting any integer with 32 or more bits to `f32`
 +    /// or any 64-bit integer to `f64`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's not bad at all. But in some applications it can be
 +    /// helpful to know where precision loss can take place. This lint can help find
 +    /// those places in the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = u64::MAX;
 +    /// x as f64;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PRECISION_LOSS,
 +    pedantic,
 +    "casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from a signed to an unsigned numerical
 +    /// type. In this case, negative values wrap around to large positive values,
 +    /// which can be quite surprising in practice. However, as the cast works as
 +    /// defined, this lint is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// Possibly surprising results. You can activate this lint
 +    /// as a one-time check to see where numerical wrapping can arise.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let y: i8 = -1;
 +    /// y as u128; // will return 18446744073709551615
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_SIGN_LOSS,
 +    pedantic,
 +    "casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// truncate large values. This is expected behavior, so the cast is `Allow` by
 +    /// default.
 +    ///
 +    /// ### Why is this bad?
 +    /// In some problem domains, it is good practice to avoid
 +    /// truncation. This lint can be activated to help assess where additional
 +    /// checks could be beneficial.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u8(x: u64) -> u8 {
 +    ///     x as u8
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_TRUNCATION,
 +    pedantic,
 +    "casts that may cause truncation of the value, e.g., `x as u8` where `x: u32`, or `x as i32` where `x: f32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an unsigned type to a signed type of
 +    /// the same size. Performing such a cast is a 'no-op' for the compiler,
 +    /// i.e., nothing is changed at the bit level, and the binary representation of
 +    /// the value is reinterpreted. This can cause wrapping if the value is too big
 +    /// for the target signed type. However, the cast works as defined, so this lint
 +    /// is `Allow` by default.
 +    ///
 +    /// ### Why is this bad?
 +    /// While such a cast is not bad in itself, the results can
 +    /// be surprising when this is not the intended behavior, as demonstrated by the
 +    /// example below.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// u32::MAX as i32; // will yield a value of `-1`
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_POSSIBLE_WRAP,
 +    pedantic,
 +    "casts that may cause wrapping around the value, e.g., `x as i32` where `x: u32` and `x > i32::MAX`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts between numerical types that may
 +    /// be replaced by safe conversion functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// Rust's `as` keyword will perform many kinds of
 +    /// conversions, including silently lossy conversions. Conversion functions such
 +    /// as `i32::from` will only perform lossless conversions. Using the conversion
 +    /// functions prevents conversions from turning into silent lossy conversions if
 +    /// the types of the input expressions ever change, and make it easier for
 +    /// people reading the code to know that the conversion is lossless.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     x as u64
 +    /// }
 +    /// ```
 +    ///
 +    /// Using `::from` would look like this:
 +    ///
 +    /// ```rust
 +    /// fn as_u64(x: u8) -> u64 {
 +    ///     u64::from(x)
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_LOSSLESS,
 +    pedantic,
 +    "casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts to the same type, casts of int literals to integer types
 +    /// and casts of float literals to float types.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's just unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = 2i32 as i32;
 +    /// let _ = 0.5 as f32;
 +    /// ```
 +    ///
 +    /// Better:
 +    ///
 +    /// ```rust
 +    /// let _ = 2_i32;
 +    /// let _ = 0.5_f32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_CAST,
 +    complexity,
 +    "cast to the same type, e.g., `x as i32` where `x: i32`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts, using `as` or `pointer::cast`,
 +    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing the resulting pointer may be undefined
 +    /// behavior.
 +    ///
 +    /// ### Known problems
 +    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
 +    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
 +    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (&1u8 as *const u8) as *const u16;
 +    /// let _ = (&mut 1u8 as *mut u8) as *mut u16;
 +    ///
 +    /// (&1u8 as *const u8).cast::<u16>();
 +    /// (&mut 1u8 as *mut u8).cast::<u16>();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CAST_PTR_ALIGNMENT,
 +    pedantic,
 +    "cast from a pointer to a more-strictly-aligned pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of function pointers to something other than usize
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to anything other than usize/isize is not portable across
 +    /// architectures, because you end up losing bits if the target type is too small or end up with a
 +    /// bunch of extra bits that waste space and add more instructions to the final binary than
 +    /// strictly necessary for the problem
 +    ///
 +    /// Casting to isize also doesn't make sense since there are no signed addresses.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun() -> i32 { 1 }
 +    /// let _ = fun as i64;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun() -> i32 { 1 }
 +    /// let _ = fun as usize;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST,
 +    style,
 +    "casting a function pointer to a numeric type other than usize"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to a numeric type not wide enough to
 +    /// store address.
 +    ///
 +    /// ### Why is this bad?
 +    /// Such a cast discards some bits of the function's address. If this is intended, it would be more
 +    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
 +    /// a comment) to perform the truncation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as i32;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // Cast to usize first, then comment with the reason for the truncation
 +    /// fn fn1() -> i16 {
 +    ///     1
 +    /// };
 +    /// let fn_ptr = fn1 as usize;
 +    /// let fn_ptr_truncated = fn_ptr as i32;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    style,
 +    "casting a function pointer to a numeric type not wide enough to store the address"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of a function pointer to any integer type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Casting a function pointer to an integer can have surprising results and can occur
 +    /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
 +    /// low-level with function pointers then you can opt-out of casting functions to integers in
 +    /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
 +    /// pointer casts in your code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // fn1 is cast as `usize`
 +    /// fn fn1() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn1 as usize;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// // maybe you intended to call the function?
 +    /// fn fn2() -> u16 {
 +    ///     1
 +    /// };
 +    /// let _ = fn2() as usize;
 +    ///
 +    /// // or
 +    ///
 +    /// // maybe you intended to cast it to a function type?
 +    /// fn fn3() -> u16 {
 +    ///     1
 +    /// }
 +    /// let _ = fn3 as fn() -> u16;
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FN_TO_NUMERIC_CAST_ANY,
 +    restriction,
 +    "casting a function pointer to any integer type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts of `&T` to `&mut T` anywhere in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// It’s basically guaranteed to be undefined behavior.
 +    /// `UnsafeCell` is the only way to obtain aliasable data that is considered
 +    /// mutable.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// fn x(r: &i32) {
 +    ///     unsafe {
 +    ///         *(r as *const _ as *mut _) += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Instead consider using interior mutability types.
 +    ///
 +    /// ```rust
 +    /// use std::cell::UnsafeCell;
 +    ///
 +    /// fn x(r: &UnsafeCell<i32>) {
 +    ///     unsafe {
 +    ///         *r.get() += 1;
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.33.0"]
 +    pub CAST_REF_TO_MUT,
 +    correctness,
 +    "a cast of reference to a mutable pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions where a character literal is cast
 +    /// to `u8` and suggests using a byte literal instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// In general, casting values to smaller types is
 +    /// error-prone and should be avoided where possible. In the particular case of
 +    /// converting a character literal to u8, it is easy to avoid by just using a
 +    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
 +    /// than `'a' as u8`.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// 'x' as u8
 +    /// ```
 +    ///
 +    /// A better version, using the byte literal:
 +    ///
 +    /// ```rust,ignore
 +    /// b'x'
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHAR_LIT_AS_U8,
 +    complexity,
 +    "casting a character literal to `u8` truncates"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers without changing its mutability,
 +    /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Though `as` casts between raw pointers is not terrible, `pointer::cast` is safer because
 +    /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr as *const i32;
 +    /// let _ = mut_ptr as *mut i32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let ptr: *const u32 = &42_u32;
 +    /// let mut_ptr: *mut u32 = &mut 42_u32;
 +    /// let _ = ptr.cast::<i32>();
 +    /// let _ = mut_ptr.cast::<i32>();
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub PTR_AS_PTR,
 +    pedantic,
 +    "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum type to an integral type which will definitely truncate the
 +    /// value.
 +    ///
 +    /// ### Why is this bad?
 +    /// The resulting integral value will not match the value of the variant it came from.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X = 256 };
 +    /// let _ = E::X as u8;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_ENUM_TRUNCATION,
 +    suspicious,
 +    "casts from an enum type to an integral type which will truncate the value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `as` casts between raw pointers to slices with differently sized elements.
 +    ///
 +    /// ### Why is this bad?
 +    /// The produced raw pointer to a slice does not update its length metadata. The produced
 +    /// pointer will point to a different number of bytes than the original pointer because the
 +    /// length metadata of a raw slice pointer is in elements rather than bytes.
 +    /// Producing a slice reference from the raw pointer will either create a slice with
 +    /// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
 +    ///
 +    /// ### Example
 +    /// // Missing data
 +    /// ```rust
 +    /// let a = [1_i32, 2, 3, 4];
 +    /// let p = &a as *const [i32] as *const [u8];
 +    /// unsafe {
 +    ///     println!("{:?}", &*p);
 +    /// }
 +    /// ```
 +    /// // Undefined Behavior (note: also potential alignment issues)
 +    /// ```rust
 +    /// let a = [1_u8, 2, 3, 4];
 +    /// let p = &a as *const [u8] as *const [u32];
 +    /// unsafe {
 +    ///     println!("{:?}", &*p);
 +    /// }
 +    /// ```
 +    /// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
 +    /// ```rust
 +    /// let a = [1_i32, 2, 3, 4];
 +    /// let old_ptr = &a as *const [i32];
 +    /// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
 +    /// // The length comes from the known length of 4 i32s times the 4 bytes per i32
 +    /// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
 +    /// unsafe {
 +    ///     println!("{:?}", &*new_ptr);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_SLICE_DIFFERENT_SIZES,
 +    correctness,
 +    "casting using `as` between raw pointers to slices of types with different sizes"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for casts from an enum tuple constructor to an integer.
 +    ///
 +    /// ### Why is this bad?
 +    /// The cast is easily confused with casting a c-like enum value to an integer.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// enum E { X(i32) };
 +    /// let _ = E::X as usize;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub CAST_ENUM_CONSTRUCTOR,
 +    suspicious,
 +    "casts from an enum tuple constructor to an integer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for uses of the `abs()` method that cast the result to unsigned.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `unsigned_abs()` method avoids panic when called on the MIN value.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: i32 = -42;
 +    /// let y: u32 = x.abs() as u32;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: i32 = -42;
 +    /// let y: u32 = x.unsigned_abs();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub CAST_ABS_TO_UNSIGNED,
 +    suspicious,
 +    "casting the result of `abs()` to an unsigned integer can panic"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Check for the usage of `as _` conversion using inferred type.
++    ///
++    /// ### Why is this bad?
++    /// The conversion might include lossy conversion and dangerous cast that might go
++    /// undetected due to the type being inferred.
++    ///
++    /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
++    ///
++    /// ### Example
++    /// ```rust
++    /// fn foo(n: usize) {}
++    /// let n: u16 = 256;
++    /// foo(n as _);
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// fn foo(n: usize) {}
++    /// let n: u16 = 256;
++    /// foo(n as usize);
++    /// ```
++    #[clippy::version = "1.63.0"]
++    pub AS_UNDERSCORE,
++    restriction,
++    "detects `as _` conversion"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for the usage of `&expr as *const T` or
++    /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
++    /// `ptr::addr_of_mut` instead.
++    ///
++    /// ### Why is this bad?
++    /// This would improve readability and avoid creating a reference
++    /// that points to an uninitialized value or unaligned place.
++    /// Read the `ptr::addr_of` docs for more information.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let val = 1;
++    /// let p = &val as *const i32;
++    ///
++    /// let mut val_mut = 1;
++    /// let p_mut = &mut val_mut as *mut i32;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let val = 1;
++    /// let p = std::ptr::addr_of!(val);
++    ///
++    /// let mut val_mut = 1;
++    /// let p_mut = std::ptr::addr_of_mut!(val_mut);
++    /// ```
++    #[clippy::version = "1.60.0"]
++    pub BORROW_AS_PTR,
++    pedantic,
++    "borrowing just to cast to a raw pointer"
++}
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for a raw slice being cast to a slice pointer
++    ///
++    /// ### Why is this bad?
++    /// This can result in multiple `&mut` references to the same location when only a pointer is
++    /// required.
++    /// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
++    /// the same [safety requirements] to be upheld.
++    ///
++    /// ### Example
++    /// ```rust,ignore
++    /// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
++    /// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
++    /// ```
++    /// Use instead:
++    /// ```rust,ignore
++    /// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
++    /// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
++    /// ```
++    /// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
++    #[clippy::version = "1.64.0"]
++    pub CAST_SLICE_FROM_RAW_PARTS,
++    suspicious,
++    "casting a slice created from a pointer and length to a slice pointer"
++}
++
 +pub struct Casts {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Casts {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Casts => [
 +    CAST_PRECISION_LOSS,
 +    CAST_SIGN_LOSS,
 +    CAST_POSSIBLE_TRUNCATION,
 +    CAST_POSSIBLE_WRAP,
 +    CAST_LOSSLESS,
 +    CAST_REF_TO_MUT,
 +    CAST_PTR_ALIGNMENT,
 +    CAST_SLICE_DIFFERENT_SIZES,
 +    UNNECESSARY_CAST,
 +    FN_TO_NUMERIC_CAST_ANY,
 +    FN_TO_NUMERIC_CAST,
 +    FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
 +    CHAR_LIT_AS_U8,
 +    PTR_AS_PTR,
 +    CAST_ENUM_TRUNCATION,
 +    CAST_ENUM_CONSTRUCTOR,
-         if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
-             if is_hir_ty_cfg_dependant(cx, cast_to) {
++    CAST_ABS_TO_UNSIGNED,
++    AS_UNDERSCORE,
++    BORROW_AS_PTR,
++    CAST_SLICE_FROM_RAW_PARTS
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Casts {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if !in_external_macro(cx.sess(), expr.span) {
 +            ptr_as_ptr::check(cx, expr, self.msrv);
 +        }
 +
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
++        if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
++            if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
 +                return;
 +            }
 +            let (cast_from, cast_to) = (
 +                cx.typeck_results().expr_ty(cast_expr),
 +                cx.typeck_results().expr_ty(expr),
 +            );
 +
 +            if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
 +                return;
 +            }
++            cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
 +            fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
 +            fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +
 +            if cast_to.is_numeric() && !in_external_macro(cx.sess(), expr.span) {
 +                cast_possible_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
 +                if cast_from.is_numeric() {
 +                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
 +                    cast_precision_loss::check(cx, expr, cast_from, cast_to);
 +                    cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
 +                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                }
 +                cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
 +                cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
 +            }
++
++            as_underscore::check(cx, expr, cast_to_hir);
++
++            if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
++                borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
++            }
 +        }
 +
 +        cast_ref_to_mut::check(cx, expr);
 +        cast_ptr_alignment::check(cx, expr);
 +        char_lit_as_u8::check(cx, expr);
 +        ptr_as_ptr::check(cx, expr, self.msrv);
 +        cast_slice_different_sizes::check(cx, expr, self.msrv);
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index fff7da8e33f2fff24aca3a5760557b58f893ac4a,0000000000000000000000000000000000000000..19d2e6e1d1298e448619d608056a591ba13d88eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,126 -1,0 +1,133 @@@
-         format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::numeric_literal::NumericLiteral;
 +use clippy_utils::source::snippet_opt;
 +use if_chain::if_chain;
 +use rustc_ast::{LitFloatType, LitIntType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
 +use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
 +
 +use super::UNNECESSARY_CAST;
 +
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'tcx>,
 +    cast_expr: &Expr<'tcx>,
 +    cast_from: Ty<'tcx>,
 +    cast_to: Ty<'tcx>,
 +) -> bool {
 +    // skip non-primitive type cast
 +    if_chain! {
 +        if let ExprKind::Cast(_, cast_to) = expr.kind;
 +        if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
 +        if let Res::PrimTy(_) = path.res;
 +        then {}
 +        else {
 +            return false
 +        }
 +    }
 +
 +    if let Some(lit) = get_numeric_literal(cast_expr) {
 +        let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
 +
 +        if_chain! {
 +            if let LitKind::Int(n, _) = lit.node;
 +            if let Some(src) = snippet_opt(cx, cast_expr.span);
 +            if cast_to.is_floating_point();
 +            if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node);
 +            let from_nbits = 128 - n.leading_zeros();
 +            let to_nbits = fp_ty_mantissa_nbits(cast_to);
 +            if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
 +            then {
 +                lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
 +                return true
 +            }
 +        }
 +
 +        match lit.node {
 +            LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
 +                lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
 +            },
 +            LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
 +                lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
 +            },
 +            LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
 +            LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
 +            | LitKind::Float(_, LitFloatType::Suffixed(_))
 +                if cast_from.kind() == cast_to.kind() =>
 +            {
 +                if let Some(src) = snippet_opt(cx, cast_expr.span) {
 +                    if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
 +                        lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
 +                    }
 +                }
 +            },
 +            _ => {
 +                if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNNECESSARY_CAST,
 +                        expr.span,
 +                        &format!(
 +                            "casting to the same type is unnecessary (`{}` -> `{}`)",
 +                            cast_from, cast_to
 +                        ),
 +                        "try",
 +                        literal_str,
 +                        Applicability::MachineApplicable,
 +                    );
 +                    return true;
 +                }
 +            },
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
 +    let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
++    let replaced_literal;
++    let matchless = if literal_str.contains(['(', ')']) {
++        replaced_literal = literal_str.replace(['(', ')'], "");
++        &replaced_literal
++    } else {
++        literal_str
++    };
 +    span_lint_and_sugg(
 +        cx,
 +        UNNECESSARY_CAST,
 +        expr.span,
 +        &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
 +        "try",
++        format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
 +    match expr.kind {
 +        ExprKind::Lit(ref lit) => Some(lit),
 +        ExprKind::Unary(UnOp::Neg, e) => {
 +            if let ExprKind::Lit(ref lit) = e.kind {
 +                Some(lit)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns the mantissa bits wide of a fp type.
 +/// Will return 0 if the type is not a fp
 +fn fp_ty_mantissa_nbits(typ: Ty<'_>) -> u32 {
 +    match typ.kind() {
 +        ty::Float(FloatTy::F32) => 23,
 +        ty::Float(FloatTy::F64) | ty::Infer(InferTy::FloatVar(_)) => 52,
 +        _ => 0,
 +    }
 +}
index 59f10247a11d4c98ea4821a3746ee2479c37aff9,0000000000000000000000000000000000000000..1506ea604f0dd21ab827db4062d57bd9a2939532
mode 100644,000000..100644
--- /dev/null
@@@ -1,1238 -1,0 +1,1486 @@@
- use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
- use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 +use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 +use clippy_utils::sugg::has_enclosing_paren;
-     self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
-     ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
-     TraitItemKind, TyKind, UnOp,
++use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
++use clippy_utils::{
++    fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
++    walk_to_expr_usage,
++};
 +use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 +use rustc_data_structures::fx::FxIndexMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_ty, Visitor};
 +use rustc_hir::{
- use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
++    self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
++    GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
++    Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
 +};
++use rustc_index::bit_set::BitSet;
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
- use rustc_trait_selection::infer::InferCtxtExt;
++use rustc_middle::ty::{
++    self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
++    ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
++};
++use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
-                 let (position, adjustments) = walk_parents(cx, expr);
++use rustc_trait_selection::infer::InferCtxtExt as _;
++use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
++use std::collections::VecDeque;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `deref()` or `deref_mut()` method calls.
 +    ///
 +    /// ### Why is this bad?
 +    /// Dereferencing by `&*x` or `&mut *x` is clearer and more concise,
 +    /// when not part of a method chain.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::ops::Deref;
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b: &str = a.deref();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a: &mut String = &mut String::from("foo");
 +    /// let b = &*a;
 +    /// ```
 +    ///
 +    /// This lint excludes:
 +    /// ```rust,ignore
 +    /// let _ = d.unwrap().deref();
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub EXPLICIT_DEREF_METHODS,
 +    pedantic,
 +    "Explicit use of deref or deref_mut method while not in a method chain."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for address of operations (`&`) that are going to
 +    /// be dereferenced immediately by the compiler.
 +    ///
 +    /// ### Why is this bad?
 +    /// Suggests that the receiver of the expression borrows
 +    /// the expression.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn fun(_a: &i32) {}
 +    ///
 +    /// let x: &i32 = &&&&&&5;
 +    /// fun(&x);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # fn fun(_a: &i32) {}
 +    /// let x: &i32 = &5;
 +    /// fun(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_BORROW,
 +    style,
 +    "taking a reference that is going to be automatically dereferenced"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `ref` bindings which create a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// The address-of operator at the use site is clearer about the need for a reference.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(ref x) = x {
 +    ///     // use `x` here
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some("");
 +    /// if let Some(x) = x {
 +    ///     // use `&x` here
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub REF_BINDING_TO_REFERENCE,
 +    pedantic,
 +    "`ref` binding to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for dereferencing expressions which would be covered by auto-deref.
 +    ///
 +    /// ### Why is this bad?
 +    /// This unnecessarily complicates the code.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &*x;
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = String::new();
 +    /// let y: &str = &x;
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub EXPLICIT_AUTO_DEREF,
 +    complexity,
 +    "dereferencing when the compiler would automatically dereference"
 +}
 +
 +impl_lint_pass!(Dereferencing => [
 +    EXPLICIT_DEREF_METHODS,
 +    NEEDLESS_BORROW,
 +    REF_BINDING_TO_REFERENCE,
 +    EXPLICIT_AUTO_DEREF,
 +]);
 +
 +#[derive(Default)]
 +pub struct Dereferencing {
 +    state: Option<(State, StateData)>,
 +
 +    // While parsing a `deref` method call in ufcs form, the path to the function is itself an
 +    // expression. This is to store the id of that expression so it can be skipped when
 +    // `check_expr` is called for it.
 +    skip_expr: Option<HirId>,
 +
 +    /// The body the first local was found in. Used to emit lints when the traversal of the body has
 +    /// been finished. Note we can't lint at the end of every body as they can be nested within each
 +    /// other.
 +    current_body: Option<BodyId>,
++
 +    /// The list of locals currently being checked by the lint.
 +    /// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
 +    /// This is needed for or patterns where one of the branches can be linted, but another can not
 +    /// be.
 +    ///
 +    /// e.g. `m!(x) | Foo::Bar(ref x)`
 +    ref_locals: FxIndexMap<HirId, Option<RefPat>>,
++
++    // `IntoIterator` for arrays requires Rust 1.53.
++    msrv: Option<RustcVersion>,
++}
++
++impl Dereferencing {
++    #[must_use]
++    pub fn new(msrv: Option<RustcVersion>) -> Self {
++        Self {
++            msrv,
++            ..Dereferencing::default()
++        }
++    }
 +}
 +
 +struct StateData {
 +    /// Span of the top level expression
 +    span: Span,
 +    hir_id: HirId,
 +    position: Position,
 +}
 +
 +struct DerefedBorrow {
 +    count: usize,
 +    msg: &'static str,
++    snip_expr: Option<HirId>,
 +}
 +
 +enum State {
 +    // Any number of deref method calls.
 +    DerefMethod {
 +        // The number of calls in a sequence which changed the referenced type
 +        ty_changed_count: usize,
 +        is_final_ufcs: bool,
 +        /// The required mutability
 +        target_mut: Mutability,
 +    },
 +    DerefedBorrow(DerefedBorrow),
 +    ExplicitDeref {
 +        mutability: Option<Mutability>,
 +    },
 +    ExplicitDerefField {
 +        name: Symbol,
 +    },
 +    Reborrow {
 +        mutability: Mutability,
 +    },
 +    Borrow {
 +        mutability: Mutability,
 +    },
 +}
 +
 +// A reference operation considered by this lint pass
 +enum RefOp {
 +    Method(Mutability),
 +    Deref,
 +    AddrOf(Mutability),
 +}
 +
 +struct RefPat {
 +    /// Whether every usage of the binding is dereferenced.
 +    always_deref: bool,
 +    /// The spans of all the ref bindings for this local.
 +    spans: Vec<Span>,
 +    /// The applicability of this suggestion.
 +    app: Applicability,
 +    /// All the replacements which need to be made.
 +    replacements: Vec<(Span, String)>,
 +    /// The [`HirId`] that the lint should be emitted at.
 +    hir_id: HirId,
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Dereferencing {
 +    #[expect(clippy::too_many_lines)]
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
 +        if Some(expr.hir_id) == self.skip_expr.take() {
 +            return;
 +        }
 +
 +        if let Some(local) = path_to_local(expr) {
 +            self.check_local_usage(cx, expr, local);
 +        }
 +
 +        // Stop processing sub expressions when a macro call is seen
 +        if expr.span.from_expansion() {
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        }
 +
 +        let typeck = cx.typeck_results();
 +        let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
 +            x
 +        } else {
 +            // The whole chain of reference operations has been seen
 +            if let Some((state, data)) = self.state.take() {
 +                report(cx, expr, state, data);
 +            }
 +            return;
 +        };
 +
 +        match (self.state.take(), kind) {
 +            (None, kind) => {
 +                let expr_ty = typeck.expr_ty(expr);
-                         let (required_refs, msg) = if position.can_auto_borrow() {
-                             (1, if deref_count == 1 { borrow_msg } else { deref_msg })
++                let (position, adjustments) = walk_parents(cx, expr, self.msrv);
 +
 +                match kind {
 +                    RefOp::Deref => {
 +                        if let Position::FieldAccess(name) = position
 +                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                        {
 +                            self.state = Some((
 +                                State::ExplicitDerefField { name },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable() {
 +                            self.state = Some((
 +                                State::ExplicitDeref { mutability: None },
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        }
 +                    }
 +                    RefOp::Method(target_mut)
 +                        if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
 +                            && position.lint_explicit_deref() =>
 +                    {
 +                        self.state = Some((
 +                            State::DerefMethod {
 +                                ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
 +                                    0
 +                                } else {
 +                                    1
 +                                },
 +                                is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                                target_mut,
 +                            },
 +                            StateData {
 +                                span: expr.span,
 +                                hir_id: expr.hir_id,
 +                                position
 +                            },
 +                        ));
 +                    },
 +                    RefOp::AddrOf(mutability) => {
 +                        // Find the number of times the borrow is auto-derefed.
 +                        let mut iter = adjustments.iter();
 +                        let mut deref_count = 0usize;
 +                        let next_adjust = loop {
 +                            match iter.next() {
 +                                Some(adjust) => {
 +                                    if !matches!(adjust.kind, Adjust::Deref(_)) {
 +                                        break Some(adjust);
 +                                    } else if !adjust.target.is_ref() {
 +                                        deref_count += 1;
 +                                        break iter.next();
 +                                    }
 +                                    deref_count += 1;
 +                                },
 +                                None => break None,
 +                            };
 +                        };
 +
 +                        // Determine the required number of references before any can be removed. In all cases the
 +                        // reference made by the current expression will be removed. After that there are four cases to
 +                        // handle.
 +                        //
 +                        // 1. Auto-borrow will trigger in the current position, so no further references are required.
 +                        // 2. Auto-deref ends at a reference, or the underlying type, so one extra needs to be left to
 +                        //    handle the automatically inserted re-borrow.
 +                        // 3. Auto-deref hits a user-defined `Deref` impl, so at least one reference needs to exist to
 +                        //    start auto-deref.
 +                        // 4. If the chain of non-user-defined derefs ends with a mutable re-borrow, and re-borrow
 +                        //    adjustments will not be inserted automatically, then leave one further reference to avoid
 +                        //    moving a mutable borrow.
 +                        //    e.g.
 +                        //        fn foo<T>(x: &mut Option<&mut T>, y: &mut T) {
 +                        //            let x = match x {
 +                        //                // Removing the borrow will cause `x` to be moved
 +                        //                Some(x) => &mut *x,
 +                        //                None => y
 +                        //            };
 +                        //        }
 +                        let deref_msg =
 +                            "this expression creates a reference which is immediately dereferenced by the compiler";
 +                        let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
++                        let impl_msg = "the borrowed expression implements the required traits";
 +
-                                 (3, deref_msg)
++                        let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
++                            (1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
++                        } else if let Position::ImplArg(hir_id) = position {
++                            (0, impl_msg, Some(hir_id))
 +                        } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
 +                            next_adjust.map(|a| &a.kind)
 +                        {
 +                            if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
 +                            {
-                                 (2, deref_msg)
++                                (3, deref_msg, None)
 +                            } else {
-                             (2, deref_msg)
++                                (2, deref_msg, None)
 +                            }
 +                        } else {
-                             hir_id: pat.hir_id
++                            (2, deref_msg, None)
 +                        };
 +
 +                        if deref_count >= required_refs {
 +                            self.state = Some((
 +                                State::DerefedBorrow(DerefedBorrow {
 +                                    // One of the required refs is for the current borrow expression, the remaining ones
 +                                    // can't be removed without breaking the code. See earlier comment.
 +                                    count: deref_count - required_refs,
 +                                    msg,
++                                    snip_expr,
 +                                }),
 +                                StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                            ));
 +                        } else if position.is_deref_stable()
 +                            // Auto-deref doesn't combine with other adjustments
 +                            && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                            && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
 +                        {
 +                            self.state = Some((
 +                                State::Borrow { mutability },
 +                                StateData {
 +                                    span: expr.span,
 +                                    hir_id: expr.hir_id,
 +                                    position
 +                                },
 +                            ));
 +                        }
 +                    },
 +                    RefOp::Method(..) => (),
 +                }
 +            },
 +            (
 +                Some((
 +                    State::DerefMethod {
 +                        target_mut,
 +                        ty_changed_count,
 +                        ..
 +                    },
 +                    data,
 +                )),
 +                RefOp::Method(_),
 +            ) => {
 +                self.state = Some((
 +                    State::DerefMethod {
 +                        ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
 +                            ty_changed_count
 +                        } else {
 +                            ty_changed_count + 1
 +                        },
 +                        is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
 +                        target_mut,
 +                    },
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(_)) if state.count != 0 => {
 +                self.state = Some((
 +                    State::DerefedBorrow(DerefedBorrow {
 +                        count: state.count - 1,
 +                        ..state
 +                    }),
 +                    data,
 +                ));
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::Borrow { mutability },
 +                        StateData {
 +                            span: expr.span,
 +                            hir_id: expr.hir_id,
 +                            position,
 +                        },
 +                    ));
 +                }
 +            },
 +            (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
 +                let position = data.position;
 +                report(cx, expr, State::DerefedBorrow(state), data);
 +                if let Position::FieldAccess(name) = position
 +                    && !ty_contains_field(typeck.expr_ty(sub_expr), name)
 +                {
 +                    self.state = Some((
 +                        State::ExplicitDerefField { name },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                } else if position.is_deref_stable() {
 +                    self.state = Some((
 +                        State::ExplicitDeref { mutability: None },
 +                        StateData { span: expr.span, hir_id: expr.hir_id, position },
 +                    ));
 +                }
 +            },
 +
 +            (Some((State::Borrow { mutability }, data)), RefOp::Deref) => {
 +                if typeck.expr_ty(sub_expr).is_ref() {
 +                    self.state = Some((State::Reborrow { mutability }, data));
 +                } else {
 +                    self.state = Some((
 +                        State::ExplicitDeref {
 +                            mutability: Some(mutability),
 +                        },
 +                        data,
 +                    ));
 +                }
 +            },
 +            (Some((State::Reborrow { mutability }, data)), RefOp::Deref) => {
 +                self.state = Some((
 +                    State::ExplicitDeref {
 +                        mutability: Some(mutability),
 +                    },
 +                    data,
 +                ));
 +            },
 +            (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
 +                self.state = state;
 +            },
 +            (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
 +                if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
 +            {
 +                self.state = Some((State::ExplicitDerefField { name }, data));
 +            },
 +
 +            (Some((state, data)), _) => report(cx, expr, state, data),
 +        }
 +    }
 +
 +    fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 +        if let PatKind::Binding(BindingAnnotation::Ref, id, name, _) = pat.kind {
 +            if let Some(opt_prev_pat) = self.ref_locals.get_mut(&id) {
 +                // This binding id has been seen before. Add this pattern to the list of changes.
 +                if let Some(prev_pat) = opt_prev_pat {
 +                    if pat.span.from_expansion() {
 +                        // Doesn't match the context of the previous pattern. Can't lint here.
 +                        *opt_prev_pat = None;
 +                    } else {
 +                        prev_pat.spans.push(pat.span);
 +                        prev_pat.replacements.push((
 +                            pat.span,
 +                            snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut prev_pat.app)
 +                                .0
 +                                .into(),
 +                        ));
 +                    }
 +                }
 +                return;
 +            }
 +
 +            if_chain! {
 +                if !pat.span.from_expansion();
 +                if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
 +                // only lint immutable refs, because borrowed `&mut T` cannot be moved out
 +                if let ty::Ref(_, _, Mutability::Not) = *tam.kind();
 +                then {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
 +                    self.current_body = self.current_body.or(cx.enclosing_body);
 +                    self.ref_locals.insert(
 +                        id,
 +                        Some(RefPat {
 +                            always_deref: true,
 +                            spans: vec![pat.span],
 +                            app,
 +                            replacements: vec![(pat.span, snip.into())],
-             Self::Deref => PREC_PREFIX,
++                            hir_id: pat.hir_id,
 +                        }),
 +                    );
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
 +        if Some(body.id()) == self.current_body {
 +            for pat in self.ref_locals.drain(..).filter_map(|(_, x)| x) {
 +                let replacements = pat.replacements;
 +                let app = pat.app;
 +                let lint = if pat.always_deref {
 +                    NEEDLESS_BORROW
 +                } else {
 +                    REF_BINDING_TO_REFERENCE
 +                };
 +                span_lint_hir_and_then(
 +                    cx,
 +                    lint,
 +                    pat.hir_id,
 +                    pat.spans,
 +                    "this pattern creates a reference to a reference",
 +                    |diag| {
 +                        diag.multipart_suggestion("try this", replacements, app);
 +                    },
 +                );
 +            }
 +            self.current_body = None;
 +        }
 +    }
++
++    extract_msrv_attr!(LateContext);
 +}
 +
 +fn try_parse_ref_op<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    typeck: &'tcx TypeckResults<'_>,
 +    expr: &'tcx Expr<'_>,
 +) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
 +    let (def_id, arg) = match expr.kind {
 +        ExprKind::MethodCall(_, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(path),
 +                hir_id,
 +                ..
 +            },
 +            [arg],
 +        ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
 +        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
 +            return Some((RefOp::Deref, sub_expr));
 +        },
 +        ExprKind::AddrOf(BorrowKind::Ref, mutability, sub_expr) => return Some((RefOp::AddrOf(mutability), sub_expr)),
 +        _ => return None,
 +    };
 +    if tcx.is_diagnostic_item(sym::deref_method, def_id) {
 +        Some((RefOp::Method(Mutability::Not), arg))
 +    } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
 +        Some((RefOp::Method(Mutability::Mut), arg))
 +    } else {
 +        None
 +    }
 +}
 +
 +// Checks whether the type for a deref call actually changed the type, not just the mutability of
 +// the reference.
 +fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
 +    match (result_ty.kind(), arg_ty.kind()) {
 +        (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => result_ty == arg_ty,
 +
 +        // The result type for a deref method is always a reference
 +        // Not matching the previous pattern means the argument type is not a reference
 +        // This means that the type did change
 +        _ => false,
 +    }
 +}
 +
 +/// The position of an expression relative to it's parent.
 +#[derive(Clone, Copy)]
 +enum Position {
 +    MethodReceiver,
 +    /// The method is defined on a reference type. e.g. `impl Foo for &T`
 +    MethodReceiverRefImpl,
 +    Callee,
++    ImplArg(HirId),
 +    FieldAccess(Symbol),
 +    Postfix,
 +    Deref,
 +    /// Any other location which will trigger auto-deref to a specific time.
 +    /// Contains the precedence of the parent expression and whether the target type is sized.
 +    DerefStable(i8, bool),
 +    /// Any other location which will trigger auto-reborrowing.
 +    /// Contains the precedence of the parent expression.
 +    ReborrowStable(i8),
 +    /// Contains the precedence of the parent expression.
 +    Other(i8),
 +}
 +impl Position {
 +    fn is_deref_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..))
 +    }
 +
 +    fn is_reborrow_stable(self) -> bool {
 +        matches!(self, Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn can_auto_borrow(self) -> bool {
 +        matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
 +    }
 +
 +    fn lint_explicit_deref(self) -> bool {
 +        matches!(self, Self::Other(_) | Self::DerefStable(..) | Self::ReborrowStable(_))
 +    }
 +
 +    fn precedence(self) -> i8 {
 +        match self {
 +            Self::MethodReceiver
 +            | Self::MethodReceiverRefImpl
 +            | Self::Callee
 +            | Self::FieldAccess(_)
 +            | Self::Postfix => PREC_POSTFIX,
- #[allow(clippy::too_many_lines)]
- fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
++            Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
 +            Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
 +        }
 +    }
 +}
 +
 +/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
 +/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
 +/// locations as those follow different rules.
-                     .and_then(|(i, sig)| sig.input_with_hir(i))
-                     .map(|(hir_ty, ty)| match hir_ty {
-                         // Type inference for closures can depend on how they're called. Only go by the explicit
-                         // types here.
-                         Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
-                         None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
-                             .position_for_arg(),
++#[expect(clippy::too_many_lines)]
++fn walk_parents<'tcx>(
++    cx: &LateContext<'tcx>,
++    e: &'tcx Expr<'_>,
++    msrv: Option<RustcVersion>,
++) -> (Position, &'tcx [Adjustment<'tcx>]) {
 +    let mut adjustments = [].as_slice();
 +    let mut precedence = 0i8;
 +    let ctxt = e.span.ctxt();
 +    let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
 +        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
 +        if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
 +            adjustments = cx.typeck_results().expr_adjustments(e);
 +        }
 +        match parent {
 +            Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
 +                Some(binding_ty_auto_deref_stability(cx, ty, precedence, List::empty()))
 +            },
 +            Node::Item(&Item {
 +                kind: ItemKind::Static(..) | ItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Const(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let ty = cx.tcx.type_of(def_id);
 +                Some(ty_auto_deref_stability(cx, ty, precedence).position_for_result(cx))
 +            },
 +
 +            Node::Item(&Item {
 +                kind: ItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::TraitItem(&TraitItem {
 +                kind: TraitItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            })
 +            | Node::ImplItem(&ImplItem {
 +                kind: ImplItemKind::Fn(..),
 +                def_id,
 +                span,
 +                ..
 +            }) if span.ctxt() == ctxt => {
 +                let output = cx
 +                    .tcx
 +                    .erase_late_bound_regions(cx.tcx.fn_sig(def_id.to_def_id()).output());
 +                Some(ty_auto_deref_stability(cx, output, precedence).position_for_result(cx))
 +            },
 +
 +            Node::ExprField(field) if field.span.ctxt() == ctxt => match get_parent_expr_for_hir(cx, field.hir_id) {
 +                Some(Expr {
 +                    hir_id,
 +                    kind: ExprKind::Struct(path, ..),
 +                    ..
 +                }) => variant_of_res(cx, cx.qpath_res(path, *hir_id))
 +                    .and_then(|variant| variant.fields.iter().find(|f| f.name == field.ident.name))
 +                    .map(|field_def| {
 +                        ty_auto_deref_stability(cx, cx.tcx.type_of(field_def.did), precedence).position_for_arg()
 +                    }),
 +                _ => None,
 +            },
 +
 +            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
 +                ExprKind::Ret(_) => {
 +                    let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
 +                    Some(
 +                        if let Node::Expr(
 +                            closure_expr @ Expr {
 +                                kind: ExprKind::Closure(closure),
 +                                ..
 +                            },
 +                        ) = cx.tcx.hir().get(owner_id)
 +                        {
 +                            closure_result_position(cx, closure, cx.typeck_results().expr_ty(closure_expr), precedence)
 +                        } else {
 +                            let output = cx
 +                                .tcx
 +                                .erase_late_bound_regions(cx.tcx.fn_sig(cx.tcx.hir().local_def_id(owner_id)).output());
 +                            ty_auto_deref_stability(cx, output, precedence).position_for_result(cx)
 +                        },
 +                    )
 +                },
 +                ExprKind::Closure(closure) => Some(closure_result_position(
 +                    cx,
 +                    closure,
 +                    cx.typeck_results().expr_ty(parent),
 +                    precedence,
 +                )),
 +                ExprKind::Call(func, _) if func.hir_id == child_id => {
 +                    (child_id == e.hir_id).then_some(Position::Callee)
 +                },
 +                ExprKind::Call(func, args) => args
 +                    .iter()
 +                    .position(|arg| arg.hir_id == child_id)
 +                    .zip(expr_sig(cx, func))
-                                     None => cx.tcx.mk_substs([].iter()),
++                    .and_then(|(i, sig)| {
++                        sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
++                            // Type inference for closures can depend on how they're called. Only go by the explicit
++                            // types here.
++                            Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
++                            None => {
++                                if let ty::Param(param_ty) = ty.skip_binder().kind() {
++                                    needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
++                                } else {
++                                    ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
++                                        .position_for_arg()
++                                }
++                            },
++                        })
 +                    }),
 +                ExprKind::MethodCall(_, args, _) => {
 +                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
 +                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
 +                        if i == 0 {
 +                            // Check for calls to trait methods where the trait is implemented on a reference.
 +                            // Two cases need to be handled:
 +                            // * `self` methods on `&T` will never have auto-borrow
 +                            // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
 +                            //   priority.
 +                            if e.hir_id != child_id {
 +                                Position::ReborrowStable(precedence)
 +                            } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
 +                                && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
 +                                && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
 +                                && let subs = match cx
 +                                    .typeck_results()
 +                                    .node_substs_opt(parent.hir_id)
 +                                    .and_then(|subs| subs.get(1..))
 +                                {
 +                                    Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
-                             ty_auto_deref_stability(
-                                 cx,
-                                 cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
-                                 precedence,
-                             )
-                             .position_for_arg()
++                                    None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
 +                                } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
 +                                    // Trait methods taking `&self`
 +                                    sub_ty
 +                                } else {
 +                                    // Trait methods taking `self`
 +                                    arg_ty
 +                                } && impl_ty.is_ref()
 +                                && cx.tcx.infer_ctxt().enter(|infcx|
 +                                    infcx
 +                                        .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
 +                                        .must_apply_modulo_regions()
 +                                )
 +                            {
 +                                Position::MethodReceiverRefImpl
 +                            } else {
 +                                Position::MethodReceiver
 +                            }
 +                        } else {
-             let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
++                            let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
++                            if let ty::Param(param_ty) = ty.kind() {
++                                needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
++                            } else {
++                                ty_auto_deref_stability(
++                                    cx,
++                                    cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
++                                    precedence,
++                                )
++                                .position_for_arg()
++                            }
 +                        }
 +                    })
 +                },
 +                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
 +                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
 +                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
 +                | ExprKind::Index(child, _)
 +                    if child.hir_id == e.hir_id =>
 +                {
 +                    Some(Position::Postfix)
 +                },
 +                _ if child_id == e.hir_id => {
 +                    precedence = parent.precedence().order();
 +                    None
 +                },
 +                _ => None,
 +            },
 +            _ => None,
 +        }
 +    })
 +    .unwrap_or(Position::Other(precedence));
 +    (position, adjustments)
 +}
 +
 +fn closure_result_position<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    closure: &'tcx Closure<'_>,
 +    ty: Ty<'tcx>,
 +    precedence: i8,
 +) -> Position {
 +    match closure.fn_decl.output {
 +        FnRetTy::Return(hir_ty) => {
 +            if let Some(sig) = ty_sig(cx, ty)
 +                && let Some(output) = sig.output()
 +            {
 +                binding_ty_auto_deref_stability(cx, hir_ty, precedence, output.bound_vars())
 +            } else {
 +                Position::Other(precedence)
 +            }
 +        },
 +        FnRetTy::DefaultReturn(_) => Position::Other(precedence),
 +    }
 +}
 +
 +// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
 +//
 +// e.g.
 +// let x = Box::new(Box::new(0u32));
 +// let y1: &Box<_> = x.deref();
 +// let y2: &Box<_> = &x;
 +//
 +// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
 +// switching to auto-dereferencing.
 +fn binding_ty_auto_deref_stability<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx hir::Ty<'_>,
 +    precedence: i8,
 +    binder_args: &'tcx List<BoundVariableKind>,
 +) -> Position {
 +    let TyKind::Rptr(_, ty) = &ty.kind else {
 +        return Position::Other(precedence);
 +    };
 +    let mut ty = ty;
 +
 +    loop {
 +        break match ty.ty.kind {
 +            TyKind::Rptr(_, ref ref_ty) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            TyKind::Path(
 +                QPath::TypeRelative(_, path)
 +                | QPath::Resolved(
 +                    _,
 +                    Path {
 +                        segments: [.., path], ..
 +                    },
 +                ),
 +            ) => {
 +                if let Some(args) = path.args
 +                    && args.args.iter().any(|arg| match arg {
 +                        GenericArg::Infer(_) => true,
 +                        GenericArg::Type(ty) => ty_contains_infer(ty),
 +                        _ => false,
 +                    })
 +                {
 +                    Position::ReborrowStable(precedence)
 +                } else {
 +                    Position::DerefStable(
 +                        precedence,
 +                        cx.tcx
 +                            .erase_late_bound_regions(Binder::bind_with_vars(
 +                                cx.typeck_results().node_type(ty.ty.hir_id),
 +                                binder_args,
 +                            ))
 +                            .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +                    )
 +                }
 +            },
 +            TyKind::Slice(_) => Position::DerefStable(precedence, false),
 +            TyKind::Array(..) | TyKind::Ptr(_) | TyKind::BareFn(_) => Position::DerefStable(precedence, true),
 +            TyKind::Never
 +            | TyKind::Tup(_)
 +            | TyKind::Path(_) => Position::DerefStable(
 +                precedence,
 +                cx.tcx
 +                    .erase_late_bound_regions(Binder::bind_with_vars(
 +                        cx.typeck_results().node_type(ty.ty.hir_id),
 +                        binder_args,
 +                    ))
 +                    .is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +            ),
 +            TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(..) | TyKind::TraitObject(..) | TyKind::Err => {
 +                Position::ReborrowStable(precedence)
 +            },
 +        };
 +    }
 +}
 +
 +// Checks whether a type is inferred at some point.
 +// e.g. `_`, `Box<_>`, `[_]`
 +fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
 +    struct V(bool);
 +    impl Visitor<'_> for V {
 +        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
 +            if self.0
 +                || matches!(
 +                    ty.kind,
 +                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
 +                )
 +            {
 +                self.0 = true;
 +            } else {
 +                walk_ty(self, ty);
 +            }
 +        }
 +
 +        fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
 +            if self.0 || matches!(arg, GenericArg::Infer(_)) {
 +                self.0 = true;
 +            } else if let GenericArg::Type(ty) = arg {
 +                self.visit_ty(ty);
 +            }
 +        }
 +    }
 +    let mut v = V(false);
 +    v.visit_ty(ty);
 +    v.0
 +}
 +
++// Checks whether:
++// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
++// * `e`'s type implements `Trait` and is copyable
++// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
++//   The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
++// be moved, but it cannot be.
++fn needless_borrow_impl_arg_position<'tcx>(
++    cx: &LateContext<'tcx>,
++    parent: &Expr<'tcx>,
++    arg_index: usize,
++    param_ty: ParamTy,
++    mut expr: &Expr<'tcx>,
++    precedence: i8,
++    msrv: Option<RustcVersion>,
++) -> Position {
++    let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
++    let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
++
++    let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
++    let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
++    let substs_with_expr_ty = cx
++        .typeck_results()
++        .node_substs(if let ExprKind::Call(callee, _) = parent.kind {
++            callee.hir_id
++        } else {
++            parent.hir_id
++        });
++
++    let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
++    let projection_predicates = predicates
++        .iter()
++        .filter_map(|predicate| {
++            if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
++                Some(projection_predicate)
++            } else {
++                None
++            }
++        })
++        .collect::<Vec<_>>();
++
++    let mut trait_with_ref_mut_self_method = false;
++
++    // If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
++    if predicates
++        .iter()
++        .filter_map(|predicate| {
++            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
++                && trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
++            {
++                Some(trait_predicate.trait_ref.def_id)
++            } else {
++                None
++            }
++        })
++        .inspect(|trait_def_id| {
++            trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
++        })
++        .all(|trait_def_id| {
++            Some(trait_def_id) == destruct_trait_def_id
++                || Some(trait_def_id) == sized_trait_def_id
++                || cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
++        })
++    {
++        return Position::Other(precedence);
++    }
++
++    // `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
++    // elements are modified each time `check_referent` is called.
++    let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
++
++    let mut check_referent = |referent| {
++        let referent_ty = cx.typeck_results().expr_ty(referent);
++
++        if !is_copy(cx, referent_ty) {
++            return false;
++        }
++
++        // https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
++        if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
++            return false;
++        }
++
++        if !replace_types(
++            cx,
++            param_ty,
++            referent_ty,
++            fn_sig,
++            arg_index,
++            &projection_predicates,
++            &mut substs_with_referent_ty,
++        ) {
++            return false;
++        }
++
++        predicates.iter().all(|predicate| {
++            if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
++                && cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
++                && let ty::Param(param_ty) = trait_predicate.self_ty().kind()
++                && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
++                && ty.is_array()
++                && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
++            {
++                return false;
++            }
++
++            let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
++            let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
++            cx.tcx
++                .infer_ctxt()
++                .enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
++        })
++    };
++
++    let mut needless_borrow = false;
++    while let ExprKind::AddrOf(_, _, referent) = expr.kind {
++        if !check_referent(referent) {
++            break;
++        }
++        expr = referent;
++        needless_borrow = true;
++    }
++
++    if needless_borrow {
++        Position::ImplArg(expr.hir_id)
++    } else {
++        Position::Other(precedence)
++    }
++}
++
++fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
++    cx.tcx
++        .associated_items(trait_def_id)
++        .in_definition_order()
++        .any(|assoc_item| {
++            if assoc_item.fn_has_self_parameter {
++                let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
++                matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
++            } else {
++                false
++            }
++        })
++}
++
++// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
++// projected type that is a type parameter. Returns `false` if replacing the types would have an
++// effect on the function signature beyond substituting `new_ty` for `param_ty`.
++// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
++fn replace_types<'tcx>(
++    cx: &LateContext<'tcx>,
++    param_ty: ParamTy,
++    new_ty: Ty<'tcx>,
++    fn_sig: FnSig<'tcx>,
++    arg_index: usize,
++    projection_predicates: &[ProjectionPredicate<'tcx>],
++    substs: &mut [ty::GenericArg<'tcx>],
++) -> bool {
++    let mut replaced = BitSet::new_empty(substs.len());
++
++    let mut deque = VecDeque::with_capacity(substs.len());
++    deque.push_back((param_ty, new_ty));
++
++    while let Some((param_ty, new_ty)) = deque.pop_front() {
++        // If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
++        if !fn_sig
++            .inputs_and_output
++            .iter()
++            .enumerate()
++            .all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
++        {
++            return false;
++        }
++
++        substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
++
++        // The `replaced.insert(...)` check provides some protection against infinite loops.
++        if replaced.insert(param_ty.index) {
++            for projection_predicate in projection_predicates {
++                if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
++                    && let ty::Term::Ty(term_ty) = projection_predicate.term
++                    && let ty::Param(term_param_ty) = term_ty.kind()
++                {
++                    let item_def_id = projection_predicate.projection_ty.item_def_id;
++                    let assoc_item = cx.tcx.associated_item(item_def_id);
++                    let projection = cx.tcx
++                        .mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
++
++                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
++                        && substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
++                    {
++                        deque.push_back((*term_param_ty, projected_ty));
++                    }
++                }
++            }
++        }
++    }
++
++    true
++}
++
 +struct TyPosition<'tcx> {
 +    position: Position,
 +    ty: Option<Ty<'tcx>>,
 +}
 +impl From<Position> for TyPosition<'_> {
 +    fn from(position: Position) -> Self {
 +        Self { position, ty: None }
 +    }
 +}
 +impl<'tcx> TyPosition<'tcx> {
 +    fn new_deref_stable_for_result(precedence: i8, ty: Ty<'tcx>) -> Self {
 +        Self {
 +            position: Position::ReborrowStable(precedence),
 +            ty: Some(ty),
 +        }
 +    }
 +    fn position_for_result(self, cx: &LateContext<'tcx>) -> Position {
 +        match (self.position, self.ty) {
 +            (Position::ReborrowStable(precedence), Some(ty)) => {
 +                Position::DerefStable(precedence, ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env))
 +            },
 +            (position, _) => position,
 +        }
 +    }
 +    fn position_for_arg(self) -> Position {
 +        self.position
 +    }
 +}
 +
 +// Checks whether a type is stable when switching to auto dereferencing,
 +fn ty_auto_deref_stability<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, precedence: i8) -> TyPosition<'tcx> {
 +    let ty::Ref(_, mut ty, _) = *ty.kind() else {
 +        return Position::Other(precedence).into();
 +    };
 +
 +    loop {
 +        break match *ty.kind() {
 +            ty::Ref(_, ref_ty, _) => {
 +                ty = ref_ty;
 +                continue;
 +            },
 +            ty::Param(_) => TyPosition::new_deref_stable_for_result(precedence, ty),
 +            ty::Infer(_) | ty::Error(_) | ty::Bound(..) | ty::Opaque(..) | ty::Placeholder(_) | ty::Dynamic(..) => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(..) if ty.has_placeholders() || ty.has_opaque_types() => {
 +                Position::ReborrowStable(precedence).into()
 +            },
 +            ty::Adt(_, substs) if substs.has_param_types_or_consts() => {
 +                TyPosition::new_deref_stable_for_result(precedence, ty)
 +            },
 +            ty::Bool
 +            | ty::Char
 +            | ty::Int(_)
 +            | ty::Uint(_)
 +            | ty::Array(..)
 +            | ty::Float(_)
 +            | ty::RawPtr(..)
 +            | ty::FnPtr(_) => Position::DerefStable(precedence, true).into(),
 +            ty::Str | ty::Slice(..) => Position::DerefStable(precedence, false).into(),
 +            ty::Adt(..)
 +            | ty::Foreign(_)
 +            | ty::FnDef(..)
 +            | ty::Generator(..)
 +            | ty::GeneratorWitness(..)
 +            | ty::Closure(..)
 +            | ty::Never
 +            | ty::Tuple(_)
 +            | ty::Projection(_) => Position::DerefStable(
 +                precedence,
 +                ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env.without_caller_bounds()),
 +            )
 +            .into(),
 +        };
 +    }
 +}
 +
 +fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
 +    if let ty::Adt(adt, _) = *ty.kind() {
 +        adt.is_struct() && adt.all_fields().any(|f| f.name == name)
 +    } else {
 +        false
 +    }
 +}
 +
 +#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
 +fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
 +    match state {
 +        State::DerefMethod {
 +            ty_changed_count,
 +            is_final_ufcs,
 +            target_mut,
 +        } => {
 +            let mut app = Applicability::MachineApplicable;
 +            let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +            let ty = cx.typeck_results().expr_ty(expr);
 +            let (_, ref_count) = peel_mid_ty_refs(ty);
 +            let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
 +                // a deref call changing &T -> &U requires two deref operators the first time
 +                // this occurs. One to remove the reference, a second to call the deref impl.
 +                "*".repeat(ty_changed_count + 1)
 +            } else {
 +                "*".repeat(ty_changed_count)
 +            };
 +            let addr_of_str = if ty_changed_count < ref_count {
 +                // Check if a reborrow from &mut T -> &T is required.
 +                if target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
 +                    "&*"
 +                } else {
 +                    ""
 +                }
 +            } else if target_mut == Mutability::Mut {
 +                "&mut "
 +            } else {
 +                "&"
 +            };
 +
 +            let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
 +                format!("({})", expr_str)
 +            } else {
 +                expr_str.into_owned()
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                EXPLICIT_DEREF_METHODS,
 +                data.span,
 +                match target_mut {
 +                    Mutability::Not => "explicit `deref` method call",
 +                    Mutability::Mut => "explicit `deref_mut` method call",
 +                },
 +                "try this",
 +                format!("{}{}{}", addr_of_str, deref_str, expr_str),
 +                app,
 +            );
 +        },
 +        State::DerefedBorrow(state) => {
 +            let mut app = Applicability::MachineApplicable;
++            let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
++            let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
 +            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
 +                let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
 +                let sugg = if !snip_is_macro
 +                    && !has_enclosing_paren(&snip)
 +                    && (expr.precedence().order() < data.position.precedence() || calls_field)
 +                {
 +                    format!("({})", snip)
 +                } else {
 +                    snip.into()
 +                };
 +                diag.span_suggestion(data.span, "change this to", sugg, app);
 +            });
 +        },
 +        State::ExplicitDeref { mutability } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            let (prefix, precedence) = if let Some(mutability) = mutability
 +                && !cx.typeck_results().expr_ty(expr).is_ref()
 +            {
 +                let prefix = match mutability {
 +                    Mutability::Not => "&",
 +                    Mutability::Mut => "&mut ",
 +                };
 +                (prefix, 0)
 +            } else {
 +                ("", data.position.precedence())
 +            };
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
 +                    let sugg =
 +                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
 +                            format!("{}({})", prefix, snip)
 +                        } else {
 +                            format!("{}{}", prefix, snip)
 +                        };
 +                    diag.span_suggestion(data.span, "try this", sugg, app);
 +                },
 +            );
 +        },
 +        State::ExplicitDerefField { .. } => {
 +            if matches!(
 +                expr.kind,
 +                ExprKind::Block(..)
 +                    | ExprKind::ConstBlock(_)
 +                    | ExprKind::If(..)
 +                    | ExprKind::Loop(..)
 +                    | ExprKind::Match(..)
 +            ) && matches!(data.position, Position::DerefStable(_, true))
 +            {
 +                // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
 +                return;
 +            }
 +
 +            span_lint_hir_and_then(
 +                cx,
 +                EXPLICIT_AUTO_DEREF,
 +                data.hir_id,
 +                data.span,
 +                "deref which would be done by auto-deref",
 +                |diag| {
 +                    let mut app = Applicability::MachineApplicable;
 +                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
 +                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
 +                },
 +            );
 +        },
 +        State::Borrow { .. } | State::Reborrow { .. } => (),
 +    }
 +}
 +
 +impl Dereferencing {
 +    fn check_local_usage<'tcx>(&mut self, cx: &LateContext<'tcx>, e: &Expr<'tcx>, local: HirId) {
 +        if let Some(outer_pat) = self.ref_locals.get_mut(&local) {
 +            if let Some(pat) = outer_pat {
 +                // Check for auto-deref
 +                if !matches!(
 +                    cx.typeck_results().expr_adjustments(e),
 +                    [
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        Adjustment {
 +                            kind: Adjust::Deref(_),
 +                            ..
 +                        },
 +                        ..
 +                    ]
 +                ) {
 +                    match get_parent_expr(cx, e) {
 +                        // Field accesses are the same no matter the number of references.
 +                        Some(Expr {
 +                            kind: ExprKind::Field(..),
 +                            ..
 +                        }) => (),
 +                        Some(&Expr {
 +                            span,
 +                            kind: ExprKind::Unary(UnOp::Deref, _),
 +                            ..
 +                        }) if !span.from_expansion() => {
 +                            // Remove explicit deref.
 +                            let snip = snippet_with_context(cx, e.span, span.ctxt(), "..", &mut pat.app).0;
 +                            pat.replacements.push((span, snip.into()));
 +                        },
 +                        Some(parent) if !parent.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            if parent.precedence().order() == PREC_POSTFIX {
 +                                // Parentheses would be needed here, don't lint.
 +                                *outer_pat = None;
 +                            } else {
 +                                pat.always_deref = false;
 +                                let snip = snippet_with_context(cx, e.span, parent.span.ctxt(), "..", &mut pat.app).0;
 +                                pat.replacements.push((e.span, format!("&{}", snip)));
 +                            }
 +                        },
 +                        _ if !e.span.from_expansion() => {
 +                            // Double reference might be needed at this point.
 +                            pat.always_deref = false;
 +                            let snip = snippet_with_applicability(cx, e.span, "..", &mut pat.app);
 +                            pat.replacements.push((e.span, format!("&{}", snip)));
 +                        },
 +                        // Edge case for macros. The span of the identifier will usually match the context of the
 +                        // binding, but not if the identifier was created in a macro. e.g. `concat_idents` and proc
 +                        // macros
 +                        _ => *outer_pat = None,
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
index a982990e4186c9cc6c2aef5bb9477d9a7b96651a,0000000000000000000000000000000000000000..9ca443b7dff6cbe67a24c5008dee96d2d09be293
mode 100644,000000..100644
--- /dev/null
@@@ -1,528 -1,0 +1,531 @@@
-                     trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::paths;
 +use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 +use clippy_utils::{is_lint_allowed, match_def_path};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 +use rustc_hir::{
 +    self as hir, BlockCheckMode, BodyId, Constness, Expr, ExprKind, FnDecl, HirId, Impl, Item, ItemKind, UnsafeSource,
 +    Unsafety,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::traits::Reveal;
 +use rustc_middle::ty::{
 +    self, Binder, BoundConstness, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef,
 +    Ty, TyCtxt, Visibility,
 +};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Hash` but implementing `PartialEq`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `HashMap`) so it’s probably a bad idea to use a
 +    /// default-generated `Hash` implementation with an explicitly defined
 +    /// `PartialEq`. In particular, the following must hold for any type:
 +    ///
 +    /// ```text
 +    /// k1 == k2 ⇒ hash(k1) == hash(k2)
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// #[derive(Hash)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialEq for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub DERIVE_HASH_XOR_EQ,
 +    correctness,
 +    "deriving `Hash` but implementing `PartialEq` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `Ord` but implementing `PartialOrd`
 +    /// explicitly or vice versa.
 +    ///
 +    /// ### Why is this bad?
 +    /// The implementation of these traits must agree (for
 +    /// example for use with `sort`) so it’s probably a bad idea to use a
 +    /// default-generated `Ord` implementation with an explicitly defined
 +    /// `PartialOrd`. In particular, the following must hold for any type
 +    /// implementing `Ord`:
 +    ///
 +    /// ```text
 +    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
 +    /// ```
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo;
 +    ///
 +    /// impl PartialOrd for Foo {
 +    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
 +    ///        Some(self.cmp(other))
 +    ///     }
 +    /// }
 +    ///
 +    /// impl Ord for Foo {
 +    ///     ...
 +    /// }
 +    /// ```
 +    /// or, if you don't need a custom ordering:
 +    /// ```rust,ignore
 +    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
 +    /// struct Foo;
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub DERIVE_ORD_XOR_PARTIAL_ORD,
 +    correctness,
 +    "deriving `Ord` but implementing `PartialOrd` explicitly"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for explicit `Clone` implementations for `Copy`
 +    /// types.
 +    ///
 +    /// ### Why is this bad?
 +    /// To avoid surprising behavior, these traits should
 +    /// agree and the behavior of `Copy` cannot be overridden. In almost all
 +    /// situations a `Copy` type should have a `Clone` implementation that does
 +    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
 +    /// gets you.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// #[derive(Copy)]
 +    /// struct Foo;
 +    ///
 +    /// impl Clone for Foo {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPL_IMPL_CLONE_ON_COPY,
 +    pedantic,
 +    "implementing `Clone` explicitly on `Copy` types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for deriving `serde::Deserialize` on a type that
 +    /// has methods using `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Deriving `serde::Deserialize` will create a constructor
 +    /// that may violate invariants hold by another constructor.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// use serde::Deserialize;
 +    ///
 +    /// #[derive(Deserialize)]
 +    /// pub struct Foo {
 +    ///     // ..
 +    /// }
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Self {
 +    ///         // setup here ..
 +    ///     }
 +    ///
 +    ///     pub unsafe fn parts() -> (&str, &str) {
 +    ///         // assumes invariants hold
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNSAFE_DERIVE_DESERIALIZE,
 +    pedantic,
 +    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for types that derive `PartialEq` and could implement `Eq`.
 +    ///
 +    /// ### Why is this bad?
 +    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
 +    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
 +    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
 +    /// `Eq` themselves.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// #[derive(PartialEq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[derive(PartialEq, Eq)]
 +    /// struct Foo {
 +    ///     i_am_eq: i32,
 +    ///     i_am_eq_too: Vec<String>,
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.63.0"]
 +    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    style,
 +    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
 +}
 +
 +declare_lint_pass!(Derive => [
 +    EXPL_IMPL_CLONE_ON_COPY,
 +    DERIVE_HASH_XOR_EQ,
 +    DERIVE_ORD_XOR_PARTIAL_ORD,
 +    UNSAFE_DERIVE_DESERIALIZE,
 +    DERIVE_PARTIAL_EQ_WITHOUT_EQ
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Derive {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if let ItemKind::Impl(Impl {
 +            of_trait: Some(ref trait_ref),
 +            ..
 +        }) = item.kind
 +        {
 +            let ty = cx.tcx.type_of(item.def_id);
 +            let is_automatically_derived = cx.tcx.has_attr(item.def_id.to_def_id(), sym::automatically_derived);
 +
 +            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
 +            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
 +
 +            if is_automatically_derived {
 +                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
 +                check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
 +            } else {
 +                check_copy_clone(cx, item, trait_ref, ty);
 +            }
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_HASH_XOR_EQ` lint.
 +fn check_hash_peq<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    hash_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::Hash, def_id);
 +        then {
 +            // Look for the PartialEq implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
 +                let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if peq_is_automatically_derived == hash_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialEq<Foo> for Foo`
 +                // For `impl PartialEq<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if peq_is_automatically_derived {
 +                        "you are implementing `Hash` explicitly but have derived `PartialEq`"
 +                    } else {
 +                        "you are deriving `Hash` but have implemented `PartialEq` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_HASH_XOR_EQ,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialEq` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
 +fn check_ord_partial_ord<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    span: Span,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +    ord_is_automatically_derived: bool,
 +) {
 +    if_chain! {
 +        if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord);
 +        if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait();
 +        if let Some(def_id) = &trait_ref.trait_def_id();
 +        if *def_id == ord_trait_def_id;
 +        then {
 +            // Look for the PartialOrd implementations for `ty`
 +            cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
 +                let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
 +
 +                if partial_ord_is_automatically_derived == ord_is_automatically_derived {
 +                    return;
 +                }
 +
 +                let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
 +
 +                // Only care about `impl PartialOrd<Foo> for Foo`
 +                // For `impl PartialOrd<B> for A, input_types is [A, B]
 +                if trait_ref.substs.type_at(1) == ty {
 +                    let mess = if partial_ord_is_automatically_derived {
 +                        "you are implementing `Ord` explicitly but have derived `PartialOrd`"
 +                    } else {
 +                        "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
 +                    };
 +
 +                    span_lint_and_then(
 +                        cx,
 +                        DERIVE_ORD_XOR_PARTIAL_ORD,
 +                        span,
 +                        mess,
 +                        |diag| {
 +                            if let Some(local_def_id) = impl_id.as_local() {
 +                                let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +                                diag.span_note(
 +                                    cx.tcx.hir().span(hir_id),
 +                                    "`PartialOrd` implemented here"
 +                                );
 +                            }
 +                        }
 +                    );
 +                }
 +            });
 +        }
 +    }
 +}
 +
 +/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
 +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    let clone_id = match cx.tcx.lang_items().clone_trait() {
 +        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
 +        _ => return,
 +    };
 +    let copy_id = match cx.tcx.lang_items().copy_trait() {
 +        Some(id) => id,
 +        None => return,
 +    };
 +    let (ty_adt, ty_subs) = match *ty.kind() {
 +        // Unions can't derive clone.
 +        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
 +        _ => return,
 +    };
 +    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
 +    // there's a Copy impl for any instance of the adt.
 +    if !is_copy(cx, ty) {
 +        if ty_subs.non_erasable_generics().next().is_some() {
 +            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
 +                impls
 +                    .iter()
 +                    .any(|&id| matches!(cx.tcx.type_of(id).kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did()))
 +            });
 +            if !has_copy_impl {
 +                return;
 +            }
 +        } else {
 +            return;
 +        }
 +    }
 +    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
 +    // this impl.
 +    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
 +        return;
 +    }
 +
 +    span_lint_and_note(
 +        cx,
 +        EXPL_IMPL_CLONE_ON_COPY,
 +        item.span,
 +        "you are implementing `Clone` explicitly on a `Copy` type",
 +        Some(item.span),
 +        "consider deriving `Clone` or removing `Copy`",
 +    );
 +}
 +
 +/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
 +fn check_unsafe_derive_deserialize<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    item: &Item<'_>,
 +    trait_ref: &hir::TraitRef<'_>,
 +    ty: Ty<'tcx>,
 +) {
 +    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
 +        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
 +        walk_item(&mut visitor, item);
 +        visitor.has_unsafe
 +    }
 +
 +    if_chain! {
 +        if let Some(trait_def_id) = trait_ref.trait_def_id();
 +        if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
 +        if let ty::Adt(def, _) = ty.kind();
 +        if let Some(local_def_id) = def.did().as_local();
 +        let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
 +        if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id);
 +        if cx.tcx.inherent_impls(def.did())
 +            .iter()
 +            .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
 +            .any(|imp| has_unsafe(cx, imp));
 +        then {
 +            span_lint_and_help(
 +                cx,
 +                UNSAFE_DERIVE_DESERIALIZE,
 +                item.span,
 +                "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
 +                None,
 +                "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html"
 +            );
 +        }
 +    }
 +}
 +
 +struct UnsafeVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    has_unsafe: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, span: Span, id: HirId) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.unsafety == Unsafety::Unsafe;
 +            then {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_fn(self, kind, decl, body_id, span, id);
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.has_unsafe {
 +            return;
 +        }
 +
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
 +                self.has_unsafe = true;
 +            }
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
 +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
 +    if_chain! {
 +        if let ty::Adt(adt, substs) = ty.kind();
 +        if cx.tcx.visibility(adt.did()) == Visibility::Public;
 +        if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq);
 +        if let Some(def_id) = trait_ref.trait_def_id();
 +        if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id);
 +        let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id);
 +        if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
 +        // If all of our fields implement `Eq`, we can implement `Eq` too
 +        if adt
 +            .all_fields()
 +            .map(|f| f.ty(cx.tcx, substs))
 +            .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]));
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +                span.ctxt().outer_expn_data().call_site,
 +                "you are deriving `PartialEq` and can implement `Eq`",
 +                "consider deriving `Eq` as well",
 +                "PartialEq, Eq".to_string(),
 +                Applicability::MachineApplicable,
 +            )
 +        }
 +    }
 +}
 +
 +/// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
 +fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
 +    // Initial map from generic index to param def.
 +    // Vec<(param_def, needs_eq)>
 +    let mut params = tcx
 +        .generics_of(did)
 +        .params
 +        .iter()
 +        .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
 +        .collect::<Vec<_>>();
 +
 +    let ty_predicates = tcx.predicates_of(did).predicates;
 +    for (p, _) in ty_predicates {
 +        if let PredicateKind::Trait(p) = p.kind().skip_binder()
 +            && p.trait_ref.def_id == eq_trait_id
 +            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
 +            && p.constness == BoundConstness::NotConst
 +        {
 +            // Flag types which already have an `Eq` bound.
 +            params[self_ty.index as usize].1 = false;
 +        }
 +    }
 +
 +    ParamEnv::new(
 +        tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
 +            params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
 +                tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
++                    trait_ref: TraitRef::new(
++                        eq_trait_id,
++                        tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
++                    ),
 +                    constness: BoundConstness::NotConst,
 +                    polarity: ImplPolarity::Positive,
 +                })))
 +            }),
 +        )),
 +        Reveal::UserFacing,
 +        Constness::NotConst,
 +    )
 +}
index cb07f57e87006ff95368b8619b4bb559f983bc8a,0000000000000000000000000000000000000000..0ff1d2755daf6284c5babcdc77a6989e05497f61
mode 100644,000000..100644
--- /dev/null
@@@ -1,60 -1,0 +1,60 @@@
-     #[clippy::version = "1.60.0"]
 +use clippy_utils::diagnostics::span_lint;
 +use itertools::Itertools;
 +use rustc_ast::{AttrKind, Attribute};
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
 +    /// outside of code blocks
 +    /// ### Why is this bad?
 +    /// It is likely a typo when defining an intra-doc link
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// /// See also: ['foo']
 +    /// fn bar() {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// /// See also: [`foo`]
 +    /// fn bar() {}
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub DOC_LINK_WITH_QUOTES,
 +    pedantic,
 +    "possible typo for an intra-doc link"
 +}
 +declare_lint_pass!(DocLinkWithQuotes => [DOC_LINK_WITH_QUOTES]);
 +
 +impl EarlyLintPass for DocLinkWithQuotes {
 +    fn check_attribute(&mut self, ctx: &EarlyContext<'_>, attr: &Attribute) {
 +        if let AttrKind::DocComment(_, symbol) = attr.kind {
 +            if contains_quote_link(symbol.as_str()) {
 +                span_lint(
 +                    ctx,
 +                    DOC_LINK_WITH_QUOTES,
 +                    attr.span,
 +                    "possible intra-doc link using quotes instead of backticks",
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn contains_quote_link(s: &str) -> bool {
 +    let mut in_backticks = false;
 +    let mut found_opening = false;
 +
 +    for c in s.chars().tuple_windows::<(char, char)>() {
 +        match c {
 +            ('`', _) => in_backticks = !in_backticks,
 +            ('[', '\'') if !in_backticks => found_opening = true,
 +            ('\'', ']') if !in_backticks && found_opening => return true,
 +            _ => {},
 +        }
 +    }
 +
 +    false
 +}
index e1eb3b6324c7820091e7b10dfa23f8c80d6e157a,0000000000000000000000000000000000000000..7ff7068f0b05e56aec583ec5dd066ab280109193
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,128 @@@
-     #[clippy::version = "1.62.0"]
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
 +use rustc_errors::MultiSpan;
 +use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{FileName, Span};
 +use std::collections::BTreeMap;
 +use std::path::PathBuf;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for files that are included as modules multiple times.
 +    ///
 +    /// ### Why is this bad?
 +    /// Loading a file as a module more than once causes it to be compiled
 +    /// multiple times, taking longer and putting duplicate content into the
 +    /// module tree.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// // lib.rs
 +    /// mod a;
 +    /// mod b;
 +    /// ```
 +    /// ```rust,ignore
 +    /// // a.rs
 +    /// #[path = "./b.rs"]
 +    /// mod b;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    ///
 +    /// ```rust,ignore
 +    /// // lib.rs
 +    /// mod a;
 +    /// mod b;
 +    /// ```
 +    /// ```rust,ignore
 +    /// // a.rs
 +    /// use crate::b;
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub DUPLICATE_MOD,
 +    suspicious,
 +    "file loaded as module multiple times"
 +}
 +
 +#[derive(PartialOrd, Ord, PartialEq, Eq)]
 +struct Modules {
 +    local_path: PathBuf,
 +    spans: Vec<Span>,
 +    lint_levels: Vec<Level>,
 +}
 +
 +#[derive(Default)]
 +pub struct DuplicateMod {
 +    /// map from the canonicalized path to `Modules`, `BTreeMap` to make the
 +    /// order deterministic for tests
 +    modules: BTreeMap<PathBuf, Modules>,
 +}
 +
 +impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
 +
 +impl EarlyLintPass for DuplicateMod {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if let ItemKind::Mod(_, ModKind::Loaded(_, Inline::No, mod_spans)) = &item.kind
 +            && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span)
 +            && let Some(local_path) = real.into_local_path()
 +            && let Ok(absolute_path) = local_path.canonicalize()
 +        {
 +            let modules = self.modules.entry(absolute_path).or_insert(Modules {
 +                local_path,
 +                spans: Vec::new(),
 +                lint_levels: Vec::new(),
 +            });
 +            modules.spans.push(item.span_with_attributes());
 +            modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD));
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
 +        for Modules {
 +            local_path,
 +            spans,
 +            lint_levels,
 +        } in self.modules.values()
 +        {
 +            if spans.len() < 2 {
 +                continue;
 +            }
 +
 +            // At this point the lint would be emitted
 +            assert_eq!(spans.len(), lint_levels.len());
 +            let spans: Vec<_> = spans
 +                .iter()
 +                .zip(lint_levels)
 +                .filter_map(|(span, lvl)| {
 +                    if let Some(id) = lvl.get_expectation_id() {
 +                        cx.fulfill_expectation(id);
 +                    }
 +
 +                    (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
 +                })
 +                .collect();
 +
 +            if spans.len() < 2 {
 +                continue;
 +            }
 +
 +            let mut multi_span = MultiSpan::from_spans(spans.clone());
 +            let (&first, duplicates) = spans.split_first().unwrap();
 +
 +            multi_span.push_span_label(first, "first loaded here");
 +            for &duplicate in duplicates {
 +                multi_span.push_span_label(duplicate, "loaded again here");
 +            }
 +
 +            span_lint_and_help(
 +                cx,
 +                DUPLICATE_MOD,
 +                multi_span,
 +                &format!("file is loaded as a module multiple times: `{}`", local_path.display()),
 +                None,
 +                "replace all but one `mod` item with `use` items",
 +            );
 +        }
 +    }
 +}
index 1ac7bfba06ba217a38c4d23560be1ac841116126,0000000000000000000000000000000000000000..327865e4c858ab36e8166c5773aa505804a44cce
mode 100644,000000..100644
--- /dev/null
@@@ -1,199 -1,0 +1,192 @@@
- use clippy_utils::ty::contains_ty;
 +use clippy_utils::diagnostics::span_lint_hir;
-     /// # fn foo(bar: usize) {}
-     /// let x = Box::new(1);
-     /// foo(*x);
-     /// println!("{}", *x);
 +use rustc_hir::intravisit;
 +use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::mir::FakeReadCause;
 +use rustc_middle::ty::layout::LayoutOf;
 +use rustc_middle::ty::{self, TraitRef, Ty};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Span;
 +use rustc_span::symbol::kw;
 +use rustc_target::spec::abi::Abi;
 +use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 +
 +#[derive(Copy, Clone)]
 +pub struct BoxedLocal {
 +    pub too_large_for_stack: u64,
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `Box<T>` where an unboxed `T` would
 +    /// work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is an unnecessary allocation, and bad for
 +    /// performance. It is only necessary to allocate if you wish to move the box
 +    /// into something.
 +    ///
 +    /// ### Example
 +    /// ```rust
-     /// # fn foo(bar: usize) {}
-     /// let x = 1;
-     /// foo(x);
-     /// println!("{}", x);
++    /// fn foo(x: Box<u32>) {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
-                     if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
++    /// fn foo(x: u32) {}
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub BOXED_LOCAL,
 +    perf,
 +    "using `Box<T>` where unnecessary"
 +}
 +
 +fn is_non_trait_box(ty: Ty<'_>) -> bool {
 +    ty.is_box() && !ty.boxed_ty().is_trait()
 +}
 +
 +struct EscapeDelegate<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    set: HirIdSet,
 +    trait_self_ty: Option<Ty<'tcx>>,
 +    too_large_for_stack: u64,
 +}
 +
 +impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: intravisit::FnKind<'tcx>,
 +        _: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        _: Span,
 +        hir_id: HirId,
 +    ) {
 +        if let Some(header) = fn_kind.header() {
 +            if header.abi != Abi::Rust {
 +                return;
 +            }
 +        }
 +
 +        let parent_id = cx.tcx.hir().get_parent_item(hir_id);
 +        let parent_node = cx.tcx.hir().find_by_def_id(parent_id);
 +
 +        let mut trait_self_ty = None;
 +        if let Some(Node::Item(item)) = parent_node {
 +            // If the method is an impl for a trait, don't warn.
 +            if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind {
 +                return;
 +            }
 +
 +            // find `self` ty for this trait if relevant
 +            if let ItemKind::Trait(_, _, _, _, items) = item.kind {
 +                for trait_item in items {
 +                    if trait_item.id.hir_id() == hir_id {
 +                        // be sure we have `self` parameter in this function
 +                        if trait_item.kind == (AssocItemKind::Fn { has_self: true }) {
 +                            trait_self_ty = Some(
 +                                TraitRef::identity(cx.tcx, trait_item.id.def_id.to_def_id())
 +                                    .self_ty()
 +                                    .skip_binder(),
 +                            );
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +
 +        let mut v = EscapeDelegate {
 +            cx,
 +            set: HirIdSet::default(),
 +            trait_self_ty,
 +            too_large_for_stack: self.too_large_for_stack,
 +        };
 +
 +        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
 +        cx.tcx.infer_ctxt().enter(|infcx| {
 +            ExprUseVisitor::new(&mut v, &infcx, fn_def_id, cx.param_env, cx.typeck_results()).consume_body(body);
 +        });
 +
 +        for node in v.set {
 +            span_lint_hir(
 +                cx,
 +                BOXED_LOCAL,
 +                node,
 +                cx.tcx.hir().span(node),
 +                "local variable doesn't need to be boxed here",
 +            );
 +        }
 +    }
 +}
 +
 +// TODO: Replace with Map::is_argument(..) when it's fixed
 +fn is_argument(map: rustc_middle::hir::map::Map<'_>, id: HirId) -> bool {
 +    match map.find(id) {
 +        Some(Node::Pat(Pat {
 +            kind: PatKind::Binding(..),
 +            ..
 +        })) => (),
 +        _ => return false,
 +    }
 +
 +    matches!(map.find(map.get_parent_node(id)), Some(Node::Param(_)))
 +}
 +
 +impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
 +    fn consume(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        if cmt.place.projections.is_empty() {
 +            if let PlaceBase::Local(lid) = cmt.place.base {
 +                self.set.remove(&lid);
 +            }
 +        }
 +    }
 +
 +    fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {
 +        if cmt.place.projections.is_empty() {
 +            if let PlaceBase::Local(lid) = cmt.place.base {
 +                self.set.remove(&lid);
 +            }
 +        }
 +    }
 +
 +    fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
 +        if cmt.place.projections.is_empty() {
 +            let map = &self.cx.tcx.hir();
 +            if is_argument(*map, cmt.hir_id) {
 +                // Skip closure arguments
 +                let parent_id = map.get_parent_node(cmt.hir_id);
 +                if let Some(Node::Expr(..)) = map.find(map.get_parent_node(parent_id)) {
 +                    return;
 +                }
 +
 +                // skip if there is a `self` parameter binding to a type
 +                // that contains `Self` (i.e.: `self: Box<Self>`), see #4804
 +                if let Some(trait_self_ty) = self.trait_self_ty {
++                    if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
 +                        return;
 +                    }
 +                }
 +
 +                if is_non_trait_box(cmt.place.ty()) && !self.is_large_box(cmt.place.ty()) {
 +                    self.set.insert(cmt.hir_id);
 +                }
 +            }
 +        }
 +    }
 +
 +    fn fake_read(&mut self, _: &rustc_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
 +}
 +
 +impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
 +    fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
 +        // Large types need to be boxed to avoid stack overflows.
 +        if ty.is_box() {
 +            self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
 +        } else {
 +            false
 +        }
 +    }
 +}
index df9b41d2c98bef1fe10d2f1eca1fe10e41477aab,0000000000000000000000000000000000000000..bb50e8fcabbb712c15c4dd0cdae52368fbb8ae23
mode 100644,000000..100644
--- /dev/null
@@@ -1,735 -1,0 +1,735 @@@
-             format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
 +use clippy_utils::consts::{
 +    constant, constant_simple, Constant,
 +    Constant::{Int, F32, F64},
 +};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::{eq_expr_value, get_parent_expr, in_constant, numeric_literal, peel_blocks, sugg};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Spanned;
 +
 +use rustc_ast::ast;
 +use std::f32::consts as f32_consts;
 +use std::f64::consts as f64_consts;
 +use sugg::Sugg;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve accuracy
 +    /// at the cost of performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.powf(1.0 / 3.0);
 +    /// let _ = (1.0 + a).ln();
 +    /// let _ = a.exp() - 1.0;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = 3f32;
 +    /// let _ = a.cbrt();
 +    /// let _ = a.ln_1p();
 +    /// let _ = a.exp_m1();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub IMPRECISE_FLOPS,
 +    nursery,
 +    "usage of imprecise floating point operations"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Looks for floating-point expressions that
 +    /// can be expressed using built-in methods to improve both
 +    /// accuracy and performance.
 +    ///
 +    /// ### Why is this bad?
 +    /// Negatively impacts accuracy and performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = (2f32).powf(a);
 +    /// let _ = E.powf(a);
 +    /// let _ = a.powf(1.0 / 2.0);
 +    /// let _ = a.log(2.0);
 +    /// let _ = a.log(10.0);
 +    /// let _ = a.log(E);
 +    /// let _ = a.powf(2.0);
 +    /// let _ = a * 2.0 + 4.0;
 +    /// let _ = if a < 0.0 {
 +    ///     -a
 +    /// } else {
 +    ///     a
 +    /// };
 +    /// let _ = if a < 0.0 {
 +    ///     a
 +    /// } else {
 +    ///     -a
 +    /// };
 +    /// ```
 +    ///
 +    /// is better expressed as
 +    ///
 +    /// ```rust
 +    /// use std::f32::consts::E;
 +    ///
 +    /// let a = 3f32;
 +    /// let _ = a.exp2();
 +    /// let _ = a.exp();
 +    /// let _ = a.sqrt();
 +    /// let _ = a.log2();
 +    /// let _ = a.log10();
 +    /// let _ = a.ln();
 +    /// let _ = a.powi(2);
 +    /// let _ = a.mul_add(2.0, 4.0);
 +    /// let _ = a.abs();
 +    /// let _ = -a.abs();
 +    /// ```
 +    #[clippy::version = "1.43.0"]
 +    pub SUBOPTIMAL_FLOPS,
 +    nursery,
 +    "usage of sub-optimal floating point operations"
 +}
 +
 +declare_lint_pass!(FloatingPointArithmetic => [
 +    IMPRECISE_FLOPS,
 +    SUBOPTIMAL_FLOPS
 +]);
 +
 +// Returns the specialized log method for a given base if base is constant
 +// and is one of 2, 10 and e
 +fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), base) {
 +        if F32(2.0) == value || F64(2.0) == value {
 +            return Some("log2");
 +        } else if F32(10.0) == value || F64(10.0) == value {
 +            return Some("log10");
 +        } else if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            return Some("ln");
 +        }
 +    }
 +
 +    None
 +}
 +
 +// Adds type suffixes and parenthesis to method receivers if necessary
 +fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Sugg<'a> {
 +    let mut suggestion = Sugg::hir(cx, expr, "..");
 +
 +    if let ExprKind::Unary(UnOp::Neg, inner_expr) = &expr.kind {
 +        expr = inner_expr;
 +    }
 +
 +    if_chain! {
 +        // if the expression is a float literal and it is unsuffixed then
 +        // add a suffix so the suggestion is valid and unambiguous
 +        if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind();
 +        if let ExprKind::Lit(lit) = &expr.kind;
 +        if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node;
 +        then {
 +            let op = format!(
 +                "{}{}{}",
 +                suggestion,
 +                // Check for float literals without numbers following the decimal
 +                // separator such as `2.` and adds a trailing zero
 +                if sym.as_str().ends_with('.') {
 +                    "0"
 +                } else {
 +                    ""
 +                },
 +                float_ty.name_str()
 +            ).into();
 +
 +            suggestion = match suggestion {
 +                Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
 +                _ => Sugg::NonParen(op)
 +            };
 +        }
 +    }
 +
 +    suggestion.maybe_par()
 +}
 +
 +fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(method) = get_specialized_log_method(cx, &args[1]) {
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "logarithm for bases 2, 10 and e can be computed more accurately",
 +            "consider using",
-                 format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
++            format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `(x + y).ln()` where y > 1 and
 +// suggest usage of `(x + (y - 1)).ln_1p()` instead
 +fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &args[0].kind
 +    {
 +        let recv = match (
 +            constant(cx, cx.typeck_results(), lhs),
 +            constant(cx, cx.typeck_results(), rhs),
 +        ) {
 +            (Some((value, _)), _) if F32(1.0) == value || F64(1.0) == value => rhs,
 +            (_, Some((value, _))) if F32(1.0) == value || F64(1.0) == value => lhs,
 +            _ => return,
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "ln(1 + x) can be computed more accurately",
 +            "consider using",
 +            format!("{}.ln_1p()", prepare_receiver_sugg(cx, recv)),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// Returns an integer if the float constant is a whole number and it can be
 +// converted to an integer without loss of precision. For now we only check
 +// ranges [-16777215, 16777216) for type f32 as whole number floats outside
 +// this range are lossy and ambiguous.
 +#[expect(clippy::cast_possible_truncation)]
 +fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
 +    match value {
 +        F32(num) if num.fract() == 0.0 => {
 +            if (-16_777_215.0..16_777_216.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        F64(num) if num.fract() == 0.0 => {
 +            if (-2_147_483_648.0..2_147_483_648.0).contains(num) {
 +                Some(num.round() as i32)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    // Check receiver
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[0]) {
 +        let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
 +            "exp"
 +        } else if F32(2.0) == value || F64(2.0) == value {
 +            "exp2"
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "exponent for bases 2 and e can be computed more accurately",
 +            "consider using",
 +            format!("{}.{}()", prepare_receiver_sugg(cx, &args[1]), method),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +
 +    // Check argument
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
 +        let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "square-root of a number can be computed more efficiently and accurately",
-                 format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
++                format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
 +            )
 +        } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
 +            (
 +                IMPRECISE_FLOPS,
 +                "cube-root of a number can be computed more accurately",
-                     Sugg::hir(cx, &args[0], ".."),
++                format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
 +            )
 +        } else if let Some(exponent) = get_integer_from_float_constant(&value) {
 +            (
 +                SUBOPTIMAL_FLOPS,
 +                "exponentiation with integer powers can be computed more efficiently",
 +                format!(
 +                    "{}.powi({})",
-                             Sugg::hir(cx, &args[0], ".."),
++                    Sugg::hir(cx, &args[0], "..").maybe_par(),
 +                    numeric_literal::format(&exponent.to_string(), None, false)
 +                ),
 +            )
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            lint,
 +            expr.span,
 +            help,
 +            "consider using",
 +            suggestion,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some((value, _)) = constant(cx, cx.typeck_results(), &args[1]) {
 +        if value == Int(2) {
 +            if let Some(parent) = get_parent_expr(cx, expr) {
 +                if let Some(grandparent) = get_parent_expr(cx, parent) {
 +                    if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, args, _) = grandparent.kind {
 +                        if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
 +                            return;
 +                        }
 +                    }
 +                }
 +
 +                if let ExprKind::Binary(
 +                    Spanned {
 +                        node: BinOpKind::Add, ..
 +                    },
 +                    lhs,
 +                    rhs,
 +                ) = parent.kind
 +                {
 +                    let other_addend = if lhs.hir_id == expr.hir_id { rhs } else { lhs };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        SUBOPTIMAL_FLOPS,
 +                        parent.span,
 +                        "multiply and add expressions can be calculated more efficiently and accurately",
 +                        "consider using",
 +                        format!(
 +                            "{}.mul_add({}, {})",
-                     Sugg::hir(cx, self_arg, "..")
++                            Sugg::hir(cx, &args[0], "..").maybe_par(),
 +                            Sugg::hir(cx, &args[0], ".."),
 +                            Sugg::hir(cx, other_addend, ".."),
 +                        ),
 +                        Applicability::MachineApplicable,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn detect_hypot(cx: &LateContext<'_>, args: &[Expr<'_>]) -> Option<String> {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        add_lhs,
 +        add_rhs,
 +    ) = args[0].kind
 +    {
 +        // check if expression of the form x * x + y * y
 +        if_chain! {
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind;
 +            if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind;
 +            if eq_expr_value(cx, lmul_lhs, lmul_rhs);
 +            if eq_expr_value(cx, rmul_lhs, rmul_rhs);
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, "..")));
 +            }
 +        }
 +
 +        // check if expression of the form x.powi(2) + y.powi(2)
 +        if_chain! {
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: lmethod_name, .. },
 +                [largs_0, largs_1, ..],
 +                _
 +            ) = &add_lhs.kind;
 +            if let ExprKind::MethodCall(
 +                PathSegment { ident: rmethod_name, .. },
 +                [rargs_0, rargs_1, ..],
 +                _
 +            ) = &add_rhs.kind;
 +            if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi";
 +            if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), largs_1);
 +            if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), rargs_1);
 +            if Int(2) == lvalue && Int(2) == rvalue;
 +            then {
 +                return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, "..")));
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
 +    if let Some(message) = detect_hypot(cx, args) {
 +        span_lint_and_sugg(
 +            cx,
 +            IMPRECISE_FLOPS,
 +            expr.span,
 +            "hypotenuse can be computed more accurately",
 +            "consider using",
 +            message,
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +// TODO: Lint expressions of the form `x.exp() - y` where y > 1
 +// and suggest usage of `x.exp_m1() - (y - 1)` instead
 +fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if let Some((value, _)) = constant(cx, cx.typeck_results(), rhs);
 +        if F32(1.0) == value || F64(1.0) == value;
 +        if let ExprKind::MethodCall(path, [self_arg, ..], _) = &lhs.kind;
 +        if cx.typeck_results().expr_ty(self_arg).is_floating_point();
 +        if path.ident.name.as_str() == "exp";
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                IMPRECISE_FLOPS,
 +                expr.span,
 +                "(e.pow(x) - 1) can be computed more accurately",
 +                "consider using",
 +                format!(
 +                    "{}.exp_m1()",
-                 format!("{}.abs()", Sugg::hir(cx, body, "..")),
++                    Sugg::hir(cx, self_arg, "..").maybe_par()
 +                ),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
 +    if_chain! {
 +        if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind;
 +        if cx.typeck_results().expr_ty(lhs).is_floating_point();
 +        if cx.typeck_results().expr_ty(rhs).is_floating_point();
 +        then {
 +            return Some((lhs, rhs));
 +        }
 +    }
 +
 +    None
 +}
 +
 +// TODO: Fix rust-lang/rust-clippy#4735
 +fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if let ExprKind::Binary(
 +        Spanned {
 +            node: BinOpKind::Add, ..
 +        },
 +        lhs,
 +        rhs,
 +    ) = &expr.kind
 +    {
 +        if let Some(parent) = get_parent_expr(cx, expr) {
 +            if let ExprKind::MethodCall(PathSegment { ident: method_name, .. }, args, _) = parent.kind {
 +                if method_name.as_str() == "sqrt" && detect_hypot(cx, args).is_some() {
 +                    return;
 +                }
 +            }
 +        }
 +
 +        let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs) {
 +            (inner_lhs, inner_rhs, rhs)
 +        } else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs) {
 +            (inner_lhs, inner_rhs, lhs)
 +        } else {
 +            return;
 +        };
 +
 +        span_lint_and_sugg(
 +            cx,
 +            SUBOPTIMAL_FLOPS,
 +            expr.span,
 +            "multiply and add expressions can be calculated more efficiently and accurately",
 +            "consider using",
 +            format!(
 +                "{}.mul_add({}, {})",
 +                prepare_receiver_sugg(cx, recv),
 +                Sugg::hir(cx, arg1, ".."),
 +                Sugg::hir(cx, arg2, ".."),
 +            ),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
 +
 +/// Returns true iff expr is an expression which tests whether or not
 +/// test is positive or an expression which tests whether or not test
 +/// is nonnegative.
 +/// Used for check-custom-abs function below
 +fn is_testing_positive(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// See [`is_testing_positive`]
 +fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -> bool {
 +    if let ExprKind::Binary(Spanned { node: op, .. }, left, right) = expr.kind {
 +        match op {
 +            BinOpKind::Gt | BinOpKind::Ge => is_zero(cx, left) && eq_expr_value(cx, right, test),
 +            BinOpKind::Lt | BinOpKind::Le => is_zero(cx, right) && eq_expr_value(cx, left, test),
 +            _ => false,
 +        }
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Returns true iff expr is some zero literal
 +fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    match constant_simple(cx, cx.typeck_results(), expr) {
 +        Some(Constant::Int(i)) => i == 0,
 +        Some(Constant::F32(f)) => f == 0.0,
 +        Some(Constant::F64(f)) => f == 0.0,
 +        _ => false,
 +    }
 +}
 +
 +/// If the two expressions are negations of each other, then it returns
 +/// a tuple, in which the first element is true iff expr1 is the
 +/// positive expressions, and the second element is the positive
 +/// one of the two expressions
 +/// If the two expressions are not negations of each other, then it
 +/// returns None.
 +fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a>) -> Option<(bool, &'a Expr<'a>)> {
 +    if let ExprKind::Unary(UnOp::Neg, expr1_negated) = &expr1.kind {
 +        if eq_expr_value(cx, expr1_negated, expr2) {
 +            return Some((false, expr2));
 +        }
 +    }
 +    if let ExprKind::Unary(UnOp::Neg, expr2_negated) = &expr2.kind {
 +        if eq_expr_value(cx, expr1, expr2_negated) {
 +            return Some((true, expr1));
 +        }
 +    }
 +    None
 +}
 +
 +fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr);
 +        let if_body_expr = peel_blocks(then);
 +        let else_body_expr = peel_blocks(r#else);
 +        if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
 +        then {
 +            let positive_abs_sugg = (
 +                "manual implementation of `abs` method",
-                 format!("-{}.abs()", Sugg::hir(cx, body, "..")),
++                format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
 +            );
 +            let negative_abs_sugg = (
 +                "manual implementation of negation of `abs` method",
-                 format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
++                format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
 +            );
 +            let sugg = if is_testing_positive(cx, cond, body) {
 +                if if_expr_positive {
 +                    positive_abs_sugg
 +                } else {
 +                    negative_abs_sugg
 +                }
 +            } else if is_testing_negative(cx, cond, body) {
 +                if if_expr_positive {
 +                    negative_abs_sugg
 +                } else {
 +                    positive_abs_sugg
 +                }
 +            } else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                sugg.0,
 +                "try",
 +                sugg.1,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, args_a, _) = expr_a.kind;
 +        if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, args_b, _) = expr_b.kind;
 +        then {
 +            return method_name_a.as_str() == method_name_b.as_str() &&
 +                args_a.len() == args_b.len() &&
 +                (
 +                    ["ln", "log2", "log10"].contains(&method_name_a.as_str()) ||
 +                    method_name_a.as_str() == "log" && args_a.len() == 2 && eq_expr_value(cx, &args_a[1], &args_b[1])
 +                );
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    // check if expression of the form x.logN() / y.logN()
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) = &expr.kind;
 +        if are_same_base_logs(cx, lhs, rhs);
 +        if let ExprKind::MethodCall(_, [largs_self, ..], _) = &lhs.kind;
 +        if let ExprKind::MethodCall(_, [rargs_self, ..], _) = &rhs.kind;
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                SUBOPTIMAL_FLOPS,
 +                expr.span,
 +                "log base can be expressed more clearly",
 +                "consider using",
-                 let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
++                format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Div, ..
 +            },
 +            div_lhs,
 +            div_rhs,
 +        ) = &expr.kind;
 +        if let ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Mul, ..
 +            },
 +            mul_lhs,
 +            mul_rhs,
 +        ) = &div_lhs.kind;
 +        if let Some((rvalue, _)) = constant(cx, cx.typeck_results(), div_rhs);
 +        if let Some((lvalue, _)) = constant(cx, cx.typeck_results(), mul_rhs);
 +        then {
 +            // TODO: also check for constant values near PI/180 or 180/PI
 +            if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
 +               (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
 +            {
-                 let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
++                let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
 +                if_chain! {
 +                    if let ExprKind::Lit(ref literal) = mul_lhs.kind;
 +                    if let ast::LitKind::Float(ref value, float_type) = literal.node;
 +                    if float_type == ast::LitFloatType::Unsuffixed;
 +                    then {
 +                        if value.as_str().ends_with('.') {
 +                            proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                        } else {
 +                            proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
 +                        }
 +                    }
 +                }
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to degrees can be done more accurately",
 +                    "consider using",
 +                    proposal,
 +                    Applicability::MachineApplicable,
 +                );
 +            } else if
 +                (F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
 +                (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
 +            {
++                let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
 +                if_chain! {
 +                    if let ExprKind::Lit(ref literal) = mul_lhs.kind;
 +                    if let ast::LitKind::Float(ref value, float_type) = literal.node;
 +                    if float_type == ast::LitFloatType::Unsuffixed;
 +                    then {
 +                        if value.as_str().ends_with('.') {
 +                            proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                        } else {
 +                            proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
 +                        }
 +                    }
 +                }
 +                span_lint_and_sugg(
 +                    cx,
 +                    SUBOPTIMAL_FLOPS,
 +                    expr.span,
 +                    "conversion to radians can be done more accurately",
 +                    "consider using",
 +                    proposal,
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        // All of these operations are currently not const.
 +        if in_constant(cx, expr.hir_id) {
 +            return;
 +        }
 +
 +        if let ExprKind::MethodCall(path, args, _) = &expr.kind {
 +            let recv_ty = cx.typeck_results().expr_ty(&args[0]);
 +
 +            if recv_ty.is_floating_point() {
 +                match path.ident.name.as_str() {
 +                    "ln" => check_ln1p(cx, expr, args),
 +                    "log" => check_log_base(cx, expr, args),
 +                    "powf" => check_powf(cx, expr, args),
 +                    "powi" => check_powi(cx, expr, args),
 +                    "sqrt" => check_hypot(cx, expr, args),
 +                    _ => {},
 +                }
 +            }
 +        } else {
 +            check_expm1(cx, expr);
 +            check_mul_add(cx, expr);
 +            check_custom_abs(cx, expr);
 +            check_log_division(cx, expr);
 +            check_radians(cx, expr);
 +        }
 +    }
 +}
index 925a8cb8deed94ff792f3f4a7ce144ec5b2486c5,0000000000000000000000000000000000000000..0c5851cdbed2a4241e0a3f2d35901f311dd732ec
mode 100644,000000..100644
--- /dev/null
@@@ -1,123 -1,0 +1,121 @@@
- use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
-         if format_args.value_args.is_empty() {
-             match *format_args.format_string_parts {
++use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::sugg::Sugg;
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::symbol::kw;
 +use rustc_span::{sym, 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
 +    /// let foo = "foo";
 +    /// format!("{}", foo);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let foo = "foo";
 +    /// foo.to_owned();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_FORMAT,
 +    complexity,
 +    "useless use of `format!`"
 +}
 +
 +declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 +
 +impl<'tcx> LateLintPass<'tcx> for UselessFormat {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let (format_args, call_site) = if_chain! {
 +            if let Some(macro_call) = root_macro_call_first_node(cx, expr);
 +            if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id);
 +            if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn);
 +            then {
 +                (format_args, macro_call.span)
 +            } else {
 +                return
 +            }
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
-                     if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
-                         // Simulate macro expansion, converting {{ and }} to { and }.
-                         let s_expand = s_src.replace("{{", "{").replace("}}", "}");
-                         let sugg = format!("{}.to_string()", s_expand);
-                         span_useless_format(cx, call_site, sugg, applicability);
-                     }
++        if format_args.args.is_empty() {
++            match *format_args.format_string.parts {
 +                [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
 +                [_] => {
-         } else if let [value] = *format_args.value_args {
++                    // Simulate macro expansion, converting {{ and }} to { and }.
++                    let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
++                    let sugg = format!("{}.to_string()", s_expand);
++                    span_useless_format(cx, call_site, sugg, applicability);
 +                },
 +                [..] => {},
 +            }
-                 if format_args.format_string_parts == [kw::Empty];
++        } else if let [arg] = &*format_args.args {
++            let value = arg.param.value;
 +            if_chain! {
-                 if let Some(args) = format_args.args();
-                 if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
++                if format_args.format_string.parts == [kw::Empty];
 +                if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
 +                    ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
 +                    ty::Str => true,
 +                    _ => false,
 +                };
++                if !arg.format.has_string_formatting();
 +                then {
 +                    let is_new_string = match value.kind {
 +                        ExprKind::Binary(..) => true,
 +                        ExprKind::MethodCall(path, ..) => path.ident.name == sym::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_empty(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        USELESS_FORMAT,
 +        span,
 +        "useless use of `format!`",
 +        "consider using `String::new()`",
 +        sugg,
 +        applicability,
 +    );
 +}
 +
 +fn span_useless_format(cx: &LateContext<'_>, span: Span, sugg: String, applicability: Applicability) {
 +    span_lint_and_sugg(
 +        cx,
 +        USELESS_FORMAT,
 +        span,
 +        "useless use of `format!`",
 +        "consider using `.to_string()`",
 +        sugg,
 +        applicability,
 +    );
 +}
index 1e6feaac26c3ab77e604572bd461efcc27803ca3,0000000000000000000000000000000000000000..9fb9fd99748b4b63eabb571073dcba3306d938fb
mode 100644,000000..100644
--- /dev/null
@@@ -1,199 -1,0 +1,207 @@@
- use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::is_diag_trait_item;
- use rustc_hir::{Expr, ExprKind};
++use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::implements_trait;
 +use if_chain::if_chain;
++use itertools::Itertools;
 +use rustc_errors::Applicability;
-             if let Some(args) = format_args.args();
++use rustc_hir::{Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Detects `format!` within the arguments of another macro that does
 +    /// formatting such as `format!` itself, `write!` or `println!`. Suggests
 +    /// inlining the `format!` call.
 +    ///
 +    /// ### Why is this bad?
 +    /// The recommended code is both shorter and avoids a temporary allocation.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: {}", format!("something failed at {}", Location::caller()));
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub FORMAT_IN_FORMAT_ARGS,
 +    perf,
 +    "`format!` used in a macro that does formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for [`ToString::to_string`](https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string)
 +    /// applied to a type that implements [`Display`](https://doc.rust-lang.org/std/fmt/trait.Display.html)
 +    /// in a macro that does formatting.
 +    ///
 +    /// ### Why is this bad?
 +    /// Since the type implements `Display`, the use of `to_string` is
 +    /// unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller().to_string());
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::panic::Location;
 +    /// println!("error: something failed at {}", Location::caller());
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TO_STRING_IN_FORMAT_ARGS,
 +    perf,
 +    "`to_string` applied to a type that implements `Display` in format args"
 +}
 +
 +declare_lint_pass!(FormatArgs => [FORMAT_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FormatArgs {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if_chain! {
 +            if let Some(format_args) = FormatArgsExpn::parse(cx, expr);
 +            let expr_expn_data = expr.span.ctxt().outer_expn_data();
 +            let outermost_expn_data = outermost_expn_data(expr_expn_data);
 +            if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
 +            if is_format_macro(cx, macro_def_id);
 +            if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
-                 for (i, arg) in args.iter().enumerate() {
-                     if arg.format_trait != sym::Display {
 +            then {
-                     if arg.has_string_formatting() {
++                for arg in &format_args.args {
++                    if arg.format.has_string_formatting() {
 +                        continue;
 +                    }
-                     if is_aliased(&args, i) {
-                         continue;
-                     }
-                     check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
-                     check_to_string_in_format_args(cx, name, arg.value);
++                    if is_aliased(&format_args, arg.param.value.hir_id) {
 +                        continue;
 +                    }
-             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,
-                     );
-                 }
++                    check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
++                    check_to_string_in_format_args(cx, name, arg.param.value);
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
 +    if expn_data.call_site.from_expansion() {
 +        outermost_expn_data(expn_data.call_site.ctxt().outer_expn_data())
 +    } else {
 +        expn_data
 +    }
 +}
 +
 +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
 +    let expn_data = arg.span.ctxt().outer_expn_data();
 +    if expn_data.call_site.from_expansion() {
 +        return;
 +    }
 +    let Some(mac_id) = expn_data.macro_def_id else { return };
 +    if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) {
 +        return;
 +    }
 +    span_lint_and_then(
 +        cx,
 +        FORMAT_IN_FORMAT_ARGS,
 +        call_site,
 +        &format!("`format!` in `{}!` args", name),
 +        |diag| {
 +            diag.help(&format!(
 +                "combine the `format!(..)` arguments with the outer `{}!(..)` call",
 +                name
 +            ));
 +            diag.help("or consider changing `format!` to `format_args!`");
 +        },
 +    );
 +}
 +
 +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
 +    if_chain! {
 +        if !value.span.from_expansion();
 +        if let ExprKind::MethodCall(_, [receiver], _) = value.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id);
 +        if is_diag_trait_item(cx, method_def_id, sym::ToString);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
++        let (n_needed_derefs, target) =
++            count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
++        if implements_trait(cx, target, display_trait_id, &[]);
++        if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
- // 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))
++            let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
++            if n_needed_derefs == 0 && !needs_ref {
++                span_lint_and_sugg(
++                    cx,
++                    TO_STRING_IN_FORMAT_ARGS,
++                    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$}{}",
++                        if needs_ref { "&" } else { "" },
++                        "",
++                        receiver_snippet,
++                        width = n_needed_derefs
++                    ),
++                    Applicability::MachineApplicable,
++                );
 +            }
 +        }
 +    }
 +}
 +
++// Returns true if `hir_id` is referred to by multiple format params
++fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
++    args.params()
++        .filter(|param| param.value.hir_id == hir_id)
++        .at_most_one()
++        .is_err()
 +}
 +
 +fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
 +where
 +    I: Iterator<Item = &'tcx Adjustment<'tcx>>,
 +{
 +    let mut n_total = 0;
 +    let mut n_needed = 0;
 +    loop {
 +        if let Some(Adjustment {
 +            kind: Adjust::Deref(overloaded_deref),
 +            target,
 +        }) = iter.next()
 +        {
 +            n_total += 1;
 +            if overloaded_deref.is_some() {
 +                n_needed = n_total;
 +            }
 +            ty = *target;
 +        } else {
 +            return (n_needed, ty);
 +        }
 +    }
 +}
index 04b5be6c80ec6acf2727dfeaec769f0f71f43de0,0000000000000000000000000000000000000000..d8bc0bf08f2b314b8513902d14f6da434e229d74
mode 100644,000000..100644
--- /dev/null
@@@ -1,253 -1,0 +1,252 @@@
- use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-         if let Some(args) = format_args.args();
++use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
 +use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, symbol::kw, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
 +    /// which uses `self` as a parameter.
 +    /// This is typically done indirectly with the `write!` macro or with `to_string()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This will lead to infinite recursion and a stack overflow.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// struct Structure(i32);
 +    /// impl fmt::Display for Structure {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "{}", self.to_string())
 +    ///     }
 +    /// }
 +    ///
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::fmt;
 +    ///
 +    /// struct Structure(i32);
 +    /// impl fmt::Display for Structure {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
 +    ///         write!(f, "{}", self.0)
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub RECURSIVE_FORMAT_IMPL,
 +    correctness,
 +    "Format trait method called while implementing the same Format trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `println`, `print`, `eprintln` or `eprint` in an
 +    /// implementation of a formatting trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using a print macro is likely unintentional since formatting traits
 +    /// should write to the `Formatter`, not stdout/stderr.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::fmt::{Display, Error, Formatter};
 +    ///
 +    /// struct S;
 +    /// impl Display for S {
 +    ///     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
 +    ///         println!("S");
 +    ///
 +    ///         Ok(())
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// use std::fmt::{Display, Error, Formatter};
 +    ///
 +    /// struct S;
 +    /// impl Display for S {
 +    ///     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
 +    ///         writeln!(f, "S");
 +    ///
 +    ///         Ok(())
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub PRINT_IN_FORMAT_IMPL,
 +    suspicious,
 +    "use of a print macro in a formatting trait impl"
 +}
 +
 +#[derive(Clone, Copy)]
 +struct FormatTrait {
 +    /// e.g. `sym::Display`
 +    name: Symbol,
 +    /// `f` in `fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {}`
 +    formatter_name: Option<Symbol>,
 +}
 +
 +#[derive(Default)]
 +pub struct FormatImpl {
 +    // Whether we are inside Display or Debug trait impl - None for neither
 +    format_trait_impl: Option<FormatTrait>,
 +}
 +
 +impl FormatImpl {
 +    pub fn new() -> Self {
 +        Self {
 +            format_trait_impl: None,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(FormatImpl => [RECURSIVE_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL]);
 +
 +impl<'tcx> LateLintPass<'tcx> for FormatImpl {
 +    fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
 +        self.format_trait_impl = is_format_trait_impl(cx, impl_item);
 +    }
 +
 +    fn check_impl_item_post(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
 +        // Assume no nested Impl of Debug and Display within eachother
 +        if is_format_trait_impl(cx, impl_item).is_some() {
 +            self.format_trait_impl = None;
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        let Some(format_trait_impl) = self.format_trait_impl else { return };
 +
 +        if format_trait_impl.name == sym::Display {
 +            check_to_string_in_display(cx, expr);
 +        }
 +
 +        check_self_in_format_args(cx, expr, format_trait_impl);
 +        check_print_in_format_impl(cx, expr, format_trait_impl);
 +    }
 +}
 +
 +fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        // Get the hir_id of the object we are calling the method on
 +        if let ExprKind::MethodCall(path, [ref self_arg, ..], _) = expr.kind;
 +        // Is the method to_string() ?
 +        if path.ident.name == sym::to_string;
 +        // Is the method a part of the ToString trait? (i.e. not to_string() implemented
 +        // separately)
 +        if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if is_diag_trait_item(cx, expr_def_id, sym::ToString);
 +        // Is the method is called on self
 +        if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind;
 +        if let [segment] = path.segments;
 +        if segment.ident.name == kw::SelfLower;
 +        then {
 +            span_lint(
 +                cx,
 +                RECURSIVE_FORMAT_IMPL,
 +                expr.span,
 +                "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
 +            );
 +        }
 +    }
 +}
 +
 +fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTrait) {
 +    // Check each arg in format calls - do we ever use Display on self (directly or via deref)?
 +    if_chain! {
 +        if let Some(outer_macro) = root_macro_call_first_node(cx, expr);
 +        if let macro_def_id = outer_macro.def_id;
 +        if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
 +        if is_format_macro(cx, macro_def_id);
-             for arg in args {
-                 if arg.format_trait != impl_trait.name {
 +        then {
- fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
++            for arg in format_args.args {
++                if arg.format.r#trait != impl_trait.name {
 +                    continue;
 +                }
 +                check_format_arg_self(cx, expr, &arg, impl_trait);
 +            }
 +        }
 +    }
 +}
 +
-     let reference = peel_ref_operators(cx, arg.value);
++fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
 +    // Handle multiple dereferencing of references e.g. &&self
 +    // Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
 +    // Since the argument to fmt is itself a reference: &self
++    let reference = peel_ref_operators(cx, arg.param.value);
 +    let map = cx.tcx.hir();
 +    // Is the reference self?
 +    if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
 +        let FormatTrait { name, .. } = impl_trait;
 +        span_lint(
 +            cx,
 +            RECURSIVE_FORMAT_IMPL,
 +            expr.span,
 +            &format!("using `self` as `{name}` in `impl {name}` will cause infinite recursion"),
 +        );
 +    }
 +}
 +
 +fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTrait) {
 +    if_chain! {
 +        if let Some(macro_call) = root_macro_call_first_node(cx, expr);
 +        if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id);
 +        then {
 +            let replacement = match name {
 +                sym::print_macro | sym::eprint_macro => "write",
 +                sym::println_macro | sym::eprintln_macro => "writeln",
 +                _ => return,
 +            };
 +
 +            let name = name.as_str().strip_suffix("_macro").unwrap();
 +
 +            span_lint_and_sugg(
 +                cx,
 +                PRINT_IN_FORMAT_IMPL,
 +                macro_call.span,
 +                &format!("use of `{}!` in `{}` impl", name, impl_trait.name),
 +                "replace with",
 +                if let Some(formatter_name) = impl_trait.formatter_name {
 +                    format!("{}!({}, ..)", replacement, formatter_name)
 +                } else {
 +                    format!("{}!(..)", replacement)
 +                },
 +                Applicability::HasPlaceholders,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTrait> {
 +    if_chain! {
 +        if impl_item.ident.name == sym::fmt;
 +        if let ImplItemKind::Fn(_, body_id) = impl_item.kind;
 +        if let Some(Impl { of_trait: Some(trait_ref),..}) = get_parent_as_impl(cx.tcx, impl_item.hir_id());
 +        if let Some(did) = trait_ref.trait_def_id();
 +        if let Some(name) = cx.tcx.get_diagnostic_name(did);
 +        if matches!(name, sym::Debug | sym::Display);
 +        then {
 +            let body = cx.tcx.hir().body(body_id);
 +            let formatter_name = body.params.get(1)
 +                .and_then(|param| param.pat.simple_ident())
 +                .map(|ident| ident.name);
 +
 +            Some(FormatTrait {
 +                name,
 +                formatter_name,
 +            })
 +        } else {
 +            None
 +        }
 +    }
 +}
index 73261fb8a44c7c9ed0891bdb971b3f40a8edcaa9,0000000000000000000000000000000000000000..90911e0bf2595ca667b9b3f14b0a755c0e4290f6
mode 100644,000000..100644
--- /dev/null
@@@ -1,276 -1,0 +1,322 @@@
- mod result_unit_err;
 +mod must_use;
 +mod not_unsafe_ptr_arg_deref;
-     pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
++mod result;
 +mod too_many_arguments;
 +mod too_many_lines;
 +
 +use rustc_hir as hir;
 +use rustc_hir::intravisit;
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions with too many parameters.
 +    ///
 +    /// ### Why is this bad?
 +    /// Functions with lots of parameters are considered bad
 +    /// style and reduce readability (“what does the 5th parameter mean?”). Consider
 +    /// grouping some parameters into a new type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct Color;
 +    /// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TOO_MANY_ARGUMENTS,
 +    complexity,
 +    "functions with too many arguments"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for functions with a large amount of lines.
 +    ///
 +    /// ### Why is this bad?
 +    /// Functions with a lot of lines are harder to understand
 +    /// due to having to look at a larger amount of code to understand what the
 +    /// function is doing. Consider splitting the body of the function into
 +    /// multiple functions.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn im_too_long() {
 +    ///     println!("");
 +    ///     // ... 100 more LoC
 +    ///     println!("");
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.34.0"]
 +    pub TOO_MANY_LINES,
 +    pedantic,
 +    "functions with too many lines"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that dereference raw pointer
 +    /// arguments but are not marked `unsafe`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The function should probably be marked `unsafe`, since
 +    /// for an arbitrary raw pointer, there is no way of telling for sure if it is
 +    /// valid.
 +    ///
 +    /// ### Known problems
 +    /// * It does not check functions recursively so if the pointer is passed to a
 +    /// private non-`unsafe` function which does the dereferencing, the lint won't
 +    /// trigger.
 +    /// * It only checks for arguments whose type are raw pointers, not raw pointers
 +    /// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
 +    /// `some_argument.get_raw_ptr()`).
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// pub fn foo(x: *const u8) {
 +    ///     println!("{}", unsafe { *x });
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// pub unsafe fn foo(x: *const u8) {
 +    ///     println!("{}", unsafe { *x });
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NOT_UNSAFE_PTR_ARG_DEREF,
 +    correctness,
 +    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a `#[must_use]` attribute on
 +    /// unit-returning functions and methods.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit values are useless. The attribute is likely
 +    /// a remnant of a refactoring that removed the return type.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// #[must_use]
 +    /// fn useless() { }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub MUST_USE_UNIT,
 +    style,
 +    "`#[must_use]` attribute on a unit-returning function / method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for a `#[must_use]` attribute without
 +    /// further information on functions and methods that return a type already
 +    /// marked as `#[must_use]`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The attribute isn't needed. Not using the result
 +    /// will already be reported. Alternatively, one can add some text to the
 +    /// attribute to improve the lint message.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// #[must_use]
 +    /// fn double_must_use() -> Result<(), ()> {
 +    ///     unimplemented!();
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub DOUBLE_MUST_USE,
 +    style,
 +    "`#[must_use]` attribute on a `#[must_use]`-returning function / method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that have no
 +    /// `#[must_use]` attribute, but return something not already marked
 +    /// must-use, have no mutable arg and mutate no statics.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not bad at all, this lint just shows places where
 +    /// you could add the attribute.
 +    ///
 +    /// ### Known problems
 +    /// The lint only checks the arguments for mutable
 +    /// types without looking if they are actually changed. On the other hand,
 +    /// it also ignores a broad range of potentially interesting side effects,
 +    /// because we cannot decide whether the programmer intends the function to
 +    /// be called for the side effect or the result. Expect many false
 +    /// positives. At least we don't lint if the result type is unit or already
 +    /// `#[must_use]`.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// // this could be annotated with `#[must_use]`.
 +    /// fn id<T>(t: T) -> T { t }
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub MUST_USE_CANDIDATE,
 +    pedantic,
 +    "function or method that could take a `#[must_use]` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for public functions that return a `Result`
 +    /// with an `Err` type of `()`. It suggests using a custom type that
 +    /// implements `std::error::Error`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Unit does not implement `Error` and carries no
 +    /// further information about what went wrong.
 +    ///
 +    /// ### Known problems
 +    /// Of course, this lint assumes that `Result` is used
 +    /// for a fallible operation (which is after all the intended use). However
 +    /// code may opt to (mis)use it as a basic two-variant-enum. In that case,
 +    /// the suggestion is misguided, and the code should use a custom enum
 +    /// instead.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// pub fn read_u8() -> Result<u8, ()> { Err(()) }
 +    /// ```
 +    /// should become
 +    /// ```rust,should_panic
 +    /// use std::fmt;
 +    ///
 +    /// #[derive(Debug)]
 +    /// pub struct EndOfStream;
 +    ///
 +    /// impl fmt::Display for EndOfStream {
 +    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +    ///         write!(f, "End of Stream")
 +    ///     }
 +    /// }
 +    ///
 +    /// impl std::error::Error for EndOfStream { }
 +    ///
 +    /// pub fn read_u8() -> Result<u8, EndOfStream> { Err(EndOfStream) }
 +    ///# fn main() {
 +    ///#     read_u8().unwrap();
 +    ///# }
 +    /// ```
 +    ///
 +    /// Note that there are crates that simplify creating the error type, e.g.
 +    /// [`thiserror`](https://docs.rs/thiserror).
 +    #[clippy::version = "1.49.0"]
 +    pub RESULT_UNIT_ERR,
 +    style,
 +    "public function returning `Result` with an `Err` type of `()`"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for functions that return `Result` with an unusually large
++    /// `Err`-variant.
++    ///
++    /// ### Why is this bad?
++    /// A `Result` is at least as large as the `Err`-variant. While we
++    /// expect that variant to be seldomly used, the compiler needs to reserve
++    /// and move that much memory every single time.
++    ///
++    /// ### Known problems
++    /// The size determined by Clippy is platform-dependent.
++    ///
++    /// ### Examples
++    /// ```rust
++    /// pub enum ParseError {
++    ///     UnparsedBytes([u8; 512]),
++    ///     UnexpectedEof,
++    /// }
++    ///
++    /// // The `Result` has at least 512 bytes, even in the `Ok`-case
++    /// pub fn parse() -> Result<(), ParseError> {
++    ///     Ok(())
++    /// }
++    /// ```
++    /// should be
++    /// ```
++    /// pub enum ParseError {
++    ///     UnparsedBytes(Box<[u8; 512]>),
++    ///     UnexpectedEof,
++    /// }
++    ///
++    /// // The `Result` is slightly larger than a pointer
++    /// pub fn parse() -> Result<(), ParseError> {
++    ///     Ok(())
++    /// }
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub RESULT_LARGE_ERR,
++    perf,
++    "function returning `Result` with large `Err` type"
++}
++
 +#[derive(Copy, Clone)]
 +pub struct Functions {
 +    too_many_arguments_threshold: u64,
 +    too_many_lines_threshold: u64,
++    large_error_threshold: u64,
 +}
 +
 +impl Functions {
-         result_unit_err::check_item(cx, item);
++    pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
 +        Self {
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
++            large_error_threshold,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Functions => [
 +    TOO_MANY_ARGUMENTS,
 +    TOO_MANY_LINES,
 +    NOT_UNSAFE_PTR_ARG_DEREF,
 +    MUST_USE_UNIT,
 +    DOUBLE_MUST_USE,
 +    MUST_USE_CANDIDATE,
 +    RESULT_UNIT_ERR,
++    RESULT_LARGE_ERR,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Functions {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: intravisit::FnKind<'tcx>,
 +        decl: &'tcx hir::FnDecl<'_>,
 +        body: &'tcx hir::Body<'_>,
 +        span: Span,
 +        hir_id: hir::HirId,
 +    ) {
 +        too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
 +        too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
 +        not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id);
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
 +        must_use::check_item(cx, item);
-         result_unit_err::check_impl_item(cx, item);
++        result::check_item(cx, item, self.large_error_threshold);
 +    }
 +
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
 +        must_use::check_impl_item(cx, item);
-         result_unit_err::check_trait_item(cx, item);
++        result::check_impl_item(cx, item, self.large_error_threshold);
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
 +        too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
 +        not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
 +        must_use::check_trait_item(cx, item);
++        result::check_trait_item(cx, item, self.large_error_threshold);
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..af520a493eda168b649d8bc9de9ce742de1b9169
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,100 @@@
++use rustc_errors::Diagnostic;
++use rustc_hir as hir;
++use rustc_lint::{LateContext, LintContext};
++use rustc_middle::lint::in_external_macro;
++use rustc_middle::ty::{self, Ty};
++use rustc_span::{sym, Span};
++use rustc_typeck::hir_ty_to_ty;
++
++use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
++use clippy_utils::trait_ref_of_method;
++use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
++
++use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
++
++/// The type of the `Err`-variant in a `std::result::Result` returned by the
++/// given `FnDecl`
++fn result_err_ty<'tcx>(
++    cx: &LateContext<'tcx>,
++    decl: &hir::FnDecl<'tcx>,
++    item_span: Span,
++) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
++    if !in_external_macro(cx.sess(), item_span)
++        && let hir::FnRetTy::Return(hir_ty) = decl.output
++        && let ty = hir_ty_to_ty(cx.tcx, hir_ty)
++        && is_type_diagnostic_item(cx, ty, sym::Result)
++        && let ty::Adt(_, substs) = ty.kind()
++    {
++        let err_ty = substs.type_at(1);
++        Some((hir_ty, err_ty))
++    } else {
++        None
++    }
++}
++
++pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
++    if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
++        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
++    {
++        if cx.access_levels.is_exported(item.def_id) {
++            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++            check_result_unit_err(cx, err_ty, fn_header_span);
++        }
++        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
++    }
++}
++
++pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
++    // Don't lint if method is a trait's implementation, we can't do anything about those
++    if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
++        && let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
++        && trait_ref_of_method(cx, item.def_id).is_none()
++    {
++        if cx.access_levels.is_exported(item.def_id) {
++            let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++            check_result_unit_err(cx, err_ty, fn_header_span);
++        }
++        check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
++    }
++}
++
++pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
++    if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
++        let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
++        if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
++            if cx.access_levels.is_exported(item.def_id) {
++                check_result_unit_err(cx, err_ty, fn_header_span);
++            }
++            check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
++        }
++    }
++}
++
++fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
++    if err_ty.is_unit() {
++        span_lint_and_help(
++            cx,
++            RESULT_UNIT_ERR,
++            fn_header_span,
++            "this returns a `Result<_, ()>`",
++            None,
++            "use a custom `Error` type instead",
++        );
++    }
++}
++
++fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
++    let ty_size = approx_ty_size(cx, err_ty);
++    if ty_size >= large_err_threshold {
++        span_lint_and_then(
++            cx,
++            RESULT_LARGE_ERR,
++            hir_ty_span,
++            "the `Err`-variant returned from this function is very large",
++            |diag: &mut Diagnostic| {
++                diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
++                diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
++            },
++        );
++    }
++}
index e9501700784931c25ac97277fafa0b825ceac596,0000000000000000000000000000000000000000..4d703d691acc2f81e57296256027072221c27214
mode 100644,000000..100644
--- /dev/null
@@@ -1,140 -1,0 +1,142 @@@
- use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::SpanlessEq;
 +use if_chain::if_chain;
++use rustc_errors::Diagnostic;
 +use rustc_hir::intravisit::{self as visit, Visitor};
 +use rustc_hir::{Expr, ExprKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `Mutex::lock` calls in `if let` expression
 +    /// with lock calls in any of the else blocks.
 +    ///
 +    /// ### Why is this bad?
 +    /// The Mutex lock remains held for the whole
 +    /// `if let ... else` block and deadlocks.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// if let Ok(thing) = mutex.lock() {
 +    ///     do_thing();
 +    /// } else {
 +    ///     mutex.lock();
 +    /// }
 +    /// ```
 +    /// Should be written
 +    /// ```rust,ignore
 +    /// let locked = mutex.lock();
 +    /// if let Ok(thing) = locked {
 +    ///     do_thing(thing);
 +    /// } else {
 +    ///     use_locked(locked);
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub IF_LET_MUTEX,
 +    correctness,
 +    "locking a `Mutex` in an `if let` block can cause deadlocks"
 +}
 +
 +declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-         let mut arm_visit = ArmVisitor {
-             mutex_lock_called: false,
-             found_mutex: None,
-             cx,
-         };
-         let mut op_visit = OppVisitor {
-             mutex_lock_called: false,
-             found_mutex: None,
-             cx,
-         };
++        let mut arm_visit = ArmVisitor { found_mutex: None, cx };
++        let mut op_visit = OppVisitor { found_mutex: None, cx };
 +        if let Some(higher::IfLet {
 +            let_expr,
 +            if_then,
 +            if_else: Some(if_else),
 +            ..
 +        }) = higher::IfLet::hir(cx, expr)
 +        {
 +            op_visit.visit_expr(let_expr);
-             if op_visit.mutex_lock_called {
++            if let Some(op_mutex) = op_visit.found_mutex {
 +                arm_visit.visit_expr(if_then);
 +                arm_visit.visit_expr(if_else);
 +
-                 if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
-                     span_lint_and_help(
++                if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
++                    let diag = |diag: &mut Diagnostic| {
++                        diag.span_label(
++                            op_mutex.span,
++                            "this Mutex will remain locked for the entire `if let`-block...",
++                        );
++                        diag.span_label(
++                            arm_mutex.span,
++                            "... and is tried to lock again here, which will always deadlock.",
++                        );
++                        diag.help("move the lock call outside of the `if let ...` expression");
++                    };
++                    span_lint_and_then(
 +                        cx,
 +                        IF_LET_MUTEX,
 +                        expr.span,
 +                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
-                         None,
-                         "move the lock call outside of the `if let ...` expression",
++                        diag,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if `Mutex::lock` is called in the `if let` expr.
 +pub struct OppVisitor<'a, 'tcx> {
-     mutex_lock_called: bool,
 +    found_mutex: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
 +            self.found_mutex = Some(mutex);
-             self.mutex_lock_called = true;
 +            return;
 +        }
 +        visit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// Checks if `Mutex::lock` is called in any of the branches.
 +pub struct ArmVisitor<'a, 'tcx> {
-     mutex_lock_called: bool,
 +    found_mutex: Option<&'tcx Expr<'tcx>>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
 +            self.found_mutex = Some(mutex);
-             self.mutex_lock_called = true;
 +            return;
 +        }
 +        visit::walk_expr(self, expr);
 +    }
 +}
 +
 +impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
-     fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
-         self.found_mutex
-             .map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
++    fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
++        self.found_mutex.and_then(|arm_mutex| {
++            SpanlessEq::new(self.cx)
++                .eq_expr(op_mutex, arm_mutex)
++                .then_some(arm_mutex)
++        })
 +    }
 +}
 +
 +fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if_chain! {
 +        if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +        if path.ident.as_str() == "lock";
-         let ty = cx.typeck_results().expr_ty(self_arg);
++        let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +        if is_type_diagnostic_item(cx, ty, sym::Mutex);
 +        then {
 +            Some(self_arg)
 +        } else {
 +            None
 +        }
 +    }
 +}
index b8d227855d97616c1f4171dcd4e0b621068e328f,0000000000000000000000000000000000000000..11c43247868ca46b817e01f60b13efe562a531b3
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,128 @@@
- use if_chain::if_chain;
 +use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 +use clippy_utils::source::snippet_with_macro_callsite;
 +use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
-     /// Checks for if-else that could be written to `bool::then`.
 +use rustc_hir::LangItem::{OptionNone, OptionSome};
 +use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// Looks a little redundant. Using `bool::then` helps it have less lines of code.
++    /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
 +    ///
 +    /// ### Why is this bad?
-     "Finds if-else that could be written using `bool::then`"
++    /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
++    /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
++    /// in comparison to `bool::then`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let v = vec![0];
 +    /// let a = if v.is_empty() {
 +    ///     println!("true!");
 +    ///     Some(42)
 +    /// } else {
 +    ///     None
 +    /// };
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```rust
 +    /// # let v = vec![0];
 +    /// let a = v.is_empty().then(|| {
 +    ///     println!("true!");
 +    ///     42
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub IF_THEN_SOME_ELSE_NONE,
 +    restriction,
-     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
++    "Finds if-else that could be written using either `bool::then` or `bool::then_some`"
 +}
 +
 +pub struct IfThenSomeElseNone {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl IfThenSomeElseNone {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
-         if_chain! {
-             if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
-             if let ExprKind::Block(then_block, _) = then.kind;
-             if let Some(then_expr) = then_block.expr;
-             if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
-             if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
-             if is_lang_ctor(cx, then_call_qpath, OptionSome);
-             if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
-             if is_lang_ctor(cx, qpath, OptionNone);
-             if !stmts_contains_early_return(then_block.stmts);
-             then {
-                 let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
-                 let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
-                     format!("({})", cond_snip)
-                 } else {
-                     cond_snip.into_owned()
-                 };
-                 let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
-                 let closure_body = if then_block.stmts.is_empty() {
-                     arg_snip.into_owned()
-                 } else {
-                     format!("{{ /* snippet */ {} }}", arg_snip)
-                 };
-                 let help = format!(
-                     "consider using `bool::then` like: `{}.then(|| {})`",
-                     cond_snip,
-                     closure_body,
-                 );
-                 span_lint_and_help(
-                     cx,
-                     IF_THEN_SOME_ELSE_NONE,
-                     expr.span,
-                     "this could be simplified with `bool::then`",
-                     None,
-                     &help,
-                 );
-             }
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
 +        if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
 +            return;
 +        }
 +
 +        if in_external_macro(cx.sess(), expr.span) {
 +            return;
 +        }
 +
 +        // We only care about the top-most `if` in the chain
 +        if is_else_clause(cx.tcx, expr) {
 +            return;
 +        }
 +
++        if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
++            && let ExprKind::Block(then_block, _) = then.kind
++            && let Some(then_expr) = then_block.expr
++            && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
++            && let ExprKind::Path(ref then_call_qpath) = then_call.kind
++            && is_lang_ctor(cx, then_call_qpath, OptionSome)
++            && let ExprKind::Path(ref qpath) = peel_blocks(els).kind
++            && is_lang_ctor(cx, qpath, OptionNone)
++            && !stmts_contains_early_return(then_block.stmts)
++        {
++            let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
++            let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
++                format!("({})", cond_snip)
++            } else {
++                cond_snip.into_owned()
++            };
++            let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
++            let mut method_body = if then_block.stmts.is_empty() {
++                arg_snip.into_owned()
++            } else {
++                format!("{{ /* snippet */ {} }}", arg_snip)
++            };
++            let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
++                "then_some"
++            } else {
++                method_body.insert_str(0, "|| ");
++                "then"
++            };
++
++            let help = format!(
++                "consider using `bool::{}` like: `{}.{}({})`",
++                method_name, cond_snip, method_name, method_body,
++            );
++            span_lint_and_help(
++                cx,
++                IF_THEN_SOME_ELSE_NONE,
++                expr.span,
++                &format!("this could be simplified with `bool::{}`", method_name),
++                None,
++                &help,
++            );
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn stmts_contains_early_return(stmts: &[Stmt<'_>]) -> bool {
 +    stmts.iter().any(|stmt| {
 +        let Stmt { kind: StmtKind::Semi(e), .. } = stmt else { return false };
 +
 +        contains_return(e)
 +    })
 +}
index 01082cc8eeb64933304d54f3c2616d81a723f247,0000000000000000000000000000000000000000..134cbbf7b5c66ad5f218173022533dc55b19c77b
mode 100644,000000..100644
--- /dev/null
@@@ -1,354 -1,0 +1,362 @@@
-     LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::all", Some("clippy_all"), vec![
 +    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(approx_const::APPROX_CONSTANT),
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(attrs::DEPRECATED_SEMVER),
 +    LintId::of(attrs::MISMATCHED_TARGET_OS),
 +    LintId::of(attrs::USELESS_ATTRIBUTE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
 +    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
-     LintId::of(get_first::GET_FIRST),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
++    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
 +    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(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_names::DISALLOWED_NAMES),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(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(escape::BOXED_LOCAL),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
 +    LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
++    LintId::of(functions::RESULT_LARGE_ERR),
 +    LintId::of(functions::RESULT_UNIT_ERR),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
-     LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(int_plus_one::INT_PLUS_ONE),
 +    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
 +    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_FIND),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
 +    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
 +    LintId::of(manual_retain::MANUAL_RETAIN),
 +    LintId::of(manual_strip::MANUAL_STRIP),
-     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    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(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::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_COUNT_TO_LEN),
 +    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::COLLAPSIBLE_STR_REPLACE),
 +    LintId::of(methods::ERR_EXPECT),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
++    LintId::of(methods::GET_FIRST),
 +    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
++    LintId::of(methods::MAP_CLONE),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
++    LintId::of(methods::MUT_MUTEX_LOCK),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::NEW_RET_NO_SELF),
++    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::OBFUSCATED_IF_ELSE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::OR_THEN_UNWRAP),
++    LintId::of(methods::RANGE_ZIP_WITH_LEN),
++    LintId::of(methods::REPEAT_ONCE),
 +    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::SUSPICIOUS_TO_OWNED),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
++    LintId::of(methods::UNIT_HASH),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
++    LintId::of(methods::UNNECESSARY_SORT_BY),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::USELESS_ASREF),
++    LintId::of(methods::VEC_RESIZE_TO_ZERO),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
++    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
-     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
-     LintId::of(ranges::RANGE_ZIP_WITH_LEN),
++    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(operators::ASSIGN_OP_PATTERN),
 +    LintId::of(operators::BAD_BIT_MASK),
 +    LintId::of(operators::CMP_NAN),
 +    LintId::of(operators::CMP_OWNED),
 +    LintId::of(operators::DOUBLE_COMPARISONS),
 +    LintId::of(operators::DURATION_SUBSEC),
 +    LintId::of(operators::EQ_OP),
 +    LintId::of(operators::ERASING_OP),
 +    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(operators::IDENTITY_OP),
 +    LintId::of(operators::INEFFECTIVE_BIT_MASK),
 +    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(operators::MODULO_ONE),
 +    LintId::of(operators::OP_REF),
 +    LintId::of(operators::PTR_EQ),
 +    LintId::of(operators::SELF_ASSIGNMENT),
 +    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(partialeq_to_none::PARTIALEQ_TO_NONE),
 +    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_offset_with_cast::PTR_OFFSET_WITH_CAST),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
-     LintId::of(repeat_once::REPEAT_ONCE),
 +    LintId::of(ranges::REVERSED_EMPTY_RANGES),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(reference::DEREF_ADDROF),
 +    LintId::of(regex::INVALID_REGEX),
-     LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    LintId::of(swap::ALMOST_SWAPPED),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
++    LintId::of(transmute::TRANSMUTING_NULL),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
-     LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::BOX_COLLECTION),
 +    LintId::of(types::REDUNDANT_ALLOCATION),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
-     LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unit_types::UNIT_CMP),
 +    LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
 +    LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
-     LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
++    LintId::of(unused_peekable::UNUSED_PEEKABLE),
 +    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(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
 +    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 3784d3c68dceef2046f01b4f2a0393004867e35a,0000000000000000000000000000000000000000..aa247352f88fb653ccbc6572c45bb74caedc63c7
mode 100644,000000..100644
--- /dev/null
@@@ -1,106 -1,0 +1,107 @@@
-     LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec![
 +    LintId::of(attrs::DEPRECATED_CFG_ATTR),
 +    LintId::of(booleans::NONMINIMAL_BOOL),
 +    LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
-     LintId::of(ranges::RANGE_ZIP_WITH_LEN),
 +    LintId::of(casts::CHAR_LIT_AS_U8),
 +    LintId::of(casts::UNNECESSARY_CAST),
 +    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
 +    LintId::of(derivable_impls::DERIVABLE_IMPLS),
 +    LintId::of(double_parens::DOUBLE_PARENS),
 +    LintId::of(explicit_write::EXPLICIT_WRITE),
 +    LintId::of(format::USELESS_FORMAT),
 +    LintId::of(functions::TOO_MANY_ARGUMENTS),
 +    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_FIND),
 +    LintId::of(loops::MANUAL_FLATTEN),
 +    LintId::of(loops::SINGLE_ELEMENT_LOOP),
 +    LintId::of(loops::WHILE_LET_LOOP),
 +    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
 +    LintId::of(manual_strip::MANUAL_STRIP),
 +    LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
 +    LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
 +    LintId::of(matches::MANUAL_UNWRAP_OR),
 +    LintId::of(matches::MATCH_AS_REF),
 +    LintId::of(matches::MATCH_SINGLE_BINDING),
 +    LintId::of(matches::NEEDLESS_MATCH),
 +    LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
 +    LintId::of(methods::BIND_INSTEAD_OF_MAP),
++    LintId::of(methods::BYTES_COUNT_TO_LEN),
 +    LintId::of(methods::CLONE_ON_COPY),
 +    LintId::of(methods::FILTER_MAP_IDENTITY),
 +    LintId::of(methods::FILTER_NEXT),
 +    LintId::of(methods::FLAT_MAP_IDENTITY),
 +    LintId::of(methods::GET_LAST_WITH_LEN),
 +    LintId::of(methods::INSPECT_FOR_EACH),
 +    LintId::of(methods::ITER_COUNT),
 +    LintId::of(methods::MANUAL_FILTER_MAP),
 +    LintId::of(methods::MANUAL_FIND_MAP),
 +    LintId::of(methods::MANUAL_SPLIT_ONCE),
 +    LintId::of(methods::MAP_FLATTEN),
 +    LintId::of(methods::MAP_IDENTITY),
 +    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
 +    LintId::of(methods::NEEDLESS_OPTION_TAKE),
 +    LintId::of(methods::NEEDLESS_SPLITN),
 +    LintId::of(methods::OPTION_AS_REF_DEREF),
 +    LintId::of(methods::OPTION_FILTER_MAP),
 +    LintId::of(methods::OR_THEN_UNWRAP),
++    LintId::of(methods::RANGE_ZIP_WITH_LEN),
++    LintId::of(methods::REPEAT_ONCE),
 +    LintId::of(methods::SEARCH_IS_SOME),
 +    LintId::of(methods::SKIP_WHILE_NEXT),
 +    LintId::of(methods::UNNECESSARY_FILTER_MAP),
 +    LintId::of(methods::UNNECESSARY_FIND_MAP),
++    LintId::of(methods::UNNECESSARY_SORT_BY),
 +    LintId::of(methods::USELESS_ASREF),
 +    LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
 +    LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
 +    LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
 +    LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
 +    LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
 +    LintId::of(needless_bool::BOOL_COMPARISON),
 +    LintId::of(needless_bool::NEEDLESS_BOOL),
 +    LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
 +    LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
 +    LintId::of(needless_update::NEEDLESS_UPDATE),
 +    LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
 +    LintId::of(no_effect::NO_EFFECT),
 +    LintId::of(no_effect::UNNECESSARY_OPERATION),
++    LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +    LintId::of(operators::DOUBLE_COMPARISONS),
 +    LintId::of(operators::DURATION_SUBSEC),
 +    LintId::of(operators::IDENTITY_OP),
 +    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(repeat_once::REPEAT_ONCE),
 +    LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
 +    LintId::of(redundant_slicing::REDUNDANT_SLICING),
 +    LintId::of(reference::DEREF_ADDROF),
-     LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
 +    LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
 +    LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
 +    LintId::of(swap::MANUAL_SWAP),
 +    LintId::of(temporary_assignment::TEMPORARY_ASSIGNMENT),
 +    LintId::of(transmute::CROSSPOINTER_TRANSMUTE),
 +    LintId::of(transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS),
 +    LintId::of(transmute::TRANSMUTE_BYTES_TO_STR),
 +    LintId::of(transmute::TRANSMUTE_FLOAT_TO_INT),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_BOOL),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_CHAR),
 +    LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
 +    LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
 +    LintId::of(transmute::USELESS_TRANSMUTE),
 +    LintId::of(types::BORROWED_BOX),
 +    LintId::of(types::TYPE_COMPLEXITY),
 +    LintId::of(types::VEC_BOX),
 +    LintId::of(unit_types::UNIT_ARG),
 +    LintId::of(unwrap::UNNECESSARY_UNWRAP),
 +    LintId::of(useless_conversion::USELESS_CONVERSION),
 +    LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
 +])
index 006275d1383ff0d313784fb9a5a486d88068bd3e,0000000000000000000000000000000000000000..bb94037ec2e7949ba52a2b25ba0bd89ae6794e89
mode 100644,000000..100644
--- /dev/null
@@@ -1,78 -1,0 +1,78 @@@
-     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
 +// 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(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(booleans::OVERLY_COMPLEX_BOOL_EXPR),
 +    LintId::of(casts::CAST_REF_TO_MUT),
 +    LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
 +    LintId::of(copies::IFS_SAME_COND),
 +    LintId::of(copies::IF_SAME_THEN_ELSE),
 +    LintId::of(derive::DERIVE_HASH_XOR_EQ),
 +    LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD),
 +    LintId::of(drop_forget_ref::DROP_COPY),
 +    LintId::of(drop_forget_ref::DROP_REF),
 +    LintId::of(drop_forget_ref::FORGET_COPY),
 +    LintId::of(drop_forget_ref::FORGET_REF),
 +    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
 +    LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
 +    LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
 +    LintId::of(formatting::POSSIBLE_MISSING_COMMA),
 +    LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
 +    LintId::of(if_let_mutex::IF_LET_MUTEX),
 +    LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
 +    LintId::of(infinite_iter::INFINITE_ITER),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
 +    LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
 +    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
 +    LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
 +    LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
 +    LintId::of(loops::ITER_NEXT_LOOP),
 +    LintId::of(loops::NEVER_LOOP),
 +    LintId::of(loops::WHILE_IMMUTABLE_CONDITION),
 +    LintId::of(matches::MATCH_STR_CASE_MISMATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
 +    LintId::of(methods::CLONE_DOUBLE_REF),
 +    LintId::of(methods::ITERATOR_STEP_BY_ZERO),
++    LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
 +    LintId::of(methods::SUSPICIOUS_SPLITN),
 +    LintId::of(methods::UNINIT_ASSUMED_INIT),
++    LintId::of(methods::UNIT_HASH),
++    LintId::of(methods::VEC_RESIZE_TO_ZERO),
 +    LintId::of(methods::ZST_OFFSET),
 +    LintId::of(minmax::MIN_MAX),
 +    LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
-     LintId::of(transmuting_null::TRANSMUTING_NULL),
 +    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
 +    LintId::of(operators::BAD_BIT_MASK),
 +    LintId::of(operators::CMP_NAN),
 +    LintId::of(operators::EQ_OP),
 +    LintId::of(operators::ERASING_OP),
 +    LintId::of(operators::INEFFECTIVE_BIT_MASK),
 +    LintId::of(operators::MODULO_ONE),
 +    LintId::of(operators::SELF_ASSIGNMENT),
 +    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(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
 +    LintId::of(regex::INVALID_REGEX),
 +    LintId::of(serde_api::SERDE_API_MISUSE),
 +    LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
 +    LintId::of(swap::ALMOST_SWAPPED),
++    LintId::of(transmute::TRANSMUTING_NULL),
 +    LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
 +    LintId::of(transmute::WRONG_TRANSMUTE),
-     LintId::of(unit_hash::UNIT_HASH),
 +    LintId::of(unicode::INVISIBLE_CHARACTERS),
 +    LintId::of(uninit_vec::UNINIT_VEC),
-     LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
 +    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),
 +])
index c540573b80228e8f3786a4117ab3dbda503266f7,0000000000000000000000000000000000000000..fd20e016578a1bad268088cfe5dac73f0182b881
mode 100644,000000..100644
--- /dev/null
@@@ -1,599 -1,0 +1,609 @@@
-     as_underscore::AS_UNDERSCORE,
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_lints(&[
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::CLIPPY_LINTS_INTERNAL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::COMPILER_LINT_FUNCTIONS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_DEPRECATION_REASON,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::DEFAULT_LINT,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::IF_CHAIN_STYLE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INTERNING_DEFINED_SYMBOL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::INVALID_PATHS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::LINT_WITHOUT_LINT_PASS,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::MISSING_MSRV_ATTR_IMPL,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::OUTER_EXPN_EXPN_DATA,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::PRODUCE_ICE,
 +    #[cfg(feature = "internal")]
 +    utils::internal_lints::UNNECESSARY_SYMBOL_STR,
 +    almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
 +    approx_const::APPROX_CONSTANT,
 +    as_conversions::AS_CONVERSIONS,
-     borrow_as_ptr::BORROW_AS_PTR,
 +    asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
 +    asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
 +    assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
 +    assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES,
 +    async_yields_async::ASYNC_YIELDS_ASYNC,
 +    attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
 +    attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
 +    attrs::DEPRECATED_CFG_ATTR,
 +    attrs::DEPRECATED_SEMVER,
 +    attrs::EMPTY_LINE_AFTER_OUTER_ATTR,
 +    attrs::INLINE_ALWAYS,
 +    attrs::MISMATCHED_TARGET_OS,
 +    attrs::USELESS_ATTRIBUTE,
 +    await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
 +    await_holding_invalid::AWAIT_HOLDING_LOCK,
 +    await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
 +    blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
 +    bool_assert_comparison::BOOL_ASSERT_COMPARISON,
 +    booleans::NONMINIMAL_BOOL,
 +    booleans::OVERLY_COMPLEX_BOOL_EXPR,
-     bytecount::NAIVE_BYTECOUNT,
-     bytes_count_to_len::BYTES_COUNT_TO_LEN,
 +    borrow_deref_ref::BORROW_DEREF_REF,
-     case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    cargo::CARGO_COMMON_METADATA,
 +    cargo::MULTIPLE_CRATE_VERSIONS,
 +    cargo::NEGATIVE_FEATURE_NAMES,
 +    cargo::REDUNDANT_FEATURE_NAMES,
 +    cargo::WILDCARD_DEPENDENCIES,
-     get_first::GET_FIRST,
++    casts::AS_UNDERSCORE,
++    casts::BORROW_AS_PTR,
 +    casts::CAST_ABS_TO_UNSIGNED,
 +    casts::CAST_ENUM_CONSTRUCTOR,
 +    casts::CAST_ENUM_TRUNCATION,
 +    casts::CAST_LOSSLESS,
 +    casts::CAST_POSSIBLE_TRUNCATION,
 +    casts::CAST_POSSIBLE_WRAP,
 +    casts::CAST_PRECISION_LOSS,
 +    casts::CAST_PTR_ALIGNMENT,
 +    casts::CAST_REF_TO_MUT,
 +    casts::CAST_SIGN_LOSS,
 +    casts::CAST_SLICE_DIFFERENT_SIZES,
++    casts::CAST_SLICE_FROM_RAW_PARTS,
 +    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,
 +    comparison_chain::COMPARISON_CHAIN,
 +    copies::BRANCHES_SHARING_CODE,
 +    copies::IFS_SAME_COND,
 +    copies::IF_SAME_THEN_ELSE,
 +    copies::SAME_FUNCTIONS_IN_IF_CONDITION,
 +    copy_iterator::COPY_ITERATOR,
 +    crate_in_macro_def::CRATE_IN_MACRO_DEF,
 +    create_dir::CREATE_DIR,
 +    dbg_macro::DBG_MACRO,
 +    default::DEFAULT_TRAIT_ACCESS,
 +    default::FIELD_REASSIGN_WITH_DEFAULT,
 +    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
 +    default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
 +    default_union_representation::DEFAULT_UNION_REPRESENTATION,
 +    dereference::EXPLICIT_AUTO_DEREF,
 +    dereference::EXPLICIT_DEREF_METHODS,
 +    dereference::NEEDLESS_BORROW,
 +    dereference::REF_BINDING_TO_REFERENCE,
 +    derivable_impls::DERIVABLE_IMPLS,
 +    derive::DERIVE_HASH_XOR_EQ,
 +    derive::DERIVE_ORD_XOR_PARTIAL_ORD,
 +    derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ,
 +    derive::EXPL_IMPL_CLONE_ON_COPY,
 +    derive::UNSAFE_DERIVE_DESERIALIZE,
 +    disallowed_methods::DISALLOWED_METHODS,
 +    disallowed_names::DISALLOWED_NAMES,
 +    disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS,
 +    disallowed_types::DISALLOWED_TYPES,
 +    doc::DOC_MARKDOWN,
 +    doc::MISSING_ERRORS_DOC,
 +    doc::MISSING_PANICS_DOC,
 +    doc::MISSING_SAFETY_DOC,
 +    doc::NEEDLESS_DOCTEST_MAIN,
 +    doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
 +    double_parens::DOUBLE_PARENS,
 +    drop_forget_ref::DROP_COPY,
 +    drop_forget_ref::DROP_NON_DROP,
 +    drop_forget_ref::DROP_REF,
 +    drop_forget_ref::FORGET_COPY,
 +    drop_forget_ref::FORGET_NON_DROP,
 +    drop_forget_ref::FORGET_REF,
 +    drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
 +    duplicate_mod::DUPLICATE_MOD,
 +    else_if_without_else::ELSE_IF_WITHOUT_ELSE,
 +    empty_drop::EMPTY_DROP,
 +    empty_enum::EMPTY_ENUM,
 +    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
 +    entry::MAP_ENTRY,
 +    enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
 +    enum_variants::ENUM_VARIANT_NAMES,
 +    enum_variants::MODULE_INCEPTION,
 +    enum_variants::MODULE_NAME_REPETITIONS,
 +    equatable_if_let::EQUATABLE_IF_LET,
 +    escape::BOXED_LOCAL,
 +    eta_reduction::REDUNDANT_CLOSURE,
 +    eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
 +    excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS,
 +    excessive_bools::STRUCT_EXCESSIVE_BOOLS,
 +    exhaustive_items::EXHAUSTIVE_ENUMS,
 +    exhaustive_items::EXHAUSTIVE_STRUCTS,
 +    exit::EXIT,
 +    explicit_write::EXPLICIT_WRITE,
 +    fallible_impl_from::FALLIBLE_IMPL_FROM,
 +    float_literal::EXCESSIVE_PRECISION,
 +    float_literal::LOSSY_FLOAT_LITERAL,
 +    floating_point_arithmetic::IMPRECISE_FLOPS,
 +    floating_point_arithmetic::SUBOPTIMAL_FLOPS,
 +    format::USELESS_FORMAT,
 +    format_args::FORMAT_IN_FORMAT_ARGS,
 +    format_args::TO_STRING_IN_FORMAT_ARGS,
 +    format_impl::PRINT_IN_FORMAT_IMPL,
 +    format_impl::RECURSIVE_FORMAT_IMPL,
 +    format_push_string::FORMAT_PUSH_STRING,
 +    formatting::POSSIBLE_MISSING_COMMA,
 +    formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING,
 +    formatting::SUSPICIOUS_ELSE_FORMATTING,
 +    formatting::SUSPICIOUS_UNARY_OP_FORMATTING,
 +    from_over_into::FROM_OVER_INTO,
 +    from_str_radix_10::FROM_STR_RADIX_10,
 +    functions::DOUBLE_MUST_USE,
 +    functions::MUST_USE_CANDIDATE,
 +    functions::MUST_USE_UNIT,
 +    functions::NOT_UNSAFE_PTR_ARG_DEREF,
++    functions::RESULT_LARGE_ERR,
 +    functions::RESULT_UNIT_ERR,
 +    functions::TOO_MANY_ARGUMENTS,
 +    functions::TOO_MANY_LINES,
 +    future_not_send::FUTURE_NOT_SEND,
-     manual_ok_or::MANUAL_OK_OR,
 +    if_let_mutex::IF_LET_MUTEX,
 +    if_not_else::IF_NOT_ELSE,
 +    if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
 +    implicit_hasher::IMPLICIT_HASHER,
 +    implicit_return::IMPLICIT_RETURN,
 +    implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
 +    inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
 +    index_refutable_slice::INDEX_REFUTABLE_SLICE,
 +    indexing_slicing::INDEXING_SLICING,
 +    indexing_slicing::OUT_OF_BOUNDS_INDEXING,
 +    infinite_iter::INFINITE_ITER,
 +    infinite_iter::MAYBE_INFINITE_ITER,
 +    inherent_impl::MULTIPLE_INHERENT_IMPL,
 +    inherent_to_string::INHERENT_TO_STRING,
 +    inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY,
 +    init_numbered_fields::INIT_NUMBERED_FIELDS,
 +    inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
 +    int_plus_one::INT_PLUS_ONE,
 +    invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
 +    invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
 +    items_after_statements::ITEMS_AFTER_STATEMENTS,
 +    iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
 +    large_const_arrays::LARGE_CONST_ARRAYS,
 +    large_enum_variant::LARGE_ENUM_VARIANT,
 +    large_include_file::LARGE_INCLUDE_FILE,
 +    large_stack_arrays::LARGE_STACK_ARRAYS,
 +    len_zero::COMPARISON_TO_EMPTY,
 +    len_zero::LEN_WITHOUT_IS_EMPTY,
 +    len_zero::LEN_ZERO,
 +    let_if_seq::USELESS_LET_IF_SEQ,
 +    let_underscore::LET_UNDERSCORE_DROP,
 +    let_underscore::LET_UNDERSCORE_LOCK,
 +    let_underscore::LET_UNDERSCORE_MUST_USE,
 +    lifetimes::EXTRA_UNUSED_LIFETIMES,
 +    lifetimes::NEEDLESS_LIFETIMES,
 +    literal_representation::DECIMAL_LITERAL_REPRESENTATION,
 +    literal_representation::INCONSISTENT_DIGIT_GROUPING,
 +    literal_representation::LARGE_DIGIT_GROUPS,
 +    literal_representation::MISTYPED_LITERAL_SUFFIXES,
 +    literal_representation::UNREADABLE_LITERAL,
 +    literal_representation::UNUSUAL_BYTE_GROUPINGS,
 +    loops::EMPTY_LOOP,
 +    loops::EXPLICIT_COUNTER_LOOP,
 +    loops::EXPLICIT_INTO_ITER_LOOP,
 +    loops::EXPLICIT_ITER_LOOP,
 +    loops::FOR_KV_MAP,
 +    loops::FOR_LOOPS_OVER_FALLIBLES,
 +    loops::ITER_NEXT_LOOP,
 +    loops::MANUAL_FIND,
 +    loops::MANUAL_FLATTEN,
 +    loops::MANUAL_MEMCPY,
 +    loops::MISSING_SPIN_LOOP,
 +    loops::MUT_RANGE_BOUND,
 +    loops::NEEDLESS_COLLECT,
 +    loops::NEEDLESS_RANGE_LOOP,
 +    loops::NEVER_LOOP,
 +    loops::SAME_ITEM_PUSH,
 +    loops::SINGLE_ELEMENT_LOOP,
 +    loops::WHILE_IMMUTABLE_CONDITION,
 +    loops::WHILE_LET_LOOP,
 +    loops::WHILE_LET_ON_ITERATOR,
 +    macro_use::MACRO_USE_IMPORTS,
 +    main_recursion::MAIN_RECURSION,
 +    manual_assert::MANUAL_ASSERT,
 +    manual_async_fn::MANUAL_ASYNC_FN,
 +    manual_bits::MANUAL_BITS,
 +    manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
 +    manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
-     map_clone::MAP_CLONE,
-     map_err_ignore::MAP_ERR_IGNORE,
 +    manual_rem_euclid::MANUAL_REM_EUCLID,
 +    manual_retain::MANUAL_RETAIN,
++    manual_string_new::MANUAL_STRING_NEW,
 +    manual_strip::MANUAL_STRIP,
-     mut_mutex_lock::MUT_MUTEX_LOCK,
 +    map_unit_fn::OPTION_MAP_UNIT_FN,
 +    map_unit_fn::RESULT_MAP_UNIT_FN,
 +    match_result_ok::MATCH_RESULT_OK,
 +    matches::COLLAPSIBLE_MATCH,
 +    matches::INFALLIBLE_DESTRUCTURING_MATCH,
 +    matches::MANUAL_MAP,
 +    matches::MANUAL_UNWRAP_OR,
 +    matches::MATCH_AS_REF,
 +    matches::MATCH_BOOL,
 +    matches::MATCH_LIKE_MATCHES_MACRO,
 +    matches::MATCH_ON_VEC_ITEMS,
 +    matches::MATCH_OVERLAPPING_ARM,
 +    matches::MATCH_REF_PATS,
 +    matches::MATCH_SAME_ARMS,
 +    matches::MATCH_SINGLE_BINDING,
 +    matches::MATCH_STR_CASE_MISMATCH,
 +    matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
 +    matches::MATCH_WILD_ERR_ARM,
 +    matches::NEEDLESS_MATCH,
 +    matches::REDUNDANT_PATTERN_MATCHING,
 +    matches::REST_PAT_IN_FULLY_BOUND_STRUCTS,
 +    matches::SIGNIFICANT_DROP_IN_SCRUTINEE,
 +    matches::SINGLE_MATCH,
 +    matches::SINGLE_MATCH_ELSE,
 +    matches::TRY_ERR,
 +    matches::WILDCARD_ENUM_MATCH_ARM,
 +    matches::WILDCARD_IN_OR_PATTERNS,
 +    mem_forget::MEM_FORGET,
 +    mem_replace::MEM_REPLACE_OPTION_WITH_NONE,
 +    mem_replace::MEM_REPLACE_WITH_DEFAULT,
 +    mem_replace::MEM_REPLACE_WITH_UNINIT,
 +    methods::BIND_INSTEAD_OF_MAP,
++    methods::BYTES_COUNT_TO_LEN,
 +    methods::BYTES_NTH,
++    methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
 +    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::COLLAPSIBLE_STR_REPLACE,
 +    methods::ERR_EXPECT,
 +    methods::EXPECT_FUN_CALL,
 +    methods::EXPECT_USED,
 +    methods::EXTEND_WITH_DRAIN,
 +    methods::FILETYPE_IS_FILE,
 +    methods::FILTER_MAP_IDENTITY,
 +    methods::FILTER_MAP_NEXT,
 +    methods::FILTER_NEXT,
 +    methods::FLAT_MAP_IDENTITY,
 +    methods::FLAT_MAP_OPTION,
 +    methods::FROM_ITER_INSTEAD_OF_COLLECT,
++    methods::GET_FIRST,
 +    methods::GET_LAST_WITH_LEN,
 +    methods::GET_UNWRAP,
 +    methods::IMPLICIT_CLONE,
 +    methods::INEFFICIENT_TO_STRING,
 +    methods::INSPECT_FOR_EACH,
 +    methods::INTO_ITER_ON_REF,
 +    methods::IS_DIGIT_ASCII_RADIX,
 +    methods::ITERATOR_STEP_BY_ZERO,
 +    methods::ITER_CLONED_COLLECT,
 +    methods::ITER_COUNT,
 +    methods::ITER_NEXT_SLICE,
 +    methods::ITER_NTH,
 +    methods::ITER_NTH_ZERO,
++    methods::ITER_ON_EMPTY_COLLECTIONS,
++    methods::ITER_ON_SINGLE_ITEMS,
 +    methods::ITER_OVEREAGER_CLONED,
 +    methods::ITER_SKIP_NEXT,
 +    methods::ITER_WITH_DRAIN,
 +    methods::MANUAL_FILTER_MAP,
 +    methods::MANUAL_FIND_MAP,
++    methods::MANUAL_OK_OR,
 +    methods::MANUAL_SATURATING_ARITHMETIC,
 +    methods::MANUAL_SPLIT_ONCE,
 +    methods::MANUAL_STR_REPEAT,
++    methods::MAP_CLONE,
 +    methods::MAP_COLLECT_RESULT_UNIT,
++    methods::MAP_ERR_IGNORE,
 +    methods::MAP_FLATTEN,
 +    methods::MAP_IDENTITY,
 +    methods::MAP_UNWRAP_OR,
++    methods::MUT_MUTEX_LOCK,
++    methods::NAIVE_BYTECOUNT,
 +    methods::NEEDLESS_OPTION_AS_DEREF,
 +    methods::NEEDLESS_OPTION_TAKE,
 +    methods::NEEDLESS_SPLITN,
 +    methods::NEW_RET_NO_SELF,
++    methods::NONSENSICAL_OPEN_OPTIONS,
 +    methods::NO_EFFECT_REPLACE,
 +    methods::OBFUSCATED_IF_ELSE,
 +    methods::OK_EXPECT,
 +    methods::OPTION_AS_REF_DEREF,
 +    methods::OPTION_FILTER_MAP,
 +    methods::OPTION_MAP_OR_NONE,
 +    methods::OR_FUN_CALL,
 +    methods::OR_THEN_UNWRAP,
++    methods::PATH_BUF_PUSH_OVERWRITE,
++    methods::RANGE_ZIP_WITH_LEN,
++    methods::REPEAT_ONCE,
 +    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::STABLE_SORT_PRIMITIVE,
 +    methods::STRING_EXTEND_CHARS,
 +    methods::SUSPICIOUS_MAP,
 +    methods::SUSPICIOUS_SPLITN,
++    methods::SUSPICIOUS_TO_OWNED,
 +    methods::UNINIT_ASSUMED_INIT,
++    methods::UNIT_HASH,
 +    methods::UNNECESSARY_FILTER_MAP,
 +    methods::UNNECESSARY_FIND_MAP,
 +    methods::UNNECESSARY_FOLD,
 +    methods::UNNECESSARY_JOIN,
 +    methods::UNNECESSARY_LAZY_EVALUATIONS,
++    methods::UNNECESSARY_SORT_BY,
 +    methods::UNNECESSARY_TO_OWNED,
 +    methods::UNWRAP_OR_ELSE_DEFAULT,
 +    methods::UNWRAP_USED,
 +    methods::USELESS_ASREF,
++    methods::VEC_RESIZE_TO_ZERO,
++    methods::VERBOSE_FILE_READS,
 +    methods::WRONG_SELF_CONVENTION,
 +    methods::ZST_OFFSET,
 +    minmax::MIN_MAX,
 +    misc::SHORT_CIRCUIT_STATEMENT,
 +    misc::TOPLEVEL_REF_ARG,
 +    misc::USED_UNDERSCORE_BINDING,
 +    misc::ZERO_PTR,
 +    misc_early::BUILTIN_TYPE_SHADOW,
 +    misc_early::DOUBLE_NEG,
 +    misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
 +    misc_early::MIXED_CASE_HEX_LITERALS,
 +    misc_early::REDUNDANT_PATTERN,
 +    misc_early::SEPARATED_LITERAL_SUFFIX,
 +    misc_early::UNNEEDED_FIELD_PATTERN,
 +    misc_early::UNNEEDED_WILDCARD_PATTERN,
 +    misc_early::UNSEPARATED_LITERAL_SUFFIX,
 +    misc_early::ZERO_PREFIXED_LITERAL,
 +    mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER,
 +    missing_const_for_fn::MISSING_CONST_FOR_FN,
 +    missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS,
 +    missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES,
 +    missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS,
 +    mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION,
 +    mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
 +    module_style::MOD_MODULE_FILES,
 +    module_style::SELF_NAMED_MODULE_FILES,
++    multi_assignments::MULTI_ASSIGNMENTS,
 +    mut_key::MUTABLE_KEY_TYPE,
 +    mut_mut::MUT_MUT,
-     open_options::NONSENSICAL_OPEN_OPTIONS,
 +    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_bool::BOOL_COMPARISON,
 +    needless_bool::NEEDLESS_BOOL,
 +    needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
 +    needless_continue::NEEDLESS_CONTINUE,
 +    needless_for_each::NEEDLESS_FOR_EACH,
 +    needless_late_init::NEEDLESS_LATE_INIT,
 +    needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS,
 +    needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
 +    needless_question_mark::NEEDLESS_QUESTION_MARK,
 +    needless_update::NEEDLESS_UPDATE,
 +    neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD,
 +    neg_multiply::NEG_MULTIPLY,
 +    new_without_default::NEW_WITHOUT_DEFAULT,
 +    no_effect::NO_EFFECT,
 +    no_effect::NO_EFFECT_UNDERSCORE_BINDING,
 +    no_effect::UNNECESSARY_OPERATION,
 +    non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
 +    non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
 +    non_expressive_names::JUST_UNDERSCORES_AND_DIGITS,
 +    non_expressive_names::MANY_SINGLE_CHAR_NAMES,
 +    non_expressive_names::SIMILAR_NAMES,
 +    non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
 +    non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
 +    nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
 +    octal_escapes::OCTAL_ESCAPES,
 +    only_used_in_recursion::ONLY_USED_IN_RECURSION,
-     path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
 +    operators::ABSURD_EXTREME_COMPARISONS,
 +    operators::ARITHMETIC,
 +    operators::ASSIGN_OP_PATTERN,
 +    operators::BAD_BIT_MASK,
 +    operators::CMP_NAN,
 +    operators::CMP_OWNED,
 +    operators::DOUBLE_COMPARISONS,
 +    operators::DURATION_SUBSEC,
 +    operators::EQ_OP,
 +    operators::ERASING_OP,
 +    operators::FLOAT_ARITHMETIC,
 +    operators::FLOAT_CMP,
 +    operators::FLOAT_CMP_CONST,
 +    operators::FLOAT_EQUALITY_WITHOUT_ABS,
 +    operators::IDENTITY_OP,
 +    operators::INEFFECTIVE_BIT_MASK,
 +    operators::INTEGER_ARITHMETIC,
 +    operators::INTEGER_DIVISION,
 +    operators::MISREFACTORED_ASSIGN_OP,
 +    operators::MODULO_ARITHMETIC,
 +    operators::MODULO_ONE,
 +    operators::NEEDLESS_BITWISE_BOOL,
 +    operators::OP_REF,
 +    operators::PTR_EQ,
 +    operators::SELF_ASSIGNMENT,
 +    operators::VERBOSE_BIT_MASK,
 +    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,
 +    partialeq_to_none::PARTIALEQ_TO_NONE,
 +    pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
 +    pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
-     ranges::RANGE_ZIP_WITH_LEN,
 +    pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
 +    precedence::PRECEDENCE,
 +    ptr::CMP_NULL,
 +    ptr::INVALID_NULL_PTR_USAGE,
 +    ptr::MUT_FROM_REF,
 +    ptr::PTR_ARG,
 +    ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
 +    pub_use::PUB_USE,
 +    question_mark::QUESTION_MARK,
 +    ranges::MANUAL_RANGE_CONTAINS,
 +    ranges::RANGE_MINUS_ONE,
 +    ranges::RANGE_PLUS_ONE,
-     repeat_once::REPEAT_ONCE,
 +    ranges::REVERSED_EMPTY_RANGES,
 +    rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
 +    read_zero_byte_vec::READ_ZERO_BYTE_VEC,
 +    redundant_clone::REDUNDANT_CLONE,
 +    redundant_closure_call::REDUNDANT_CLOSURE_CALL,
 +    redundant_else::REDUNDANT_ELSE,
 +    redundant_field_names::REDUNDANT_FIELD_NAMES,
 +    redundant_pub_crate::REDUNDANT_PUB_CRATE,
 +    redundant_slicing::DEREF_BY_SLICING,
 +    redundant_slicing::REDUNDANT_SLICING,
 +    redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES,
 +    ref_option_ref::REF_OPTION_REF,
 +    reference::DEREF_ADDROF,
 +    regex::INVALID_REGEX,
 +    regex::TRIVIAL_REGEX,
-     stable_sort_primitive::STABLE_SORT_PRIMITIVE,
 +    return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
 +    returns::LET_AND_RETURN,
 +    returns::NEEDLESS_RETURN,
 +    same_name_method::SAME_NAME_METHOD,
 +    self_named_constructors::SELF_NAMED_CONSTRUCTORS,
 +    semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
 +    serde_api::SERDE_API_MISUSE,
 +    shadow::SHADOW_REUSE,
 +    shadow::SHADOW_SAME,
 +    shadow::SHADOW_UNRELATED,
 +    single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES,
 +    single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
 +    size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
 +    slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
-     transmuting_null::TRANSMUTING_NULL,
 +    std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
 +    std_instead_of_core::STD_INSTEAD_OF_ALLOC,
 +    std_instead_of_core::STD_INSTEAD_OF_CORE,
 +    strings::STRING_ADD,
 +    strings::STRING_ADD_ASSIGN,
 +    strings::STRING_FROM_UTF8_AS_BYTES,
 +    strings::STRING_LIT_AS_BYTES,
 +    strings::STRING_SLICE,
 +    strings::STRING_TO_STRING,
 +    strings::STR_TO_STRING,
 +    strings::TRIM_SPLIT_WHITESPACE,
 +    strlen_on_c_strings::STRLEN_ON_C_STRINGS,
 +    suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS,
 +    suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL,
 +    suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL,
 +    swap::ALMOST_SWAPPED,
 +    swap::MANUAL_SWAP,
 +    swap_ptr_to_ref::SWAP_PTR_TO_REF,
 +    tabs_in_doc_comments::TABS_IN_DOC_COMMENTS,
 +    temporary_assignment::TEMPORARY_ASSIGNMENT,
 +    to_digit_is_some::TO_DIGIT_IS_SOME,
 +    trailing_empty_array::TRAILING_EMPTY_ARRAY,
 +    trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS,
 +    trait_bounds::TYPE_REPETITION_IN_BOUNDS,
 +    transmute::CROSSPOINTER_TRANSMUTE,
 +    transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    transmute::TRANSMUTE_BYTES_TO_STR,
 +    transmute::TRANSMUTE_FLOAT_TO_INT,
 +    transmute::TRANSMUTE_INT_TO_BOOL,
 +    transmute::TRANSMUTE_INT_TO_CHAR,
 +    transmute::TRANSMUTE_INT_TO_FLOAT,
 +    transmute::TRANSMUTE_NUM_TO_BYTES,
 +    transmute::TRANSMUTE_PTR_TO_PTR,
 +    transmute::TRANSMUTE_PTR_TO_REF,
 +    transmute::TRANSMUTE_UNDEFINED_REPR,
++    transmute::TRANSMUTING_NULL,
 +    transmute::UNSOUND_COLLECTION_TRANSMUTE,
 +    transmute::USELESS_TRANSMUTE,
 +    transmute::WRONG_TRANSMUTE,
-     unit_hash::UNIT_HASH,
 +    types::BORROWED_BOX,
 +    types::BOX_COLLECTION,
 +    types::LINKEDLIST,
 +    types::OPTION_OPTION,
 +    types::RC_BUFFER,
 +    types::RC_MUTEX,
 +    types::REDUNDANT_ALLOCATION,
 +    types::TYPE_COMPLEXITY,
 +    types::VEC_BOX,
 +    undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
 +    unicode::INVISIBLE_CHARACTERS,
 +    unicode::NON_ASCII_LITERAL,
 +    unicode::UNICODE_NOT_NFC,
 +    uninit_vec::UNINIT_VEC,
-     unnecessary_sort_by::UNNECESSARY_SORT_BY,
 +    unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
 +    unit_types::LET_UNIT_VALUE,
 +    unit_types::UNIT_ARG,
 +    unit_types::UNIT_CMP,
 +    unnamed_address::FN_ADDRESS_COMPARISONS,
 +    unnamed_address::VTABLE_ADDRESS_COMPARISONS,
 +    unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
 +    unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
-     vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
-     verbose_file_reads::VERBOSE_FILE_READS,
 +    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_peekable::UNUSED_PEEKABLE,
 +    unused_rounding::UNUSED_ROUNDING,
 +    unused_self::UNUSED_SELF,
 +    unused_unit::UNUSED_UNIT,
 +    unwrap::PANICKING_UNWRAP,
 +    unwrap::UNNECESSARY_UNWRAP,
 +    unwrap_in_result::UNWRAP_IN_RESULT,
 +    upper_case_acronyms::UPPER_CASE_ACRONYMS,
 +    use_self::USE_SELF,
 +    useless_conversion::USELESS_CONVERSION,
 +    vec::USELESS_VEC,
 +    vec_init_then_push::VEC_INIT_THEN_PUSH,
 +    wildcard_imports::ENUM_GLOB_USE,
 +    wildcard_imports::WILDCARD_IMPORTS,
++    write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
 +    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 91210b23afe30c46c31d796b7b4f1e1bdcae82b5,0000000000000000000000000000000000000000..e319e7ee72c57ac656a77dbe35cd248795788d41
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,36 @@@
-     LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
 +    LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR),
 +    LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY),
 +    LintId::of(copies::BRANCHES_SHARING_CODE),
 +    LintId::of(equatable_if_let::EQUATABLE_IF_LET),
 +    LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM),
 +    LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS),
 +    LintId::of(floating_point_arithmetic::SUBOPTIMAL_FLOPS),
 +    LintId::of(future_not_send::FUTURE_NOT_SEND),
 +    LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
 +    LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
 +    LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
++    LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
++    LintId::of(methods::ITER_ON_SINGLE_ITEMS),
 +    LintId::of(methods::ITER_WITH_DRAIN),
++    LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
 +    LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
 +    LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
 +    LintId::of(mutex_atomic::MUTEX_ATOMIC),
 +    LintId::of(mutex_atomic::MUTEX_INTEGER),
 +    LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
 +    LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
-     LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
 +    LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
 +    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::TRANSMUTE_UNDEFINED_REPR),
 +    LintId::of(unused_rounding::UNUSED_ROUNDING),
 +    LintId::of(use_self::USE_SELF),
 +])
index bd7d1a15ab4ea1dad05f3b905d28f3392107d38f,0000000000000000000000000000000000000000..584ccf55e5114fc8f008eb57a4c14bb1de4b85be
mode 100644,000000..100644
--- /dev/null
@@@ -1,103 -1,0 +1,104 @@@
-     LintId::of(borrow_as_ptr::BORROW_AS_PTR),
-     LintId::of(bytecount::NAIVE_BYTECOUNT),
-     LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +// 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(manual_ok_or::MANUAL_OK_OR),
++    LintId::of(casts::BORROW_AS_PTR),
 +    LintId::of(casts::CAST_LOSSLESS),
 +    LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
 +    LintId::of(casts::CAST_POSSIBLE_WRAP),
 +    LintId::of(casts::CAST_PRECISION_LOSS),
 +    LintId::of(casts::CAST_PTR_ALIGNMENT),
 +    LintId::of(casts::CAST_SIGN_LOSS),
 +    LintId::of(casts::PTR_AS_PTR),
 +    LintId::of(checked_conversions::CHECKED_CONVERSIONS),
 +    LintId::of(copies::SAME_FUNCTIONS_IN_IF_CONDITION),
 +    LintId::of(copy_iterator::COPY_ITERATOR),
 +    LintId::of(default::DEFAULT_TRAIT_ACCESS),
 +    LintId::of(dereference::EXPLICIT_DEREF_METHODS),
 +    LintId::of(dereference::REF_BINDING_TO_REFERENCE),
 +    LintId::of(derive::EXPL_IMPL_CLONE_ON_COPY),
 +    LintId::of(derive::UNSAFE_DERIVE_DESERIALIZE),
 +    LintId::of(doc::DOC_MARKDOWN),
 +    LintId::of(doc::MISSING_ERRORS_DOC),
 +    LintId::of(doc::MISSING_PANICS_DOC),
 +    LintId::of(doc_link_with_quotes::DOC_LINK_WITH_QUOTES),
 +    LintId::of(empty_enum::EMPTY_ENUM),
 +    LintId::of(enum_variants::MODULE_NAME_REPETITIONS),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS),
 +    LintId::of(excessive_bools::FN_PARAMS_EXCESSIVE_BOOLS),
 +    LintId::of(excessive_bools::STRUCT_EXCESSIVE_BOOLS),
 +    LintId::of(functions::MUST_USE_CANDIDATE),
 +    LintId::of(functions::TOO_MANY_LINES),
 +    LintId::of(if_not_else::IF_NOT_ELSE),
 +    LintId::of(implicit_hasher::IMPLICIT_HASHER),
 +    LintId::of(implicit_saturating_sub::IMPLICIT_SATURATING_SUB),
 +    LintId::of(inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR),
 +    LintId::of(infinite_iter::MAYBE_INFINITE_ITER),
 +    LintId::of(invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS),
 +    LintId::of(items_after_statements::ITEMS_AFTER_STATEMENTS),
 +    LintId::of(iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR),
 +    LintId::of(large_stack_arrays::LARGE_STACK_ARRAYS),
 +    LintId::of(let_underscore::LET_UNDERSCORE_DROP),
 +    LintId::of(literal_representation::LARGE_DIGIT_GROUPS),
 +    LintId::of(literal_representation::UNREADABLE_LITERAL),
 +    LintId::of(loops::EXPLICIT_INTO_ITER_LOOP),
 +    LintId::of(loops::EXPLICIT_ITER_LOOP),
 +    LintId::of(macro_use::MACRO_USE_IMPORTS),
 +    LintId::of(manual_assert::MANUAL_ASSERT),
 +    LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
-     LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
++    LintId::of(manual_string_new::MANUAL_STRING_NEW),
 +    LintId::of(matches::MATCH_BOOL),
 +    LintId::of(matches::MATCH_ON_VEC_ITEMS),
 +    LintId::of(matches::MATCH_SAME_ARMS),
 +    LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
 +    LintId::of(matches::MATCH_WILD_ERR_ARM),
 +    LintId::of(matches::SINGLE_MATCH_ELSE),
++    LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
 +    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::MANUAL_OK_OR),
 +    LintId::of(methods::MAP_UNWRAP_OR),
++    LintId::of(methods::NAIVE_BYTECOUNT),
++    LintId::of(methods::STABLE_SORT_PRIMITIVE),
 +    LintId::of(methods::UNNECESSARY_JOIN),
 +    LintId::of(misc::USED_UNDERSCORE_BINDING),
 +    LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
 +    LintId::of(mut_mut::MUT_MUT),
 +    LintId::of(needless_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(operators::FLOAT_CMP),
 +    LintId::of(operators::NEEDLESS_BITWISE_BOOL),
 +    LintId::of(operators::VERBOSE_BIT_MASK),
 +    LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
 +    LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
 +    LintId::of(ranges::RANGE_MINUS_ONE),
 +    LintId::of(ranges::RANGE_PLUS_ONE),
 +    LintId::of(redundant_else::REDUNDANT_ELSE),
 +    LintId::of(ref_option_ref::REF_OPTION_REF),
 +    LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
 +    LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
 +    LintId::of(strings::STRING_ADD_ASSIGN),
 +    LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
 +    LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
 +    LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
 +    LintId::of(types::LINKEDLIST),
 +    LintId::of(types::OPTION_OPTION),
 +    LintId::of(unicode::UNICODE_NOT_NFC),
 +    LintId::of(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 e1b90acb93c2e9ae78baa4778790830996eb5065,0000000000000000000000000000000000000000..195ce41e31e9f6b4687633830b35d974eca6ef3c
mode 100644,000000..100644
--- /dev/null
@@@ -1,31 -1,0 +1,33 @@@
 +// 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(functions::RESULT_LARGE_ERR),
 +    LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
 +    LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
 +    LintId::of(loops::MANUAL_MEMCPY),
 +    LintId::of(loops::MISSING_SPIN_LOOP),
 +    LintId::of(loops::NEEDLESS_COLLECT),
 +    LintId::of(manual_retain::MANUAL_RETAIN),
++    LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
 +    LintId::of(methods::EXPECT_FUN_CALL),
 +    LintId::of(methods::EXTEND_WITH_DRAIN),
 +    LintId::of(methods::ITER_NTH),
 +    LintId::of(methods::ITER_OVEREAGER_CLONED),
 +    LintId::of(methods::MANUAL_STR_REPEAT),
 +    LintId::of(methods::OR_FUN_CALL),
 +    LintId::of(methods::SINGLE_CHAR_PATTERN),
 +    LintId::of(methods::UNNECESSARY_TO_OWNED),
 +    LintId::of(operators::CMP_OWNED),
 +    LintId::of(redundant_clone::REDUNDANT_CLONE),
 +    LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
 +    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 a7339ef272174d4ee4955b60edd66778cd032461,0000000000000000000000000000000000000000..dd1e1e1a8e33d82088578a75adcaa49cf7db7128
mode 100644,000000..100644
--- /dev/null
@@@ -1,88 -1,0 +1,88 @@@
-     LintId::of(as_underscore::AS_UNDERSCORE),
 +// 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(as_conversions::AS_CONVERSIONS),
-     LintId::of(map_err_ignore::MAP_ERR_IGNORE),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
 +    LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
 +    LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
 +    LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
++    LintId::of(casts::AS_UNDERSCORE),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
 +    LintId::of(create_dir::CREATE_DIR),
 +    LintId::of(dbg_macro::DBG_MACRO),
 +    LintId::of(default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK),
 +    LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
 +    LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
 +    LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
 +    LintId::of(empty_drop::EMPTY_DROP),
 +    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
 +    LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
 +    LintId::of(exit::EXIT),
 +    LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
 +    LintId::of(format_push_string::FORMAT_PUSH_STRING),
 +    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(large_include_file::LARGE_INCLUDE_FILE),
 +    LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
 +    LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
-     LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
 +    LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
 +    LintId::of(matches::TRY_ERR),
 +    LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
 +    LintId::of(mem_forget::MEM_FORGET),
 +    LintId::of(methods::CLONE_ON_REF_PTR),
 +    LintId::of(methods::EXPECT_USED),
 +    LintId::of(methods::FILETYPE_IS_FILE),
 +    LintId::of(methods::GET_UNWRAP),
++    LintId::of(methods::MAP_ERR_IGNORE),
 +    LintId::of(methods::UNWRAP_USED),
++    LintId::of(methods::VERBOSE_FILE_READS),
 +    LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
 +    LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
 +    LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
 +    LintId::of(missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS),
 +    LintId::of(missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES),
 +    LintId::of(missing_inline::MISSING_INLINE_IN_PUBLIC_ITEMS),
 +    LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
 +    LintId::of(module_style::MOD_MODULE_FILES),
 +    LintId::of(module_style::SELF_NAMED_MODULE_FILES),
 +    LintId::of(operators::ARITHMETIC),
 +    LintId::of(operators::FLOAT_ARITHMETIC),
 +    LintId::of(operators::FLOAT_CMP_CONST),
 +    LintId::of(operators::INTEGER_ARITHMETIC),
 +    LintId::of(operators::INTEGER_DIVISION),
 +    LintId::of(operators::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(pub_use::PUB_USE),
 +    LintId::of(redundant_slicing::DEREF_BY_SLICING),
 +    LintId::of(same_name_method::SAME_NAME_METHOD),
 +    LintId::of(shadow::SHADOW_REUSE),
 +    LintId::of(shadow::SHADOW_SAME),
 +    LintId::of(shadow::SHADOW_UNRELATED),
 +    LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
 +    LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
 +    LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
 +    LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
 +    LintId::of(strings::STRING_ADD),
 +    LintId::of(strings::STRING_SLICE),
 +    LintId::of(strings::STRING_TO_STRING),
 +    LintId::of(strings::STR_TO_STRING),
 +    LintId::of(types::RC_BUFFER),
 +    LintId::of(types::RC_MUTEX),
 +    LintId::of(undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS),
 +    LintId::of(unicode::NON_ASCII_LITERAL),
 +    LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
 +    LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
 +    LintId::of(write::PRINT_STDERR),
 +    LintId::of(write::PRINT_STDOUT),
 +    LintId::of(write::USE_DEBUG),
 +])
index bfa654238f130386f1332b134d6a86eefc0fe544,0000000000000000000000000000000000000000..b5cb078e7a3ccce2fc5a8f0b7ed93b68cca24585
mode 100644,000000..100644
--- /dev/null
@@@ -1,128 -1,0 +1,128 @@@
-     LintId::of(get_first::GET_FIRST),
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::style", Some("clippy_style"), vec![
 +    LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
 +    LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
 +    LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST),
 +    LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
 +    LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),
 +    LintId::of(collapsible_if::COLLAPSIBLE_IF),
 +    LintId::of(comparison_chain::COMPARISON_CHAIN),
 +    LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
 +    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
 +    LintId::of(dereference::NEEDLESS_BORROW),
 +    LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
 +    LintId::of(disallowed_methods::DISALLOWED_METHODS),
 +    LintId::of(disallowed_names::DISALLOWED_NAMES),
 +    LintId::of(disallowed_types::DISALLOWED_TYPES),
 +    LintId::of(doc::MISSING_SAFETY_DOC),
 +    LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
 +    LintId::of(enum_variants::ENUM_VARIANT_NAMES),
 +    LintId::of(enum_variants::MODULE_INCEPTION),
 +    LintId::of(eta_reduction::REDUNDANT_CLOSURE),
 +    LintId::of(float_literal::EXCESSIVE_PRECISION),
 +    LintId::of(from_over_into::FROM_OVER_INTO),
 +    LintId::of(from_str_radix_10::FROM_STR_RADIX_10),
 +    LintId::of(functions::DOUBLE_MUST_USE),
 +    LintId::of(functions::MUST_USE_UNIT),
 +    LintId::of(functions::RESULT_UNIT_ERR),
-     LintId::of(map_clone::MAP_CLONE),
 +    LintId::of(inherent_to_string::INHERENT_TO_STRING),
 +    LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
 +    LintId::of(len_zero::COMPARISON_TO_EMPTY),
 +    LintId::of(len_zero::LEN_WITHOUT_IS_EMPTY),
 +    LintId::of(len_zero::LEN_ZERO),
 +    LintId::of(literal_representation::INCONSISTENT_DIGIT_GROUPING),
 +    LintId::of(literal_representation::UNUSUAL_BYTE_GROUPINGS),
 +    LintId::of(loops::FOR_KV_MAP),
 +    LintId::of(loops::NEEDLESS_RANGE_LOOP),
 +    LintId::of(loops::SAME_ITEM_PUSH),
 +    LintId::of(loops::WHILE_LET_ON_ITERATOR),
 +    LintId::of(main_recursion::MAIN_RECURSION),
 +    LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
 +    LintId::of(manual_bits::MANUAL_BITS),
 +    LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
-     LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
 +    LintId::of(match_result_ok::MATCH_RESULT_OK),
 +    LintId::of(matches::COLLAPSIBLE_MATCH),
 +    LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
 +    LintId::of(matches::MANUAL_MAP),
 +    LintId::of(matches::MATCH_LIKE_MATCHES_MACRO),
 +    LintId::of(matches::MATCH_OVERLAPPING_ARM),
 +    LintId::of(matches::MATCH_REF_PATS),
 +    LintId::of(matches::REDUNDANT_PATTERN_MATCHING),
 +    LintId::of(matches::SINGLE_MATCH),
 +    LintId::of(mem_replace::MEM_REPLACE_OPTION_WITH_NONE),
 +    LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
 +    LintId::of(methods::BYTES_NTH),
 +    LintId::of(methods::CHARS_LAST_CMP),
 +    LintId::of(methods::CHARS_NEXT_CMP),
 +    LintId::of(methods::ERR_EXPECT),
++    LintId::of(methods::GET_FIRST),
 +    LintId::of(methods::INTO_ITER_ON_REF),
 +    LintId::of(methods::IS_DIGIT_ASCII_RADIX),
 +    LintId::of(methods::ITER_CLONED_COLLECT),
 +    LintId::of(methods::ITER_NEXT_SLICE),
 +    LintId::of(methods::ITER_NTH_ZERO),
 +    LintId::of(methods::ITER_SKIP_NEXT),
 +    LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
++    LintId::of(methods::MAP_CLONE),
 +    LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
++    LintId::of(methods::MUT_MUTEX_LOCK),
 +    LintId::of(methods::NEW_RET_NO_SELF),
 +    LintId::of(methods::OBFUSCATED_IF_ELSE),
 +    LintId::of(methods::OK_EXPECT),
 +    LintId::of(methods::OPTION_MAP_OR_NONE),
 +    LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
 +    LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
 +    LintId::of(methods::SINGLE_CHAR_ADD_STR),
 +    LintId::of(methods::STRING_EXTEND_CHARS),
 +    LintId::of(methods::UNNECESSARY_FOLD),
 +    LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
 +    LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
 +    LintId::of(methods::WRONG_SELF_CONVENTION),
 +    LintId::of(misc::TOPLEVEL_REF_ARG),
 +    LintId::of(misc::ZERO_PTR),
 +    LintId::of(misc_early::BUILTIN_TYPE_SHADOW),
 +    LintId::of(misc_early::DOUBLE_NEG),
 +    LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
 +    LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
 +    LintId::of(misc_early::REDUNDANT_PATTERN),
 +    LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
 +    LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
 +    LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
 +    LintId::of(neg_multiply::NEG_MULTIPLY),
 +    LintId::of(new_without_default::NEW_WITHOUT_DEFAULT),
 +    LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
 +    LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
 +    LintId::of(operators::ASSIGN_OP_PATTERN),
 +    LintId::of(operators::OP_REF),
 +    LintId::of(operators::PTR_EQ),
 +    LintId::of(partialeq_to_none::PARTIALEQ_TO_NONE),
 +    LintId::of(ptr::CMP_NULL),
 +    LintId::of(ptr::PTR_ARG),
 +    LintId::of(question_mark::QUESTION_MARK),
 +    LintId::of(ranges::MANUAL_RANGE_CONTAINS),
 +    LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
 +    LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
 +    LintId::of(returns::LET_AND_RETURN),
 +    LintId::of(returns::NEEDLESS_RETURN),
 +    LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
 +    LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
 +    LintId::of(strings::TRIM_SPLIT_WHITESPACE),
 +    LintId::of(tabs_in_doc_comments::TABS_IN_DOC_COMMENTS),
 +    LintId::of(to_digit_is_some::TO_DIGIT_IS_SOME),
 +    LintId::of(unit_types::LET_UNIT_VALUE),
 +    LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
 +    LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
 +    LintId::of(unused_unit::UNUSED_UNIT),
 +    LintId::of(upper_case_acronyms::UPPER_CASE_ACRONYMS),
 +    LintId::of(write::PRINTLN_EMPTY_STRING),
 +    LintId::of(write::PRINT_LITERAL),
 +    LintId::of(write::PRINT_WITH_NEWLINE),
 +    LintId::of(write::WRITELN_EMPTY_STRING),
 +    LintId::of(write::WRITE_LITERAL),
 +    LintId::of(write::WRITE_WITH_NEWLINE),
 +])
index 964992bd94fe259f64aa4cd6a99a3e05621cca44,0000000000000000000000000000000000000000..8f131bbf98be3fecab313b2ae904e982620fd88d
mode 100644,000000..100644
--- /dev/null
@@@ -1,35 -1,0 +1,40 @@@
 +// This file was generated by `cargo dev update_lints`.
 +// Use that command to update this file and do not edit by hand.
 +// Manual edits will be overwritten.
 +
 +store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
 +    LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
 +    LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
 +    LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
 +    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
 +    LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
 +    LintId::of(casts::CAST_ENUM_TRUNCATION),
++    LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
 +    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
 +    LintId::of(drop_forget_ref::DROP_NON_DROP),
 +    LintId::of(drop_forget_ref::FORGET_NON_DROP),
 +    LintId::of(duplicate_mod::DUPLICATE_MOD),
 +    LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
 +    LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
 +    LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
 +    LintId::of(loops::EMPTY_LOOP),
 +    LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
 +    LintId::of(loops::MUT_RANGE_BOUND),
 +    LintId::of(methods::NO_EFFECT_REPLACE),
 +    LintId::of(methods::SUSPICIOUS_MAP),
++    LintId::of(methods::SUSPICIOUS_TO_OWNED),
++    LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
 +    LintId::of(mut_key::MUTABLE_KEY_TYPE),
 +    LintId::of(octal_escapes::OCTAL_ESCAPES),
 +    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
 +    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
 +    LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
 +    LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
 +    LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
++    LintId::of(unused_peekable::UNUSED_PEEKABLE),
++    LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
 +])
index ec5c73c135764db66e1271342e7c16d277f3b91e,0000000000000000000000000000000000000000..dfdaf90f09f48151eda7964dd3d2a02cba705271
mode 100644,000000..100644
--- /dev/null
@@@ -1,998 -1,0 +1,965 @@@
- mod as_underscore;
 +#![feature(array_windows)]
 +#![feature(binary_heap_into_iter_sorted)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(drain_filter)]
 +#![feature(iter_intersperse)]
 +#![feature(let_chains)]
 +#![feature(let_else)]
 +#![feature(lint_reasons)]
 +#![feature(never_type)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![feature(stmt_expr_attributes)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_docs_in_private_items, clippy::must_use_candidate)]
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +// Disable this rustc lint for now, as it was also done in rustc
 +#![allow(rustc::potential_query_instability)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_arena;
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_driver;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_hir_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_semver::RustcVersion;
 +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
 +///     /// Insert a short example of code that triggers the lint
 +///     /// ```
 +///     ///
 +///     /// Use instead:
 +///     /// ```rust
 +///     /// Insert a short example of improved code that doesn't trigger the lint
 +///     /// ```
 +///     pub LINT_NAME,
 +///     pedantic,
 +///     "description"
 +/// }
 +/// ```
 +/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
 +#[macro_export]
 +macro_rules! declare_clippy_lint {
 +    { $(#[$attr:meta])* pub $name:tt, style, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, correctness, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Deny, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, suspicious, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, complexity, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, perf, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, pedantic, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, restriction, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, cargo, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, nursery, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Allow, $description, report_in_external_macro: true
 +        }
 +    };
 +    { $(#[$attr:meta])* pub $name:tt, internal_warn, $description:tt } => {
 +        declare_tool_lint! {
 +            $(#[$attr])* pub clippy::$name, Warn, $description, report_in_external_macro: true
 +        }
 +    };
 +}
 +
 +#[cfg(feature = "internal")]
 +pub mod deprecated_lints;
 +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 +mod utils;
 +
 +mod renamed_lints;
 +
 +// begin lints modules, do not remove this comment, it’s used in `update_lints`
 +mod almost_complete_letter_range;
 +mod approx_const;
 +mod as_conversions;
- mod borrow_as_ptr;
 +mod asm_syntax;
 +mod assertions_on_constants;
 +mod assertions_on_result_states;
 +mod async_yields_async;
 +mod attrs;
 +mod await_holding_invalid;
 +mod blocks_in_if_conditions;
 +mod bool_assert_comparison;
 +mod booleans;
- mod bytecount;
- mod bytes_count_to_len;
 +mod borrow_deref_ref;
- mod case_sensitive_file_extension_comparisons;
 +mod cargo;
- mod get_first;
 +mod casts;
 +mod checked_conversions;
 +mod cognitive_complexity;
 +mod collapsible_if;
 +mod comparison_chain;
 +mod copies;
 +mod copy_iterator;
 +mod crate_in_macro_def;
 +mod create_dir;
 +mod dbg_macro;
 +mod default;
 +mod default_instead_of_iter_empty;
 +mod default_numeric_fallback;
 +mod default_union_representation;
 +mod dereference;
 +mod derivable_impls;
 +mod derive;
 +mod disallowed_methods;
 +mod disallowed_names;
 +mod disallowed_script_idents;
 +mod disallowed_types;
 +mod doc;
 +mod doc_link_with_quotes;
 +mod double_parens;
 +mod drop_forget_ref;
 +mod duplicate_mod;
 +mod else_if_without_else;
 +mod empty_drop;
 +mod empty_enum;
 +mod empty_structs_with_brackets;
 +mod entry;
 +mod enum_clike;
 +mod enum_variants;
 +mod equatable_if_let;
 +mod escape;
 +mod eta_reduction;
 +mod excessive_bools;
 +mod exhaustive_items;
 +mod exit;
 +mod explicit_write;
 +mod fallible_impl_from;
 +mod float_literal;
 +mod floating_point_arithmetic;
 +mod format;
 +mod format_args;
 +mod format_impl;
 +mod format_push_string;
 +mod formatting;
 +mod from_over_into;
 +mod from_str_radix_10;
 +mod functions;
 +mod future_not_send;
- mod manual_ok_or;
 +mod if_let_mutex;
 +mod if_not_else;
 +mod if_then_some_else_none;
 +mod implicit_hasher;
 +mod implicit_return;
 +mod implicit_saturating_sub;
 +mod inconsistent_struct_constructor;
 +mod index_refutable_slice;
 +mod indexing_slicing;
 +mod infinite_iter;
 +mod inherent_impl;
 +mod inherent_to_string;
 +mod init_numbered_fields;
 +mod inline_fn_without_body;
 +mod int_plus_one;
 +mod invalid_upcast_comparisons;
 +mod invalid_utf8_in_unchecked;
 +mod items_after_statements;
 +mod iter_not_returning_iterator;
 +mod large_const_arrays;
 +mod large_enum_variant;
 +mod large_include_file;
 +mod large_stack_arrays;
 +mod len_zero;
 +mod let_if_seq;
 +mod let_underscore;
 +mod lifetimes;
 +mod literal_representation;
 +mod loops;
 +mod macro_use;
 +mod main_recursion;
 +mod manual_assert;
 +mod manual_async_fn;
 +mod manual_bits;
 +mod manual_instant_elapsed;
 +mod manual_non_exhaustive;
- mod map_clone;
- mod map_err_ignore;
 +mod manual_rem_euclid;
 +mod manual_retain;
++mod manual_string_new;
 +mod manual_strip;
- mod mut_mutex_lock;
 +mod map_unit_fn;
 +mod match_result_ok;
 +mod matches;
 +mod mem_forget;
 +mod mem_replace;
 +mod methods;
 +mod minmax;
 +mod misc;
 +mod misc_early;
 +mod mismatching_type_param_order;
 +mod missing_const_for_fn;
 +mod missing_doc;
 +mod missing_enforced_import_rename;
 +mod missing_inline;
 +mod mixed_read_write_in_expression;
 +mod module_style;
++mod multi_assignments;
 +mod mut_key;
 +mod mut_mut;
- mod open_options;
 +mod mut_reference;
 +mod mutable_debug_assertion;
 +mod mutex_atomic;
 +mod needless_arbitrary_self_type;
 +mod needless_bool;
 +mod needless_borrowed_ref;
 +mod needless_continue;
 +mod needless_for_each;
 +mod needless_late_init;
 +mod needless_parens_on_range_literals;
 +mod needless_pass_by_value;
 +mod needless_question_mark;
 +mod needless_update;
 +mod neg_cmp_op_on_partial_ord;
 +mod neg_multiply;
 +mod new_without_default;
 +mod no_effect;
 +mod non_copy_const;
 +mod non_expressive_names;
 +mod non_octal_unix_permissions;
 +mod non_send_fields_in_send_ty;
 +mod nonstandard_macro_braces;
 +mod octal_escapes;
 +mod only_used_in_recursion;
- mod path_buf_push_overwrite;
 +mod operators;
 +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 partialeq_to_none;
 +mod pass_by_ref_or_value;
- mod repeat_once;
 +mod pattern_type_mismatch;
 +mod precedence;
 +mod ptr;
 +mod ptr_offset_with_cast;
 +mod pub_use;
 +mod question_mark;
 +mod ranges;
 +mod rc_clone_in_vec_init;
 +mod read_zero_byte_vec;
 +mod redundant_clone;
 +mod redundant_closure_call;
 +mod redundant_else;
 +mod redundant_field_names;
 +mod redundant_pub_crate;
 +mod redundant_slicing;
 +mod redundant_static_lifetimes;
 +mod ref_option_ref;
 +mod reference;
 +mod regex;
- mod stable_sort_primitive;
 +mod return_self_not_must_use;
 +mod returns;
 +mod same_name_method;
 +mod self_named_constructors;
 +mod semicolon_if_nothing_returned;
 +mod serde_api;
 +mod shadow;
 +mod single_char_lifetime_names;
 +mod single_component_path_imports;
 +mod size_of_in_element_count;
 +mod slow_vector_initialization;
- mod transmuting_null;
 +mod std_instead_of_core;
 +mod strings;
 +mod strlen_on_c_strings;
 +mod suspicious_operation_groupings;
 +mod suspicious_trait_impl;
 +mod swap;
 +mod swap_ptr_to_ref;
 +mod tabs_in_doc_comments;
 +mod temporary_assignment;
 +mod to_digit_is_some;
 +mod trailing_empty_array;
 +mod trait_bounds;
 +mod transmute;
- mod unit_hash;
 +mod types;
 +mod undocumented_unsafe_blocks;
 +mod unicode;
 +mod uninit_vec;
- mod unnecessary_sort_by;
 +mod unit_return_expecting_ord;
 +mod unit_types;
 +mod unnamed_address;
 +mod unnecessary_owned_empty_strings;
 +mod unnecessary_self_imports;
- mod vec_resize_to_zero;
- mod verbose_file_reads;
 +mod unnecessary_wraps;
 +mod unnested_or_patterns;
 +mod unsafe_removed_from_name;
 +mod unused_async;
 +mod unused_io_amount;
++mod unused_peekable;
 +mod unused_rounding;
 +mod unused_self;
 +mod unused_unit;
 +mod unwrap;
 +mod unwrap_in_result;
 +mod upper_case_acronyms;
 +mod use_self;
 +mod useless_conversion;
 +mod vec;
 +mod vec_init_then_push;
-     store.register_late_pass(|| Box::new(unit_hash::UnitHash));
 +mod wildcard_imports;
 +mod write;
 +mod zero_div_zero;
 +mod zero_sized_map_values;
 +// end lints modules, do not remove this comment, it’s used in `update_lints`
 +
 +pub use crate::utils::conf::Conf;
 +use crate::utils::conf::{format_error, TryConf};
 +
 +/// Register all pre expansion lints
 +///
 +/// Pre-expansion lints run before any macro expansion has happened.
 +///
 +/// Note that due to the architecture of the compiler, currently `cfg_attr` attributes on crate
 +/// level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
 +
 +    let msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    store.register_pre_expansion_pass(|| Box::new(write::Write::default()));
 +    store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 +}
 +
 +fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
 +    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
 +        .ok()
 +        .and_then(|v| parse_msrv(&v, None, None));
 +    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
 +        parse_msrv(s, None, None).or_else(|| {
 +            sess.err(&format!(
 +                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
 +                s
 +            ));
 +            None
 +        })
 +    });
 +
 +    if let Some(cargo_msrv) = cargo_msrv {
 +        if let Some(clippy_msrv) = clippy_msrv {
 +            // if both files have an msrv, let's compare them and emit a warning if they differ
 +            if clippy_msrv != cargo_msrv {
 +                sess.warn(&format!(
 +                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`",
 +                    clippy_msrv
 +                ));
 +            }
 +
 +            Some(clippy_msrv)
 +        } else {
 +            Some(cargo_msrv)
 +        }
 +    } else {
 +        clippy_msrv
 +    }
 +}
 +
 +#[doc(hidden)]
 +pub fn read_conf(sess: &Session) -> Conf {
 +    let file_name = match utils::conf::lookup_conf_file() {
 +        Ok(Some(path)) => path,
 +        Ok(None) => return Conf::default(),
 +        Err(error) => {
 +            sess.struct_err(&format!("error finding Clippy's configuration file: {}", error))
 +                .emit();
 +            return Conf::default();
 +        },
 +    };
 +
 +    let TryConf { conf, errors, warnings } = utils::conf::read(&file_name);
 +    // all conf errors are non-fatal, we just use the default conf in case of error
 +    for error in errors {
 +        sess.err(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(error)
 +        ));
 +    }
 +
 +    for warning in warnings {
 +        sess.struct_warn(&format!(
 +            "error reading Clippy's configuration file `{}`: {}",
 +            file_name.display(),
 +            format_error(warning)
 +        ))
 +        .emit();
 +    }
 +
 +    conf
 +}
 +
 +/// Register all lints and lint groups with the rustc plugin registry
 +///
 +/// Used in `./src/driver.rs`.
 +#[expect(clippy::too_many_lines)]
 +pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) {
 +    register_removed_non_tool_lints(store);
 +
 +    include!("lib.deprecated.rs");
 +
 +    include!("lib.register_lints.rs");
 +    include!("lib.register_restriction.rs");
 +    include!("lib.register_pedantic.rs");
 +
 +    #[cfg(feature = "internal")]
 +    include!("lib.register_internal.rs");
 +
 +    include!("lib.register_all.rs");
 +    include!("lib.register_style.rs");
 +    include!("lib.register_complexity.rs");
 +    include!("lib.register_correctness.rs");
 +    include!("lib.register_suspicious.rs");
 +    include!("lib.register_perf.rs");
 +    include!("lib.register_cargo.rs");
 +    include!("lib.register_nursery.rs");
 +
 +    #[cfg(feature = "internal")]
 +    {
 +        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
 +            store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
 +            return;
 +        }
 +    }
 +
 +    // all the internal lints
 +    #[cfg(feature = "internal")]
 +    {
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal));
 +        store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CollapsibleCalls));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::CompilerLintFunctions::new()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::IfChainStyle));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InvalidPaths));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::InterningDefinedSymbol::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::LintWithoutLintPass::default()));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MatchTypeOnDiagItem));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::OuterExpnDataPass));
 +        store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
 +    }
 +
 +    let arithmetic_allowed = conf.arithmetic_allowed.clone();
 +    store.register_late_pass(move || Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone())));
 +    store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
 +    store.register_late_pass(|| Box::new(utils::author::Author));
 +    let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
 +    store.register_late_pass(move || {
 +        Box::new(await_holding_invalid::AwaitHolding::new(
 +            await_holding_invalid_types.clone(),
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(serde_api::SerdeApi));
 +    let vec_box_size_threshold = conf.vec_box_size_threshold;
 +    let type_complexity_threshold = conf.type_complexity_threshold;
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    store.register_late_pass(move || {
 +        Box::new(types::Types::new(
 +            vec_box_size_threshold,
 +            type_complexity_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(booleans::NonminimalBool));
 +    store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
 +    store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
 +    store.register_late_pass(|| Box::new(ptr::Ptr));
 +    store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
 +    store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
 +    store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
 +    store.register_late_pass(|| Box::new(misc::MiscLints));
 +    store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
 +    store.register_late_pass(|| Box::new(mut_mut::MutMut));
 +    store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
 +    store.register_late_pass(|| Box::new(len_zero::LenZero));
 +    store.register_late_pass(|| Box::new(attrs::Attributes));
 +    store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
 +    store.register_late_pass(|| Box::new(unicode::Unicode));
 +    store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
-     store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
 +    store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
 +    store.register_late_pass(|| Box::new(strings::StringAdd));
 +    store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
 +    store.register_late_pass(|| Box::new(implicit_saturating_sub::ImplicitSaturatingSub));
 +    store.register_late_pass(|| Box::new(default_numeric_fallback::DefaultNumericFallback));
 +    store.register_late_pass(|| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
 +    store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
 +    store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 +
 +    let msrv = read_msrv(conf, sess);
 +    let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
 +    let allow_expect_in_tests = conf.allow_expect_in_tests;
 +    let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
 +    store.register_late_pass(move || Box::new(approx_const::ApproxConstant::new(msrv)));
 +    store.register_late_pass(move || {
 +        Box::new(methods::Methods::new(
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        ))
 +    });
 +    store.register_late_pass(move || Box::new(matches::Matches::new(msrv)));
 +    store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_strip::ManualStrip::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv)));
 +    store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv)));
 +    store.register_late_pass(move || Box::new(checked_conversions::CheckedConversions::new(msrv)));
 +    store.register_late_pass(move || Box::new(mem_replace::MemReplace::new(msrv)));
 +    store.register_late_pass(move || Box::new(ranges::Ranges::new(msrv)));
 +    store.register_late_pass(move || Box::new(from_over_into::FromOverInto::new(msrv)));
 +    store.register_late_pass(move || Box::new(use_self::UseSelf::new(msrv)));
 +    store.register_late_pass(move || Box::new(missing_const_for_fn::MissingConstForFn::new(msrv)));
 +    store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
 +    store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
 +    store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
-     store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
 +    store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
 +    store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
 +    let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
 +    store.register_late_pass(move || {
 +        Box::new(index_refutable_slice::IndexRefutableSlice::new(
 +            max_suggested_slice_pattern_length,
 +            msrv,
 +        ))
 +    });
-     store.register_late_pass(|| Box::new(open_options::OpenOptions));
 +    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(bytecount::ByteCount));
 +    store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
 +    store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
 +    store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
 +    store.register_late_pass(|| Box::new(needless_borrowed_ref::NeedlessBorrowedRef));
 +    store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
 +    store.register_late_pass(|| Box::new(no_effect::NoEffect));
 +    store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
 +    store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv)));
 +    let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(cognitive_complexity::CognitiveComplexity::new(
 +            cognitive_complexity_threshold,
 +        ))
 +    });
 +    let too_large_for_stack = conf.too_large_for_stack;
 +    store.register_late_pass(move || Box::new(escape::BoxedLocal { too_large_for_stack }));
 +    store.register_late_pass(move || Box::new(vec::UselessVec { too_large_for_stack }));
 +    store.register_late_pass(|| Box::new(panic_unimplemented::PanicUnimplemented));
 +    store.register_late_pass(|| Box::new(strings::StringLitAsBytes));
 +    store.register_late_pass(|| Box::new(derive::Derive));
 +    store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
 +    store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
 +    store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
 +    store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
 +    store.register_late_pass(|| Box::new(regex::Regex));
 +    store.register_late_pass(|| Box::new(copies::CopyAndPaste));
 +    store.register_late_pass(|| Box::new(copy_iterator::CopyIterator));
 +    store.register_late_pass(|| Box::new(format::UselessFormat));
 +    store.register_late_pass(|| Box::new(swap::Swap));
 +    store.register_late_pass(|| Box::new(overflow_check_conditional::OverflowCheckConditional));
 +    store.register_late_pass(|| Box::new(new_without_default::NewWithoutDefault::default()));
 +    let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
 +    let too_many_arguments_threshold = conf.too_many_arguments_threshold;
 +    let too_many_lines_threshold = conf.too_many_lines_threshold;
++    let large_error_threshold = conf.large_error_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(functions::Functions::new(
 +            too_many_arguments_threshold,
 +            too_many_lines_threshold,
++            large_error_threshold,
 +        ))
 +    });
 +    let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
 +    store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
 +    store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
 +    store.register_late_pass(|| Box::new(mem_forget::MemForget));
 +    store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
 +    store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
 +    store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
 +    store.register_late_pass(|| Box::new(missing_inline::MissingInline));
 +    store.register_late_pass(move || Box::new(exhaustive_items::ExhaustiveItems));
 +    store.register_late_pass(|| Box::new(match_result_ok::MatchResultOk));
 +    store.register_late_pass(|| Box::new(partialeq_ne_impl::PartialEqNeImpl));
 +    store.register_late_pass(|| Box::new(unused_io_amount::UnusedIoAmount));
 +    let enum_variant_size_threshold = conf.enum_variant_size_threshold;
 +    store.register_late_pass(move || Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
 +    store.register_late_pass(|| Box::new(explicit_write::ExplicitWrite));
 +    store.register_late_pass(|| Box::new(needless_pass_by_value::NeedlessPassByValue));
 +    let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
 +        conf.trivial_copy_size_limit,
 +        conf.pass_by_value_size_limit,
 +        conf.avoid_breaking_exported_api,
 +        &sess.target,
 +    );
 +    store.register_late_pass(move || Box::new(pass_by_ref_or_value));
 +    store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
-     store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
 +    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(question_mark::QuestionMark));
 +    store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
 +    store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
 +    store.register_late_pass(|| Box::new(map_unit_fn::MapUnit));
 +    store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
 +    store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
 +    store.register_late_pass(|| Box::new(unwrap::Unwrap));
 +    store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
 +    store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
 +    store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
 +    store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
 +    store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
-     store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
-     store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
 +    store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
 +    store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
-     store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
 +    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_early_pass(|| Box::new(reference::DerefAddrOf));
 +    store.register_early_pass(|| Box::new(double_parens::DoubleParens));
 +    store.register_late_pass(|| Box::new(format_impl::FormatImpl::new()));
 +    store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
 +    store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
 +    store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
 +    store.register_early_pass(|| Box::new(formatting::Formatting));
 +    store.register_early_pass(|| Box::new(misc_early::MiscEarlyLints));
 +    store.register_early_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_late_pass(|| Box::new(redundant_closure_call::RedundantClosureCall));
 +    store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
 +    store.register_late_pass(|| Box::new(returns::Return));
 +    store.register_early_pass(|| Box::new(collapsible_if::CollapsibleIf));
 +    store.register_early_pass(|| Box::new(items_after_statements::ItemsAfterStatements));
 +    store.register_early_pass(|| Box::new(precedence::Precedence));
 +    store.register_late_pass(|| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
 +    store.register_early_pass(|| Box::new(needless_continue::NeedlessContinue));
 +    store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
 +    store.register_late_pass(|| Box::new(create_dir::CreateDir));
 +    store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
 +    let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::LiteralDigitGrouping::new(
 +            literal_representation_lint_fraction_readability,
 +        ))
 +    });
 +    let literal_representation_threshold = conf.literal_representation_threshold;
 +    store.register_early_pass(move || {
 +        Box::new(literal_representation::DecimalLiteralRepresentation::new(
 +            literal_representation_threshold,
 +        ))
 +    });
 +    let enum_variant_name_threshold = conf.enum_variant_name_threshold;
 +    store.register_late_pass(move || {
 +        Box::new(enum_variants::EnumVariantNames::new(
 +            enum_variant_name_threshold,
 +            avoid_breaking_exported_api,
 +        ))
 +    });
 +    store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
 +    let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
 +    store.register_late_pass(move || {
 +        Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
 +            avoid_breaking_exported_api,
 +            upper_case_acronyms_aggressive,
 +        ))
 +    });
 +    store.register_late_pass(|| Box::new(default::Default::default()));
 +    store.register_late_pass(move || Box::new(unused_self::UnusedSelf::new(avoid_breaking_exported_api)));
 +    store.register_late_pass(|| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
 +    store.register_late_pass(|| Box::new(exit::Exit));
 +    store.register_late_pass(|| Box::new(to_digit_is_some::ToDigitIsSome));
 +    let array_size_threshold = conf.array_size_threshold;
 +    store.register_late_pass(move || Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold)));
 +    store.register_late_pass(move || Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
 +    store.register_late_pass(|| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
 +    store.register_early_pass(|| Box::new(as_conversions::AsConversions));
 +    store.register_late_pass(|| Box::new(let_underscore::LetUnderscore));
 +    store.register_early_pass(|| Box::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(dereference::Dereferencing::default()));
 +    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(mut_mutex_lock::MutMutexLock));
++    store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
 +    store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
 +    store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
 +    store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
 +    store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
 +    store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
-     store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
 +    store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
-     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(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(manual_ok_or::ManualOkOr));
 +    store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
-     store.register_late_pass(|| {
-         Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
-     });
 +    store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
 +    store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
 +    let disallowed_methods = conf.disallowed_methods.clone();
 +    store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
 +    store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
 +    store.register_late_pass(|| Box::new(empty_drop::EmptyDrop));
 +    store.register_late_pass(|| Box::new(strings::StrToString));
 +    store.register_late_pass(|| Box::new(strings::StringToString));
 +    store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
 +    store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
-     store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
 +    store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
 +    store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
 +    store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
 +    store.register_late_pass(|| Box::new(bool_assert_comparison::BoolAssertComparison));
 +    store.register_early_pass(move || Box::new(module_style::ModStyle));
 +    store.register_late_pass(|| Box::new(unused_async::UnusedAsync));
 +    let disallowed_types = conf.disallowed_types.clone();
 +    store.register_late_pass(move || Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
 +    let import_renames = conf.enforced_import_renames.clone();
 +    store.register_late_pass(move || {
 +        Box::new(missing_enforced_import_rename::ImportRename::new(
 +            import_renames.clone(),
 +        ))
 +    });
 +    let scripts = conf.allowed_scripts.clone();
 +    store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
 +    store.register_late_pass(|| Box::new(strlen_on_c_strings::StrlenOnCStrings));
 +    store.register_late_pass(move || Box::new(self_named_constructors::SelfNamedConstructors));
 +    store.register_late_pass(move || Box::new(iter_not_returning_iterator::IterNotReturningIterator));
 +    store.register_late_pass(move || Box::new(manual_assert::ManualAssert));
 +    let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
 +    store.register_late_pass(move || {
 +        Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
 +            enable_raw_pointer_heuristic_for_send,
 +        ))
 +    });
 +    store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
 +    store.register_late_pass(move || Box::new(format_args::FormatArgs));
 +    store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
 +    store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
 +    store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit));
 +    store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
 +    store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
 +    store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
-     store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
 +    store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
 +    store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
 +    store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
-     store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
++    store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
 +    let allow_dbg_in_tests = conf.allow_dbg_in_tests;
 +    store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
 +    let cargo_ignore_publish = conf.cargo_ignore_publish;
 +    store.register_late_pass(move || {
 +        Box::new(cargo::Cargo {
 +            ignore_publish: cargo_ignore_publish,
 +        })
 +    });
 +    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
 +    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
 +    store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
 +    store.register_early_pass(|| Box::new(pub_use::PubUse));
 +    store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
-     store.register_late_pass(|| Box::new(get_first::GetFirst));
 +    let max_include_file_size = conf.max_include_file_size;
 +    store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
 +    store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
 +    store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
 +    store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
-     store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
 +    store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
 +    store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
 +    store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
 +    store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
 +    store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
 +    store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
 +    store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
 +    store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
 +    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
 +    store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
 +    store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
 +    store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
 +    store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
 +    store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
++    store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
++    store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
++    store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
 +    // add lints here, do not remove this comment, it's used in `new_lint`
 +}
 +
 +#[rustfmt::skip]
 +fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
 +    store.register_removed(
 +        "should_assert_eq",
 +        "`assert!()` will be more flexible with RFC 2011",
 +    );
 +    store.register_removed(
 +        "extend_from_slice",
 +        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
 +    );
 +    store.register_removed(
 +        "range_step_by_zero",
 +        "`iterator.step_by(0)` panics nowadays",
 +    );
 +    store.register_removed(
 +        "unstable_as_slice",
 +        "`Vec::as_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "unstable_as_mut_slice",
 +        "`Vec::as_mut_slice` has been stabilized in 1.7",
 +    );
 +    store.register_removed(
 +        "misaligned_transmute",
 +        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
 +    );
 +    store.register_removed(
 +        "assign_ops",
 +        "using compound assignment operators (e.g., `+=`) is harmless",
 +    );
 +    store.register_removed(
 +        "if_let_redundant_pattern_matching",
 +        "this lint has been changed to redundant_pattern_matching",
 +    );
 +    store.register_removed(
 +        "unsafe_vector_initialization",
 +        "the replacement suggested by this lint had substantially different behavior",
 +    );
 +    store.register_removed(
 +        "reverse_range_loop",
 +        "this lint is now included in reversed_empty_ranges",
 +    );
 +}
 +
 +/// Register renamed lints.
 +///
 +/// Used in `./src/driver.rs`.
 +pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
 +    for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
 +        ls.register_renamed(old_name, new_name);
 +    }
 +}
 +
 +// only exists to let the dogfood integration test works.
 +// Don't run clippy as an executable directly
 +#[allow(dead_code)]
 +fn main() {
 +    panic!("Please use the cargo-clippy executable");
 +}
index ddaffc751880db30251f01d8c246af8c9d8568d6,0000000000000000000000000000000000000000..6d987f393fa5ce353c2d38dae517e5cdf18f1a6d
mode 100644,000000..100644
--- /dev/null
@@@ -1,369 -1,0 +1,401 @@@
-             self.visit_block_expr(expr, None);
 +use super::NEEDLESS_COLLECT;
 +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
++use clippy_utils::higher;
 +use clippy_utils::source::{snippet, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{can_move_expr_to_closure, is_trait_method, path_to_local, path_to_local_id, CaptureKind};
 +use if_chain::if_chain;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::{Applicability, MultiSpan};
 +use rustc_hir::intravisit::{walk_block, walk_expr, Visitor};
 +use rustc_hir::{Block, Expr, ExprKind, HirId, HirIdSet, Local, Mutability, Node, PatKind, Stmt, StmtKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_middle::ty::{self, Ty};
 +use rustc_span::sym;
 +use rustc_span::Span;
 +
 +const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed";
 +
 +pub(super) fn check<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    check_needless_collect_direct_usage(expr, cx);
 +    check_needless_collect_indirect_usage(expr, cx);
 +}
 +fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    if_chain! {
 +        if let ExprKind::MethodCall(method, args, _) = expr.kind;
 +        if let ExprKind::MethodCall(chain_method, _, _) = args[0].kind;
 +        if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
 +        then {
 +            let ty = cx.typeck_results().expr_ty(&args[0]);
 +            let mut applicability = Applicability::MaybeIncorrect;
 +            let is_empty_sugg = "next().is_none()".to_string();
 +            let method_name = method.ident.name.as_str();
 +            let sugg = if is_type_diagnostic_item(cx, ty, sym::Vec) ||
 +                        is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
 +                        is_type_diagnostic_item(cx, ty, sym::LinkedList) ||
 +                        is_type_diagnostic_item(cx, ty, sym::BinaryHeap) {
 +                match method_name {
 +                    "len" => "count()".to_string(),
 +                    "is_empty" => is_empty_sugg,
 +                    "contains" => {
 +                        let contains_arg = snippet_with_applicability(cx, args[1].span, "??", &mut applicability);
 +                        let (arg, pred) = contains_arg
 +                            .strip_prefix('&')
 +                            .map_or(("&x", &*contains_arg), |s| ("x", s));
 +                        format!("any(|{}| x == {})", arg, pred)
 +                    }
 +                    _ => return,
 +                }
 +            }
 +            else if is_type_diagnostic_item(cx, ty, sym::BTreeMap) ||
 +                is_type_diagnostic_item(cx, ty, sym::HashMap) {
 +                match method_name {
 +                    "is_empty" => is_empty_sugg,
 +                    _ => return,
 +                }
 +            }
 +            else {
 +                return;
 +            };
 +            span_lint_and_sugg(
 +                cx,
 +                NEEDLESS_COLLECT,
 +                chain_method.ident.span.with_hi(expr.span.hi()),
 +                NEEDLESS_COLLECT_MSG,
 +                "replace with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) {
 +    if let ExprKind::Block(block, _) = expr.kind {
 +        for stmt in block.stmts {
 +            if_chain! {
 +                if let StmtKind::Local(local) = stmt.kind;
 +                if let PatKind::Binding(_, id, ..) = local.pat.kind;
 +                if let Some(init_expr) = local.init;
 +                if let ExprKind::MethodCall(method_name, &[ref iter_source], ..) = init_expr.kind;
 +                if method_name.ident.name == sym!(collect) && is_trait_method(cx, init_expr, sym::Iterator);
 +                let ty = cx.typeck_results().expr_ty(init_expr);
 +                if is_type_diagnostic_item(cx, ty, sym::Vec) ||
 +                    is_type_diagnostic_item(cx, ty, sym::VecDeque) ||
 +                    is_type_diagnostic_item(cx, ty, sym::BinaryHeap) ||
 +                    is_type_diagnostic_item(cx, ty, sym::LinkedList);
 +                let iter_ty = cx.typeck_results().expr_ty(iter_source);
 +                if let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty));
 +                if let [iter_call] = &*iter_calls;
 +                then {
 +                    let mut used_count_visitor = UsedCountVisitor {
 +                        cx,
 +                        id,
 +                        count: 0,
 +                    };
 +                    walk_block(&mut used_count_visitor, block);
 +                    if used_count_visitor.count > 1 {
 +                        return;
 +                    }
 +
 +                    // Suggest replacing iter_call with iter_replacement, and removing stmt
 +                    let mut span = MultiSpan::from_span(method_name.ident.span);
 +                    span.push_span_label(iter_call.span, "the iterator could be used here instead");
 +                    span_lint_hir_and_then(
 +                        cx,
 +                        super::NEEDLESS_COLLECT,
 +                        init_expr.hir_id,
 +                        span,
 +                        NEEDLESS_COLLECT_MSG,
 +                        |diag| {
 +                            let iter_replacement = format!("{}{}", Sugg::hir(cx, iter_source, ".."), iter_call.get_iter_method(cx));
 +                            diag.multipart_suggestion(
 +                                iter_call.get_suggestion_text(),
 +                                vec![
 +                                    (stmt.span, String::new()),
 +                                    (iter_call.span, iter_replacement)
 +                                ],
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct IterFunction {
 +    func: IterFunctionKind,
 +    span: Span,
 +}
 +impl IterFunction {
 +    fn get_iter_method(&self, cx: &LateContext<'_>) -> String {
 +        match &self.func {
 +            IterFunctionKind::IntoIter => String::new(),
 +            IterFunctionKind::Len => String::from(".count()"),
 +            IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
 +            IterFunctionKind::Contains(span) => {
 +                let s = snippet(cx, *span, "..");
 +                if let Some(stripped) = s.strip_prefix('&') {
 +                    format!(".any(|x| x == {})", stripped)
 +                } else {
 +                    format!(".any(|x| x == *{})", s)
 +                }
 +            },
 +        }
 +    }
 +    fn get_suggestion_text(&self) -> &'static str {
 +        match &self.func {
 +            IterFunctionKind::IntoIter => {
 +                "use the original Iterator instead of collecting it and then producing a new one"
 +            },
 +            IterFunctionKind::Len => {
 +                "take the original Iterator's count instead of collecting it and finding the length"
 +            },
 +            IterFunctionKind::IsEmpty => {
 +                "check if the original Iterator has anything instead of collecting it and seeing if it's empty"
 +            },
 +            IterFunctionKind::Contains(_) => {
 +                "check if the original Iterator contains an element instead of collecting then checking"
 +            },
 +        }
 +    }
 +}
 +enum IterFunctionKind {
 +    IntoIter,
 +    Len,
 +    IsEmpty,
 +    Contains(Span),
 +}
 +
 +struct IterFunctionVisitor<'a, 'tcx> {
 +    illegal_mutable_capture_ids: HirIdSet,
 +    current_mutably_captured_ids: HirIdSet,
 +    cx: &'a LateContext<'tcx>,
 +    uses: Vec<Option<IterFunction>>,
 +    hir_id_uses_map: FxHashMap<HirId, usize>,
 +    current_statement_hir_id: Option<HirId>,
 +    seen_other: bool,
 +    target: HirId,
 +}
 +impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
 +    fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
 +        for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
++            if check_loop_kind(expr).is_some() {
++                continue;
++            }
 +            self.visit_block_expr(expr, hir_id);
 +        }
 +        if let Some(expr) = block.expr {
++            if let Some(loop_kind) = check_loop_kind(expr) {
++                if let LoopKind::Conditional(block_expr) = loop_kind {
++                    self.visit_block_expr(block_expr, None);
++                }
++            } else {
++                self.visit_block_expr(expr, None);
++            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
 +        // Check function calls on our collection
 +        if let ExprKind::MethodCall(method_name, [recv, args @ ..], _) = &expr.kind {
 +            if method_name.ident.name == sym!(collect) && is_trait_method(self.cx, expr, sym::Iterator) {
 +                self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
 +                self.visit_expr(recv);
 +                return;
 +            }
 +
 +            if path_to_local_id(recv, self.target) {
 +                if self
 +                    .illegal_mutable_capture_ids
 +                    .intersection(&self.current_mutably_captured_ids)
 +                    .next()
 +                    .is_none()
 +                {
 +                    if let Some(hir_id) = self.current_statement_hir_id {
 +                        self.hir_id_uses_map.insert(hir_id, self.uses.len());
 +                    }
 +                    match method_name.ident.name.as_str() {
 +                        "into_iter" => self.uses.push(Some(IterFunction {
 +                            func: IterFunctionKind::IntoIter,
 +                            span: expr.span,
 +                        })),
 +                        "len" => self.uses.push(Some(IterFunction {
 +                            func: IterFunctionKind::Len,
 +                            span: expr.span,
 +                        })),
 +                        "is_empty" => self.uses.push(Some(IterFunction {
 +                            func: IterFunctionKind::IsEmpty,
 +                            span: expr.span,
 +                        })),
 +                        "contains" => self.uses.push(Some(IterFunction {
 +                            func: IterFunctionKind::Contains(args[0].span),
 +                            span: expr.span,
 +                        })),
 +                        _ => {
 +                            self.seen_other = true;
 +                            if let Some(hir_id) = self.current_statement_hir_id {
 +                                self.hir_id_uses_map.remove(&hir_id);
 +                            }
 +                        },
 +                    }
 +                }
 +                return;
 +            }
 +
 +            if let Some(hir_id) = path_to_local(recv) {
 +                if let Some(index) = self.hir_id_uses_map.remove(&hir_id) {
 +                    if self
 +                        .illegal_mutable_capture_ids
 +                        .intersection(&self.current_mutably_captured_ids)
 +                        .next()
 +                        .is_none()
 +                    {
 +                        if let Some(hir_id) = self.current_statement_hir_id {
 +                            self.hir_id_uses_map.insert(hir_id, index);
 +                        }
 +                    } else {
 +                        self.uses[index] = None;
 +                    }
 +                }
 +            }
 +        }
 +        // Check if the collection is used for anything else
 +        if path_to_local_id(expr, self.target) {
 +            self.seen_other = true;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +}
 +
++enum LoopKind<'tcx> {
++    Conditional(&'tcx Expr<'tcx>),
++    Loop,
++}
++
++fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
++    if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
++        return Some(LoopKind::Conditional(let_expr));
++    }
++    if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
++        return Some(LoopKind::Conditional(condition));
++    }
++    if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
++        return Some(LoopKind::Conditional(arg));
++    }
++    if let ExprKind::Loop { .. } = expr.kind {
++        return Some(LoopKind::Loop);
++    }
++
++    None
++}
++
 +impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
 +    fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
 +        self.current_statement_hir_id = hir_id;
 +        self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(expr));
 +        self.visit_expr(expr);
 +    }
 +}
 +
 +fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>, Option<HirId>)> {
 +    match stmt.kind {
 +        StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
 +        StmtKind::Item(..) => None,
 +        StmtKind::Local(Local { init, pat, .. }) => {
 +            if let PatKind::Binding(_, hir_id, ..) = pat.kind {
 +                init.map(|init_expr| (init_expr, Some(hir_id)))
 +            } else {
 +                init.map(|init_expr| (init_expr, None))
 +            }
 +        },
 +    }
 +}
 +
 +struct UsedCountVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    id: HirId,
 +    count: usize,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UsedCountVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::OnlyBodies;
 +
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if path_to_local_id(expr, self.id) {
 +            self.count += 1;
 +        } else {
 +            walk_expr(self, expr);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +/// Detect the occurrences of calls to `iter` or `into_iter` for the
 +/// given identifier
 +fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
 +    block: &'tcx Block<'tcx>,
 +    id: HirId,
 +    cx: &'a LateContext<'tcx>,
 +    captured_ids: HirIdSet,
 +) -> Option<Vec<IterFunction>> {
 +    let mut visitor = IterFunctionVisitor {
 +        uses: Vec::new(),
 +        target: id,
 +        seen_other: false,
 +        cx,
 +        current_mutably_captured_ids: HirIdSet::default(),
 +        illegal_mutable_capture_ids: captured_ids,
 +        hir_id_uses_map: FxHashMap::default(),
 +        current_statement_hir_id: None,
 +    };
 +    visitor.visit_block(block);
 +    if visitor.seen_other {
 +        None
 +    } else {
 +        Some(visitor.uses.into_iter().flatten().collect())
 +    }
 +}
 +
 +fn get_captured_ids(cx: &LateContext<'_>, ty: Ty<'_>) -> HirIdSet {
 +    fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: Ty<'_>, set: &mut HirIdSet) {
 +        match ty.kind() {
 +            ty::Adt(_, generics) => {
 +                for generic in *generics {
 +                    if let GenericArgKind::Type(ty) = generic.unpack() {
 +                        get_captured_ids_recursive(cx, ty, set);
 +                    }
 +                }
 +            },
 +            ty::Closure(def_id, _) => {
 +                let closure_hir_node = cx.tcx.hir().get_if_local(*def_id).unwrap();
 +                if let Node::Expr(closure_expr) = closure_hir_node {
 +                    can_move_expr_to_closure(cx, closure_expr)
 +                        .unwrap()
 +                        .into_iter()
 +                        .for_each(|(hir_id, capture_kind)| {
 +                            if matches!(capture_kind, CaptureKind::Ref(Mutability::Mut)) {
 +                                set.insert(hir_id);
 +                            }
 +                        });
 +                }
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    let mut set = HirIdSet::default();
 +
 +    get_captured_ids_recursive(cx, ty, &mut set);
 +
 +    set
 +}
index a0ca7e6ff1e22b78f2cd4c08a8ca29c6cea71e8c,0000000000000000000000000000000000000000..2502c8f880ddce76aa1045cfcdc815aaa315209d
mode 100644,000000..100644
--- /dev/null
@@@ -1,202 -1,0 +1,202 @@@
-             Some((sugg, "".into()))
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::match_function_call;
 +use clippy_utils::paths::FUTURE_FROM_GENERATOR;
 +use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::{
 +    AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
 +    HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// It checks for manual implementations of `async` functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's more idiomatic to use the dedicated syntax.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// use std::future::Future;
 +    ///
 +    /// fn foo() -> impl Future<Output = i32> { async { 42 } }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// async fn foo() -> i32 { 42 }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MANUAL_ASYNC_FN,
 +    style,
 +    "manual implementations of `async` functions can be simplified using the dedicated syntax"
 +}
 +
 +declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        decl: &'tcx FnDecl<'_>,
 +        body: &'tcx Body<'_>,
 +        span: Span,
 +        _: HirId,
 +    ) {
 +        if_chain! {
 +            if let Some(header) = kind.header();
 +            if header.asyncness == IsAsync::NotAsync;
 +            // Check that this function returns `impl Future`
 +            if let FnRetTy::Return(ret_ty) = decl.output;
 +            if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty);
 +            if let Some(output) = future_output_ty(trait_ref);
 +            if captures_all_lifetimes(decl.inputs, &output_lifetimes);
 +            // Check that the body of the function consists of one async block
 +            if let ExprKind::Block(block, _) = body.value.kind;
 +            if block.stmts.is_empty();
 +            if let Some(closure_body) = desugared_async_block(cx, block);
 +            then {
 +                let header_span = span.with_hi(ret_ty.span.hi());
 +
 +                span_lint_and_then(
 +                    cx,
 +                    MANUAL_ASYNC_FN,
 +                    header_span,
 +                    "this function can be simplified using the `async fn` syntax",
 +                    |diag| {
 +                        if_chain! {
 +                            if let Some(header_snip) = snippet_opt(cx, header_span);
 +                            if let Some(ret_pos) = position_before_rarrow(&header_snip);
 +                            if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
 +                            then {
 +                                let help = format!("make the function `async` and {}", ret_sugg);
 +                                diag.span_suggestion(
 +                                    header_span,
 +                                    &help,
 +                                    format!("async {}{}", &header_snip[..ret_pos], ret_snip),
 +                                    Applicability::MachineApplicable
 +                                );
 +
 +                                let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
 +                                diag.span_suggestion(
 +                                    block.span,
 +                                    "move the body of the async block to the enclosing function",
 +                                    body_snip,
 +                                    Applicability::MachineApplicable
 +                                );
 +                            }
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn future_trait_ref<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: &'tcx Ty<'tcx>,
 +) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
 +    if_chain! {
 +        if let TyKind::OpaqueDef(item_id, bounds) = ty.kind;
 +        let item = cx.tcx.hir().item(item_id);
 +        if let ItemKind::OpaqueTy(opaque) = &item.kind;
 +        if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
 +            if let GenericBound::Trait(poly, _) = bound {
 +                Some(&poly.trait_ref)
 +            } else {
 +                None
 +            }
 +        });
 +        if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait();
 +        then {
 +            let output_lifetimes = bounds
 +                .iter()
 +                .filter_map(|bound| {
 +                    if let GenericArg::Lifetime(lt) = bound {
 +                        Some(lt.name)
 +                    } else {
 +                        None
 +                    }
 +                })
 +                .collect();
 +
 +            return Some((trait_ref, output_lifetimes));
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
 +    if_chain! {
 +        if let Some(segment) = trait_ref.path.segments.last();
 +        if let Some(args) = segment.args;
 +        if args.bindings.len() == 1;
 +        let binding = &args.bindings[0];
 +        if binding.ident.name == sym::Output;
 +        if let TypeBindingKind::Equality{term: Term::Ty(output)} = binding.kind;
 +        then {
 +            return Some(output)
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName]) -> bool {
 +    let input_lifetimes: Vec<LifetimeName> = inputs
 +        .iter()
 +        .filter_map(|ty| {
 +            if let TyKind::Rptr(lt, _) = ty.kind {
 +                Some(lt.name)
 +            } else {
 +                None
 +            }
 +        })
 +        .collect();
 +
 +    // The lint should trigger in one of these cases:
 +    // - There are no input lifetimes
 +    // - There's only one output lifetime bound using `+ '_`
 +    // - All input lifetimes are explicitly bound to the output
 +    input_lifetimes.is_empty()
 +        || (output_lifetimes.len() == 1 && matches!(output_lifetimes[0], LifetimeName::Infer))
 +        || input_lifetimes
 +            .iter()
 +            .all(|in_lt| output_lifetimes.iter().any(|out_lt| in_lt == out_lt))
 +}
 +
 +fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
 +    if_chain! {
 +        if let Some(block_expr) = block.expr;
 +        if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
 +        if args.len() == 1;
 +        if let Expr{kind: ExprKind::Closure(&Closure { body, .. }), ..} = args[0];
 +        let closure_body = cx.tcx.hir().body(body);
 +        if closure_body.generator_kind == Some(GeneratorKind::Async(AsyncGeneratorKind::Block));
 +        then {
 +            return Some(closure_body);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, String)> {
 +    match output.kind {
 +        TyKind::Tup(tys) if tys.is_empty() => {
 +            let sugg = "remove the return type";
++            Some((sugg, String::new()))
 +        },
 +        _ => {
 +            let sugg = "return the output of the future directly";
 +            snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {}", snip)))
 +        },
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a90eaa8fdcbe35b200216f180c5675be3d5ff6d0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,140 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use rustc_ast::LitKind;
++use rustc_errors::Applicability::MachineApplicable;
++use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
++use rustc_lint::{LateContext, LateLintPass};
++use rustc_middle::ty;
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++use rustc_span::{sym, symbol, Span};
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
++    /// `String::from("")` and others.
++    ///
++    /// ### Why is this bad?
++    ///
++    /// Different ways of creating an empty string makes your code less standardized, which can
++    /// be confusing.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let a = "".to_string();
++    /// let b: String = "".into();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let a = String::new();
++    /// let b = String::new();
++    /// ```
++    #[clippy::version = "1.65.0"]
++    pub MANUAL_STRING_NEW,
++    pedantic,
++    "empty String is being created manually"
++}
++declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]);
++
++impl LateLintPass<'_> for ManualStringNew {
++    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
++        if expr.span.from_expansion() {
++            return;
++        }
++
++        let ty = cx.typeck_results().expr_ty(expr);
++        match ty.kind() {
++            ty::Adt(adt_def, _) if adt_def.is_struct() => {
++                if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
++                    return;
++                }
++            },
++            _ => return,
++        }
++
++        match expr.kind {
++            ExprKind::Call(func, args) => {
++                parse_call(cx, expr.span, func, args);
++            },
++            ExprKind::MethodCall(path_segment, args, _) => {
++                parse_method_call(cx, expr.span, path_segment, args);
++            },
++            _ => (),
++        }
++    }
++}
++
++/// Checks if an expression's kind corresponds to an empty &str.
++fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
++    if  let ExprKind::Lit(lit) = expr_kind &&
++        let LitKind::Str(value, _) = lit.node &&
++        value == symbol::kw::Empty
++    {
++        return true;
++    }
++
++    false
++}
++
++fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
++    span_lint_and_sugg(
++        cx,
++        MANUAL_STRING_NEW,
++        span,
++        "empty String is being created manually",
++        "consider using",
++        "String::new()".into(),
++        MachineApplicable,
++    );
++}
++
++/// Tries to parse an expression as a method call, emitting the warning if necessary.
++fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
++    if args.is_empty() {
++        // When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
++        return;
++    }
++
++    let ident = path_segment.ident.as_str();
++    let method_arg_kind = &args[0].kind;
++    if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
++        warn_then_suggest(cx, span);
++    } else if let ExprKind::Call(func, args) = method_arg_kind {
++        // If our first argument is a function call itself, it could be an `unwrap`-like function.
++        // E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
++        parse_call(cx, span, func, args);
++    }
++}
++
++/// Tries to parse an expression as a function call, emitting the warning if necessary.
++fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
++    if args.len() != 1 {
++        return;
++    }
++
++    let arg_kind = &args[0].kind;
++    if let ExprKind::Path(qpath) = &func.kind {
++        if let QPath::TypeRelative(_, _) = qpath {
++            // String::from(...) or String::try_from(...)
++            if  let QPath::TypeRelative(ty, path_seg) = qpath &&
++                [sym::from, sym::try_from].contains(&path_seg.ident.name) &&
++                let TyKind::Path(qpath) = &ty.kind &&
++                let QPath::Resolved(_, path) = qpath &&
++                let [path_seg] = path.segments &&
++                path_seg.ident.name == sym::String &&
++                is_expr_kind_empty_str(arg_kind)
++            {
++                warn_then_suggest(cx, span);
++            }
++        } else if let QPath::Resolved(_, path) = qpath {
++            // From::from(...) or TryFrom::try_from(...)
++            if  let [path_seg1, path_seg2] = path.segments &&
++                is_expr_kind_empty_str(arg_kind) && (
++                    (path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
++                    (path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
++                )
++            {
++                warn_then_suggest(cx, span);
++            }
++        }
++    }
++}
index 0da4833f1dfe0c9dfc5694874ab0cad257c30487,0000000000000000000000000000000000000000..34cc082687ec20426d6f36ae4c1aa11a4530458a
mode 100644,000000..100644
--- /dev/null
@@@ -1,171 -1,0 +1,173 @@@
- use rustc_lint::LateContext;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_wild;
 +use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::span_contains_comment;
 +use rustc_ast::{Attribute, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
++use rustc_lint::{LateContext, LintContext};
 +use rustc_middle::ty;
 +use rustc_span::source_map::Spanned;
 +
 +use super::MATCH_LIKE_MATCHES_MACRO;
 +
 +/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
 +pub(crate) fn check_if_let<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    let_pat: &'tcx Pat<'_>,
 +    let_expr: &'tcx Expr<'_>,
 +    then_expr: &'tcx Expr<'_>,
 +    else_expr: &'tcx Expr<'_>,
 +) {
 +    find_matches_sugg(
 +        cx,
 +        let_expr,
 +        IntoIterator::into_iter([
 +            (&[][..], Some(let_pat), then_expr, None),
 +            (&[][..], None, else_expr, None),
 +        ]),
 +        expr,
 +        true,
 +    );
 +}
 +
 +pub(super) fn check_match<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    scrutinee: &'tcx Expr<'_>,
 +    arms: &'tcx [Arm<'tcx>],
 +) -> bool {
 +    find_matches_sugg(
 +        cx,
 +        scrutinee,
 +        arms.iter().map(|arm| {
 +            (
 +                cx.tcx.hir().attrs(arm.hir_id),
 +                Some(arm.pat),
 +                arm.body,
 +                arm.guard.as_ref(),
 +            )
 +        }),
 +        e,
 +        false,
 +    )
 +}
 +
 +/// Lint a `match` or `if let` for replacement by `matches!`
 +fn find_matches_sugg<'a, 'b, I>(
 +    cx: &LateContext<'_>,
 +    ex: &Expr<'_>,
 +    mut iter: I,
 +    expr: &Expr<'_>,
 +    is_if_let: bool,
 +) -> bool
 +where
 +    'b: 'a,
 +    I: Clone
 +        + DoubleEndedIterator
 +        + ExactSizeIterator
 +        + Iterator<
 +            Item = (
 +                &'a [Attribute],
 +                Option<&'a Pat<'b>>,
 +                &'a Expr<'b>,
 +                Option<&'a Guard<'b>>,
 +            ),
 +        >,
 +{
 +    if_chain! {
++        if !span_contains_comment(cx.sess().source_map(), expr.span);
 +        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);
 +        if let Some(b1) = find_bool_lit(&last_expr.kind);
 +        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).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<'_>) -> Option<bool> {
 +    match ex {
 +        ExprKind::Lit(Spanned {
 +            node: LitKind::Bool(b), ..
 +        }) => Some(*b),
 +        ExprKind::Block(
 +            rustc_hir::Block {
 +                stmts: &[],
 +                expr: Some(exp),
 +                ..
 +            },
 +            _,
 +        ) => {
 +            if let ExprKind::Lit(Spanned {
 +                node: LitKind::Bool(b), ..
 +            }) = exp.kind
 +            {
 +                Some(b)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
index fa19cddd35ec7afff4258f68eb901967e01b411b,0000000000000000000000000000000000000000..6f037339ec75870552a3d2867f447a012364d143
mode 100644,000000..100644
--- /dev/null
@@@ -1,207 -1,0 +1,225 @@@
- use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
 +use super::NEEDLESS_MATCH;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
 +use clippy_utils::{
 +    eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
 +    peel_blocks_with_stmt,
 +};
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::OptionNone;
-             return eq_expr_value(cx, match_expr, strip_return(arm_expr));
++use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +use rustc_typeck::hir_ty_to_ty;
 +
 +pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
 +    if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            NEEDLESS_MATCH,
 +            expr.span,
 +            "this match expression is unnecessary",
 +            "replace it with",
 +            snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
 +            applicability,
 +        );
 +    }
 +}
 +
 +/// Check for nop `if let` expression that assembled as unnecessary match
 +///
 +/// ```rust,ignore
 +/// if let Some(a) = option {
 +///     Some(a)
 +/// } else {
 +///     None
 +/// }
 +/// ```
 +/// OR
 +/// ```rust,ignore
 +/// if let SomeEnum::A = some_enum {
 +///     SomeEnum::A
 +/// } else if let SomeEnum::B = some_enum {
 +///     SomeEnum::B
 +/// } else {
 +///     some_enum
 +/// }
 +/// ```
 +pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let: &higher::IfLet<'tcx>) {
 +    if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let_inner(cx, if_let) {
 +        let mut applicability = Applicability::MachineApplicable;
 +        span_lint_and_sugg(
 +            cx,
 +            NEEDLESS_MATCH,
 +            ex.span,
 +            "this if-let expression is unnecessary",
 +            "replace it with",
 +            snippet_with_applicability(cx, if_let.let_expr.span, "..", &mut applicability).to_string(),
 +            applicability,
 +        );
 +    }
 +}
 +
 +fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
 +    for arm in arms {
 +        let arm_expr = peel_blocks_with_stmt(arm.body);
++
++        if let Some(guard_expr) = &arm.guard {
++            match guard_expr {
++                // gives up if `pat if expr` can have side effects
++                Guard::If(if_cond) => {
++                    if if_cond.can_have_side_effects() {
++                        return false;
++                    }
++                },
++                // gives up `pat if let ...` arm
++                Guard::IfLet(_) => {
++                    return false;
++                },
++            };
++        }
++
 +        if let PatKind::Wild = arm.pat.kind {
++            if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
++                return false;
++            }
 +        } else if !pat_same_as_expr(arm.pat, arm_expr) {
 +            return false;
 +        }
 +    }
 +
 +    true
 +}
 +
 +fn check_if_let_inner(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
 +    if let Some(if_else) = if_let.if_else {
 +        if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
 +            return false;
 +        }
 +
 +        // Recursively check for each `else if let` phrase,
 +        if let Some(ref nested_if_let) = higher::IfLet::hir(cx, if_else) {
 +            return check_if_let_inner(cx, nested_if_let);
 +        }
 +
 +        if matches!(if_else.kind, ExprKind::Block(..)) {
 +            let else_expr = peel_blocks_with_stmt(if_else);
 +            if matches!(else_expr.kind, ExprKind::Block(..)) {
 +                return false;
 +            }
 +            let ret = strip_return(else_expr);
 +            let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
 +            if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
 +                if let ExprKind::Path(ref qpath) = ret.kind {
 +                    return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
 +                }
 +                return false;
 +            }
 +            return eq_expr_value(cx, if_let.let_expr, ret);
 +        }
 +    }
 +
 +    false
 +}
 +
 +/// Strip `return` keyword if the expression type is `ExprKind::Ret`.
 +fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    if let ExprKind::Ret(Some(ret)) = expr.kind {
 +        ret
 +    } else {
 +        expr
 +    }
 +}
 +
 +/// Manually check for coercion casting by checking if the type of the match operand or let expr
 +/// differs with the assigned local variable or the function return type.
 +fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
 +    if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
 +        match p_node {
 +            // Compare match_expr ty with local in `let local = match match_expr {..}`
 +            Node::Local(local) => {
 +                let results = cx.typeck_results();
 +                return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
 +            },
 +            // compare match_expr ty with RetTy in `fn foo() -> RetTy`
 +            Node::Item(..) => {
 +                if let Some(fn_decl) = p_node.fn_decl() {
 +                    if let FnRetTy::Return(ret_ty) = fn_decl.output {
 +                        return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr));
 +                    }
 +                }
 +            },
 +            // check the parent expr for this whole block `{ match match_expr {..} }`
 +            Node::Block(block) => {
 +                if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) {
 +                    return expr_ty_matches_p_ty(cx, expr, block_parent_expr);
 +                }
 +            },
 +            // recursively call on `if xxx {..}` etc.
 +            Node::Expr(p_expr) => {
 +                return expr_ty_matches_p_ty(cx, expr, p_expr);
 +            },
 +            _ => {},
 +        }
 +    }
 +    false
 +}
 +
 +fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
 +    let expr = strip_return(expr);
 +    match (&pat.kind, &expr.kind) {
 +        // Example: `Some(val) => Some(val)`
 +        (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => {
 +            if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
 +                return over(path.segments, call_path.segments, |pat_seg, call_seg| {
 +                    pat_seg.ident.name == call_seg.ident.name
 +                }) && same_non_ref_symbols(tuple_params, call_params);
 +            }
 +        },
 +        // Example: `val => val`
 +        (
 +            PatKind::Binding(annot, _, pat_ident, _),
 +            ExprKind::Path(QPath::Resolved(
 +                _,
 +                Path {
 +                    segments: [first_seg, ..],
 +                    ..
 +                },
 +            )),
 +        ) => {
 +            return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut)
 +                && pat_ident.name == first_seg.ident.name;
 +        },
 +        // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
 +        (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
 +            return over(p_path.segments, e_path.segments, |p_seg, e_seg| {
 +                p_seg.ident.name == e_seg.ident.name
 +            });
 +        },
 +        // Example: `5 => 5`
 +        (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
 +            if let ExprKind::Lit(pat_spanned) = &pat_lit_expr.kind {
 +                return pat_spanned.node == expr_spanned.node;
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    false
 +}
 +
 +fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool {
 +    if pats.len() != exprs.len() {
 +        return false;
 +    }
 +
 +    for i in 0..pats.len() {
 +        if !pat_same_as_expr(&pats[i], &exprs[i]) {
 +            return false;
 +        }
 +    }
 +
 +    true
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a7c63d76f72c8afee58a465bec5ba33a21e5436
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,70 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::match_type;
++use clippy_utils::visitors::is_local_used;
++use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, UintTy};
++use rustc_span::sym;
++
++use super::NAIVE_BYTECOUNT;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    filter_recv: &'tcx Expr<'_>,
++    filter_arg: &'tcx Expr<'_>,
++) {
++    if_chain! {
++        if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
++        let body = cx.tcx.hir().body(body);
++        if let [param] = body.params;
++        if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
++        if let ExprKind::Binary(ref op, l, r) = body.value.kind;
++        if op.node == BinOpKind::Eq;
++        if match_type(cx,
++                    cx.typeck_results().expr_ty(filter_recv).peel_refs(),
++                    &paths::SLICE_ITER);
++        let operand_is_arg = |expr| {
++            let expr = peel_ref_operators(cx, peel_blocks(expr));
++            path_to_local_id(expr, arg_id)
++        };
++        let needle = if operand_is_arg(l) {
++            r
++        } else if operand_is_arg(r) {
++            l
++        } else {
++            return;
++        };
++        if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
++        if !is_local_used(cx, needle, arg_id);
++        then {
++            let haystack = if let ExprKind::MethodCall(path, args, _) =
++                    filter_recv.kind {
++                let p = path.ident.name;
++                if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
++                    &args[0]
++                } else {
++                    filter_recv
++                }
++            } else {
++                filter_recv
++            };
++            let mut applicability = Applicability::MaybeIncorrect;
++            span_lint_and_sugg(
++                cx,
++                NAIVE_BYTECOUNT,
++                expr.span,
++                "you appear to be counting bytes the naive way",
++                "consider using the bytecount crate",
++                format!("bytecount::count({}, {})",
++                        snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
++                        snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
++                applicability,
++            );
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fcfc25b523dac4d6dc16ff284fea4fe55e4d2e2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::is_type_diagnostic_item;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::BYTES_COUNT_TO_LEN;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    count_recv: &'tcx hir::Expr<'_>,
++    bytes_recv: &'tcx hir::Expr<'_>,
++) {
++    if_chain! {
++        if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
++        if cx.tcx.type_of(impl_id).is_str();
++        let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
++        if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
++        then {
++            let mut applicability = Applicability::MachineApplicable;
++            span_lint_and_sugg(
++                cx,
++                BYTES_COUNT_TO_LEN,
++                expr.span,
++                "using long and hard to read `.bytes().count()`",
++                "consider calling `.len()` instead",
++                format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
++                applicability
++            );
++        }
++    };
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b3c2c7c9a2dcc3a70cac63fe2363ecf238a1d514
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,41 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::ty::is_type_diagnostic_item;
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_span::{source_map::Spanned, symbol::sym, Span};
++
++use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    call_span: Span,
++    recv: &'tcx Expr<'_>,
++    arg: &'tcx Expr<'_>,
++) {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if cx.tcx.type_of(impl_id).is_str();
++        if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
++        if (2..=6).contains(&ext_literal.as_str().len());
++        let ext_str = ext_literal.as_str();
++        if ext_str.starts_with('.');
++        if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
++            || ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
++        let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
++        if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
++        then {
++            span_lint_and_help(
++                cx,
++                CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
++                call_span,
++                "case-sensitive file extension comparison",
++                None,
++                "consider using a case-insensitive comparison instead",
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..561033be5b6af3945bfadf83c6903a56b7f486c9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,96 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::visitors::for_each_expr;
++use clippy_utils::{eq_expr_value, get_parent_expr};
++use core::ops::ControlFlow;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use std::collections::VecDeque;
++
++use super::method_call;
++use super::COLLAPSIBLE_STR_REPLACE;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx hir::Expr<'tcx>,
++    from: &'tcx hir::Expr<'tcx>,
++    to: &'tcx hir::Expr<'tcx>,
++) {
++    let replace_methods = collect_replace_calls(cx, expr, to);
++    if replace_methods.methods.len() > 1 {
++        let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
++        // If the parent node's `to` argument is the same as the `to` argument
++        // of the last replace call in the current chain, don't lint as it was already linted
++        if let Some(parent) = get_parent_expr(cx, expr)
++            && let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
++            && eq_expr_value(cx, to, current_to)
++            && from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
++        {
++            return;
++        }
++
++        check_consecutive_replace_calls(cx, expr, &replace_methods, to);
++    }
++}
++
++struct ReplaceMethods<'tcx> {
++    methods: VecDeque<&'tcx hir::Expr<'tcx>>,
++    from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
++}
++
++fn collect_replace_calls<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx hir::Expr<'tcx>,
++    to_arg: &'tcx hir::Expr<'tcx>,
++) -> ReplaceMethods<'tcx> {
++    let mut methods = VecDeque::new();
++    let mut from_args = VecDeque::new();
++
++    let _: Option<()> = for_each_expr(expr, |e| {
++        if let Some(("replace", [_, from, to], _)) = method_call(e) {
++            if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
++                methods.push_front(e);
++                from_args.push_front(from);
++                ControlFlow::Continue(())
++            } else {
++                ControlFlow::BREAK
++            }
++        } else {
++            ControlFlow::Continue(())
++        }
++    });
++
++    ReplaceMethods { methods, from_args }
++}
++
++/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
++fn check_consecutive_replace_calls<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx hir::Expr<'tcx>,
++    replace_methods: &ReplaceMethods<'tcx>,
++    to_arg: &'tcx hir::Expr<'tcx>,
++) {
++    let from_args = &replace_methods.from_args;
++    let from_arg_reprs: Vec<String> = from_args
++        .iter()
++        .map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
++        .collect();
++    let app = Applicability::MachineApplicable;
++    let earliest_replace_call = replace_methods.methods.front().unwrap();
++    if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
++        span_lint_and_sugg(
++            cx,
++            COLLAPSIBLE_STR_REPLACE,
++            expr.span.with_lo(span_lo.lo()),
++            "used consecutive `str::replace` call",
++            "replace with",
++            format!(
++                "replace([{}], {})",
++                from_arg_reprs.join(", "),
++                snippet(cx, to_arg.span, ".."),
++            ),
++            app,
++        );
++    }
++}
index 5ef08ca6290bae6adbc15a6887edbd755c040711,0000000000000000000000000000000000000000..d59fefa1ddc0ebf68104498532f8da1f1c8c0e26
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,44 @@@
- /// lint use of `expect()` for `Option`s and `Result`s
- pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::is_in_test_function;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::EXPECT_USED;
 +
-     let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
++/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
++pub(super) fn check(
++    cx: &LateContext<'_>,
++    expr: &hir::Expr<'_>,
++    recv: &hir::Expr<'_>,
++    is_err: bool,
++    allow_expect_in_tests: bool,
++) {
 +    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 +
-         Some((EXPECT_USED, "a Result", "Err", "an "))
++    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
 +        Some((EXPECT_USED, "an Option", "None", ""))
 +    } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-             &format!("used `expect()` on `{kind}` value"),
++        Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
 +    } else {
 +        None
 +    };
 +
++    let method = if is_err { "expect_err" } else { "expect" };
++
 +    if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
 +        return;
 +    }
 +
 +    if let Some((lint, kind, none_value, none_prefix)) = mess {
 +        span_lint_and_help(
 +            cx,
 +            lint,
 +            expr.span,
++            &format!("used `{method}()` on `{kind}` value"),
 +            None,
 +            &format!("if this value is {none_prefix}`{none_value}`, it will panic"),
 +        );
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4de77de74042171a65e7092def7c30cad1895558
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,39 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::is_slice_of_primitives;
++use clippy_utils::source::snippet_with_applicability;
++use if_chain::if_chain;
++use rustc_ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_span::source_map::Spanned;
++
++use super::GET_FIRST;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx hir::Expr<'_>,
++    recv: &'tcx hir::Expr<'_>,
++    arg: &'tcx hir::Expr<'_>,
++) {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if cx.tcx.type_of(impl_id).is_slice();
++        if let Some(_) = is_slice_of_primitives(cx, recv);
++        if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
++        then {
++            let mut app = Applicability::MachineApplicable;
++            let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
++            span_lint_and_sugg(
++                cx,
++                GET_FIRST,
++                expr.span,
++                &format!("accessing first element with `{0}.get(0)`", slice_name),
++                "try",
++                format!("{}.first()", slice_name),
++                app,
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cea7b0d82ff3f0f071161c94bacd1af86e51a59c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,107 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
++
++use rustc_errors::Applicability;
++use rustc_hir::LangItem::{OptionNone, OptionSome};
++use rustc_hir::{Expr, ExprKind, Node};
++use rustc_lint::LateContext;
++
++use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
++
++enum IterType {
++    Iter,
++    IterMut,
++    IntoIter,
++}
++
++impl IterType {
++    fn ref_prefix(&self) -> &'static str {
++        match self {
++            Self::Iter => "&",
++            Self::IterMut => "&mut ",
++            Self::IntoIter => "",
++        }
++    }
++}
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
++    let item = match &recv.kind {
++        ExprKind::Array(v) if v.len() <= 1 => v.first(),
++        ExprKind::Path(p) => {
++            if is_lang_ctor(cx, p, OptionNone) {
++                None
++            } else {
++                return;
++            }
++        },
++        ExprKind::Call(f, some_args) if some_args.len() == 1 => {
++            if let ExprKind::Path(p) = &f.kind {
++                if is_lang_ctor(cx, p, OptionSome) {
++                    Some(&some_args[0])
++                } else {
++                    return;
++                }
++            } else {
++                return;
++            }
++        },
++        _ => return,
++    };
++    let iter_type = match method_name {
++        "iter" => IterType::Iter,
++        "iter_mut" => IterType::IterMut,
++        "into_iter" => IterType::IntoIter,
++        _ => return,
++    };
++
++    let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
++        Some((Node::Expr(parent), child_id)) => match parent.kind {
++            ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
++            ExprKind::If(_, _, _)
++            | ExprKind::Match(_, _, _)
++            | ExprKind::Closure(_)
++            | ExprKind::Ret(_)
++            | ExprKind::Break(_, _) => true,
++            _ => false,
++        },
++        Some((Node::Stmt(_) | Node::Local(_), _)) => false,
++        _ => true,
++    };
++
++    if is_unified {
++        return;
++    }
++
++    if let Some(i) = item {
++        let sugg = format!(
++            "{}::iter::once({}{})",
++            if is_no_std_crate(cx) { "core" } else { "std" },
++            iter_type.ref_prefix(),
++            snippet(cx, i.span, "...")
++        );
++        span_lint_and_sugg(
++            cx,
++            ITER_ON_SINGLE_ITEMS,
++            expr.span,
++            &format!("`{method_name}` call on a collection with only one item"),
++            "try",
++            sugg,
++            Applicability::MaybeIncorrect,
++        );
++    } else {
++        span_lint_and_sugg(
++            cx,
++            ITER_ON_EMPTY_COLLECTIONS,
++            expr.span,
++            &format!("`{method_name}` call on an empty collection"),
++            "try",
++            if is_no_std_crate(cx) {
++                "core::iter::empty()".to_string()
++            } else {
++                "std::iter::empty()".to_string()
++            },
++            Applicability::MaybeIncorrect,
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffd2f4a38b8ac1ac92ec391abca7716e811ebe7b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,64 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
++use clippy_utils::ty::is_type_diagnostic_item;
++use clippy_utils::{is_lang_ctor, path_to_local_id};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::LangItem::{ResultErr, ResultOk};
++use rustc_hir::{Closure, Expr, ExprKind, PatKind};
++use rustc_lint::LateContext;
++use rustc_span::symbol::sym;
++
++use super::MANUAL_OK_OR;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'tcx>,
++    recv: &'tcx Expr<'_>,
++    or_expr: &'tcx Expr<'_>,
++    map_expr: &'tcx Expr<'_>,
++) {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
++        if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
++        if is_lang_ctor(cx, err_path, ResultErr);
++        if is_ok_wrapping(cx, map_expr);
++        if let Some(recv_snippet) = snippet_opt(cx, recv.span);
++        if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
++        if let Some(indent) = indent_of(cx, expr.span);
++        then {
++            let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
++            span_lint_and_sugg(
++                cx,
++                MANUAL_OK_OR,
++                expr.span,
++                "this pattern reimplements `Option::ok_or`",
++                "replace with",
++                format!(
++                    "{}.ok_or({})",
++                    recv_snippet,
++                    reindented_err_arg_snippet
++                ),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
++
++fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
++    if let ExprKind::Path(ref qpath) = map_expr.kind {
++        if is_lang_ctor(cx, qpath, ResultOk) {
++            return true;
++        }
++    }
++    if_chain! {
++        if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
++        let body = cx.tcx.hir().body(body);
++        if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
++        if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
++        if is_lang_ctor(cx, ok_path, ResultOk);
++        then { path_to_local_id(ok_arg, param_id) } else { false }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ffedda95ff8e57870170c940bebf4933f107e3e4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,122 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet_with_applicability;
++use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
++use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_middle::mir::Mutability;
++use rustc_middle::ty;
++use rustc_middle::ty::adjustment::Adjust;
++use rustc_semver::RustcVersion;
++use rustc_span::symbol::Ident;
++use rustc_span::{sym, Span};
++
++use super::MAP_CLONE;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'_>,
++    e: &hir::Expr<'_>,
++    recv: &hir::Expr<'_>,
++    arg: &'tcx hir::Expr<'_>,
++    msrv: Option<RustcVersion>,
++) {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
++        if cx.tcx.impl_of_method(method_id)
++            .map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
++            || is_diag_trait_item(cx, method_id, sym::Iterator);
++        if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
++        then {
++            let closure_body = cx.tcx.hir().body(body);
++            let closure_expr = peel_blocks(&closure_body.value);
++            match closure_body.params[0].pat.kind {
++                hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
++                    hir::BindingAnnotation::Unannotated, .., name, None
++                ) = inner.kind {
++                    if ident_eq(name, closure_expr) {
++                        lint_explicit_closure(cx, e.span, recv.span, true, msrv);
++                    }
++                },
++                hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
++                    match closure_expr.kind {
++                        hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
++                            if ident_eq(name, inner) {
++                                if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
++                                    lint_explicit_closure(cx, e.span, recv.span, true, msrv);
++                                }
++                            }
++                        },
++                        hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
++                            if ident_eq(name, obj) && method.ident.name == sym::clone;
++                            if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
++                            if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
++                            if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
++                            // no autoderefs
++                            if !cx.typeck_results().expr_adjustments(obj).iter()
++                                .any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
++                            then {
++                                let obj_ty = cx.typeck_results().expr_ty(obj);
++                                if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
++                                    if matches!(mutability, Mutability::Not) {
++                                        let copy = is_copy(cx, *ty);
++                                        lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
++                                    }
++                                } else {
++                                    lint_needless_cloning(cx, e.span, recv.span);
++                                }
++                            }
++                        },
++                        _ => {},
++                    }
++                },
++                _ => {},
++            }
++        }
++    }
++}
++
++fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
++    if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
++        path.segments.len() == 1 && path.segments[0].ident == name
++    } else {
++        false
++    }
++}
++
++fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
++    span_lint_and_sugg(
++        cx,
++        MAP_CLONE,
++        root.trim_start(receiver).unwrap(),
++        "you are needlessly cloning iterator elements",
++        "remove the `map` call",
++        String::new(),
++        Applicability::MachineApplicable,
++    );
++}
++
++fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
++    let mut applicability = Applicability::MachineApplicable;
++
++    let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
++        ("you are using an explicit closure for copying elements", "copied")
++    } else {
++        ("you are using an explicit closure for cloning elements", "cloned")
++    };
++
++    span_lint_and_sugg(
++        cx,
++        MAP_CLONE,
++        replace,
++        message,
++        &format!("consider calling the dedicated `{}` method", sugg_method),
++        format!(
++            "{}.{}()",
++            snippet_with_applicability(cx, root, "..", &mut applicability),
++            sugg_method,
++        ),
++        applicability,
++    );
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fb6617145e718bb0a9f116f955a3c5a73a30f09
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::ty::is_type_diagnostic_item;
++use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::MAP_ERR_IGNORE;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
++    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
++        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
++        && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
++        && let ExprKind::Closure(&Closure {
++            capture_clause: CaptureBy::Ref,
++            body,
++            fn_decl_span,
++            ..
++        }) = arg.kind
++        && let closure_body = cx.tcx.hir().body(body)
++        && let [param] = closure_body.params
++        && let PatKind::Wild = param.pat.kind
++    {
++        // span the area of the closure capture and warn that the
++        // original error will be thrown away
++        span_lint_and_help(
++            cx,
++            MAP_ERR_IGNORE,
++            fn_decl_span,
++            "`map_err(|_|...` wildcard pattern discards the original error",
++            None,
++            "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
++        );
++    }
++}
index 5ac6b09f0aa27bff22fa87c05292791f0a13f1b0,0000000000000000000000000000000000000000..a0d190a58aff9a7f10478600f0d8ddab1b3b2840
mode 100644,000000..100644
--- /dev/null
@@@ -1,3063 -1,0 +1,3888 @@@
- use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
- use clippy_utils::{contains_return, get_trait_def_id, iter_input_pats, meets_msrv, msrvs, paths, return_ty};
 +mod bind_instead_of_map;
++mod bytecount;
++mod bytes_count_to_len;
 +mod bytes_nth;
++mod case_sensitive_file_extension_comparisons;
 +mod chars_cmp;
 +mod chars_cmp_with_unwrap;
 +mod chars_last_cmp;
 +mod chars_last_cmp_with_unwrap;
 +mod chars_next_cmp;
 +mod chars_next_cmp_with_unwrap;
 +mod clone_on_copy;
 +mod clone_on_ref_ptr;
 +mod cloned_instead_of_copied;
++mod collapsible_str_replace;
 +mod err_expect;
 +mod expect_fun_call;
 +mod expect_used;
 +mod extend_with_drain;
 +mod filetype_is_file;
 +mod filter_map;
 +mod filter_map_identity;
 +mod filter_map_next;
 +mod filter_next;
 +mod flat_map_identity;
 +mod flat_map_option;
 +mod from_iter_instead_of_collect;
++mod get_first;
 +mod get_last_with_len;
 +mod get_unwrap;
 +mod implicit_clone;
 +mod inefficient_to_string;
 +mod inspect_for_each;
 +mod into_iter_on_ref;
 +mod is_digit_ascii_radix;
 +mod iter_cloned_collect;
 +mod iter_count;
 +mod iter_next_slice;
 +mod iter_nth;
 +mod iter_nth_zero;
++mod iter_on_single_or_empty_collections;
 +mod iter_overeager_cloned;
 +mod iter_skip_next;
 +mod iter_with_drain;
 +mod iterator_step_by_zero;
++mod manual_ok_or;
 +mod manual_saturating_arithmetic;
 +mod manual_str_repeat;
++mod map_clone;
 +mod map_collect_result_unit;
++mod map_err_ignore;
 +mod map_flatten;
 +mod map_identity;
 +mod map_unwrap_or;
++mod mut_mutex_lock;
 +mod needless_option_as_deref;
 +mod needless_option_take;
 +mod no_effect_replace;
 +mod obfuscated_if_else;
 +mod ok_expect;
++mod open_options;
 +mod option_as_ref_deref;
 +mod option_map_or_none;
 +mod option_map_unwrap_or;
 +mod or_fun_call;
 +mod or_then_unwrap;
++mod path_buf_push_overwrite;
++mod range_zip_with_len;
++mod repeat_once;
 +mod search_is_some;
 +mod single_char_add_str;
 +mod single_char_insert_string;
 +mod single_char_pattern;
 +mod single_char_push_string;
 +mod skip_while_next;
++mod stable_sort_primitive;
 +mod str_splitn;
 +mod string_extend_chars;
 +mod suspicious_map;
 +mod suspicious_splitn;
++mod suspicious_to_owned;
 +mod uninit_assumed_init;
++mod unit_hash;
 +mod unnecessary_filter_map;
 +mod unnecessary_fold;
 +mod unnecessary_iter_cloned;
 +mod unnecessary_join;
 +mod unnecessary_lazy_eval;
++mod unnecessary_sort_by;
 +mod unnecessary_to_owned;
 +mod unwrap_or_else_default;
 +mod unwrap_used;
 +mod useless_asref;
 +mod utils;
++mod vec_resize_to_zero;
++mod verbose_file_reads;
 +mod wrong_self_convention;
 +mod zst_offset;
 +
 +use bind_instead_of_map::BindInsteadOfMap;
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-     /// Checks for `.unwrap()` calls on `Option`s and on `Result`s.
++use clippy_utils::ty::{contains_adt_constructor, implements_trait, is_copy, is_type_diagnostic_item};
++use clippy_utils::{
++    contains_return, get_trait_def_id, is_trait_method, iter_input_pats, meets_msrv, msrvs, paths, return_ty,
++};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_hir::def::Res;
 +use rustc_hir::{Expr, ExprKind, PrimTy, QPath, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::{self, TraitRef, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{sym, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `cloned()` on an `Iterator` or `Option` where
 +    /// `copied()` could be used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// `copied()` is better because it guarantees that the type being cloned
 +    /// implements `Copy`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1, 2, 3].iter().cloned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// [1, 2, 3].iter().copied();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub CLONED_INSTEAD_OF_COPIED,
 +    pedantic,
 +    "used `cloned` where `copied` could be used instead"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for consecutive calls to `str::replace` (2 or more)
++    /// that can be collapsed into a single call.
++    ///
++    /// ### Why is this bad?
++    /// Consecutive `str::replace` calls scan the string multiple times
++    /// with repetitive code.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let hello = "hesuo worpd"
++    ///     .replace('s', "l")
++    ///     .replace("u", "l")
++    ///     .replace('p', "l");
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let hello = "hesuo worpd".replace(&['s', 'u', 'p'], "l");
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub COLLAPSIBLE_STR_REPLACE,
++    perf,
++    "collapse consecutive calls to str::replace (2 or more) into a single call"
++}
++
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.cloned().<func>()` where call to `.cloned()` can be postponed.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's often inefficient to clone all elements of an iterator, when eventually, only some
 +    /// of them will be consumed.
 +    ///
 +    /// ### Known Problems
 +    /// This `lint` removes the side of effect of cloning items in the iterator.
 +    /// A code that relies on that side-effect could fail.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().cloned().take(10);
 +    /// vec.iter().cloned().last();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec!["string".to_string()];
 +    /// vec.iter().take(10).cloned();
 +    /// vec.iter().last().cloned();
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub ITER_OVEREAGER_CLONED,
 +    perf,
 +    "using `cloned()` early with `Iterator::iter()` can lead to some performance inefficiencies"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `Iterator::flat_map()` where `filter_map()` could be
 +    /// used instead.
 +    ///
 +    /// ### Why is this bad?
 +    /// When applicable, `filter_map()` is more clear since it shows that
 +    /// `Option` is used to produce 0 or 1 items.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().flat_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let nums: Vec<i32> = ["1", "2", "whee!"].iter().filter_map(|x| x.parse().ok()).collect();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub FLAT_MAP_OPTION,
 +    pedantic,
 +    "used `flat_map` where `filter_map` could be used instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     /// Checks for `.expect()` calls on `Option`s and `Result`s.
++    /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is better to handle the `None` or `Err` case,
 +    /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
 +    /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
 +    /// `Allow` by default.
 +    ///
 +    /// `result.unwrap()` will let the thread panic on `Err` values.
 +    /// Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// Even if you want to panic on errors, not all `Error`s implement good
 +    /// messages on display. Therefore, it may be beneficial to look at the places
 +    /// where they may get displayed. Activate this lint to do just that.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.unwrap();
 +    /// result.unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("more helpful message");
 +    /// result.expect("more helpful message");
 +    /// ```
 +    ///
 +    /// If [expect_used](#expect_used) is enabled, instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub UNWRAP_USED,
 +    restriction,
 +    "using `.unwrap()` on `Result` or `Option`, which should at least get a better message using `expect()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
-     #[clippy::version = "1.62.0"]
++    /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
 +    ///
 +    /// ### Why is this bad?
 +    /// Usually it is better to handle the `None` or `Err` case.
 +    /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
 +    /// this lint is `Allow` by default.
 +    ///
 +    /// `result.expect()` will let the thread panic on `Err`
 +    /// values. Normally, you want to implement more sophisticated error handling,
 +    /// and propagate errors upwards with `?` operator.
 +    ///
 +    /// ### Examples
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option.expect("one");
 +    /// result.expect("one");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// option?;
 +    ///
 +    /// // or
 +    ///
 +    /// result?;
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub EXPECT_USED,
 +    restriction,
 +    "using `.expect()` on `Result` or `Option`, which might be better handled"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods that should live in a trait
 +    /// implementation of a `std` trait (see [llogiq's blog
 +    /// post](http://llogiq.github.io/2015/07/30/traits.html) for further
 +    /// information) instead of an inherent implementation.
 +    ///
 +    /// ### Why is this bad?
 +    /// Implementing the traits improve ergonomics for users of
 +    /// the code, often with very little cost. Also people seeing a `mul(...)`
 +    /// method
 +    /// may expect `*` to work equally, so you should have good reason to disappoint
 +    /// them.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct X;
 +    /// impl X {
 +    ///     fn add(&self, other: &X) -> X {
 +    ///         // ..
 +    /// # X
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SHOULD_IMPLEMENT_TRAIT,
 +    style,
 +    "defining a method that should be implementing a std trait"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for methods with certain name prefixes and which
 +    /// doesn't match how self is taken. The actual rules are:
 +    ///
 +    /// |Prefix |Postfix     |`self` taken                   | `self` type  |
 +    /// |-------|------------|-------------------------------|--------------|
 +    /// |`as_`  | none       |`&self` or `&mut self`         | any          |
 +    /// |`from_`| none       | none                          | any          |
 +    /// |`into_`| none       |`self`                         | any          |
 +    /// |`is_`  | none       |`&mut self` or `&self` or none | any          |
 +    /// |`to_`  | `_mut`     |`&mut self`                    | any          |
 +    /// |`to_`  | not `_mut` |`self`                         | `Copy`       |
 +    /// |`to_`  | not `_mut` |`&self`                        | not `Copy`   |
 +    ///
 +    /// Note: Clippy doesn't trigger methods with `to_` prefix in:
 +    /// - Traits definition.
 +    /// Clippy can not tell if a type that implements a trait is `Copy` or not.
 +    /// - Traits implementation, when `&self` is taken.
 +    /// The method signature is controlled by the trait and often `&self` is required for all types that implement the trait
 +    /// (see e.g. the `std::string::ToString` trait).
 +    ///
 +    /// Clippy allows `Pin<&Self>` and `Pin<&mut Self>` if `&self` and `&mut self` is required.
 +    ///
 +    /// Please find more info here:
 +    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
 +    ///
 +    /// ### Why is this bad?
 +    /// Consistency breeds readability. If you follow the
 +    /// conventions, your users won't be surprised that they, e.g., need to supply a
 +    /// mutable reference to a `as_..` function.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # struct X;
 +    /// impl X {
 +    ///     fn as_str(self) -> &'static str {
 +    ///         // ..
 +    /// # ""
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_SELF_CONVENTION,
 +    style,
 +    "defining a method named with an established prefix (like \"into_\") that takes `self` with the wrong convention"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `ok().expect(..)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Because you usually call `expect()` on the `Result`
 +    /// directly to get a better error message.
 +    ///
 +    /// ### Known problems
 +    /// The error type needs to implement `Debug`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.ok().expect("why did I do this again?");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Ok::<_, ()>(());
 +    /// x.expect("why did I do this again?");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OK_EXPECT,
 +    style,
 +    "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.err().expect()` calls on the `Result` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
 +    ///
 +    /// ### Example
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.err().expect("Testing err().expect()");
 +    /// ```
 +    /// Use instead:
 +    /// ```should_panic
 +    /// let x: Result<u32, &str> = Ok(10);
 +    /// x.expect_err("Testing expect_err");
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub ERR_EXPECT,
 +    style,
 +    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
 +    /// `Result` values.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written as `_.unwrap_or_default`, which is
 +    /// simpler and more concise.
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_else(Default::default);
 +    /// x.unwrap_or_else(u32::default);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = Some(1);
 +    /// x.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub UNWRAP_OR_ELSE_DEFAULT,
 +    style,
 +    "using `.unwrap_or_else(Default::default)`, which is more succinctly expressed as `.unwrap_or_default()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `option.map(_).unwrap_or(_)` or `option.map(_).unwrap_or_else(_)` or
 +    /// `result.map(_).unwrap_or_else(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, these can be written more concisely (resp.) as
 +    /// `option.map_or(_, _)`, `option.map_or_else(_, _)` and `result.map_or_else(_, _)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order
 +    ///
 +    /// ### Examples
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map(|a| a + 1).unwrap_or(0);
 +    /// result.map(|a| a + 1).unwrap_or_else(some_function);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let option = Some(1);
 +    /// # let result: Result<usize, ()> = Ok(1);
 +    /// # fn some_function(foo: ()) -> usize { 1 }
 +    /// option.map_or(0, |a| a + 1);
 +    /// result.map_or_else(some_function, |a| a + 1);
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub MAP_UNWRAP_OR,
 +    pedantic,
 +    "using `.map(f).unwrap_or(a)` or `.map(f).unwrap_or_else(func)`, which are more succinctly expressed as `map_or(a, f)` or `map_or_else(a, f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, _)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.and_then(_)`.
 +    ///
 +    /// ### Known problems
 +    /// The order of the arguments is not in execution order.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.map_or(None, |a| Some(a + 1));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let opt = Some(1);
 +    /// opt.and_then(|a| Some(a + 1));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OPTION_MAP_OR_NONE,
 +    style,
 +    "using `Option.map_or(None, f)`, which is more succinctly expressed as `and_then(f)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map_or(None, Some)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ok()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.map_or(None, Some));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let r: Result<u32, &str> = Ok(1);
 +    /// assert_eq!(Some(1), r.ok());
 +    /// ```
 +    #[clippy::version = "1.44.0"]
 +    pub RESULT_MAP_OR_INTO_OPTION,
 +    style,
 +    "using `Result.map_or(None, Some)`, which is more succinctly expressed as `ok()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
 +    /// `_.or_else(|x| Err(y))`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().and_then(|s| Some(s.len()));
 +    /// let _ = res().and_then(|s| if s.len() == 42 { Ok(10) } else { Ok(20) });
 +    /// let _ = res().or_else(|s| if s.len() == 42 { Err(10) } else { Err(20) });
 +    /// ```
 +    ///
 +    /// The correct use would be:
 +    ///
 +    /// ```rust
 +    /// # fn opt() -> Option<&'static str> { Some("42") }
 +    /// # fn res() -> Result<&'static str, &'static str> { Ok("42") }
 +    /// let _ = opt().map(|s| s.len());
 +    /// let _ = res().map(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// let _ = res().map_err(|s| if s.len() == 42 { 10 } else { 20 });
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub BIND_INSTEAD_OF_MAP,
 +    complexity,
 +    "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().filter(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub FILTER_NEXT,
 +    complexity,
 +    "using `filter(p).next()`, which is more succinctly expressed as `.find(p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.skip_while(condition).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find(!condition)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().skip_while(|x| **x == 0).next();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![1];
 +    /// vec.iter().find(|x| **x != 0);
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub SKIP_WHILE_NEXT,
 +    complexity,
 +    "using `skip_while(p).next()`, which is more succinctly expressed as `.find(!p)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).flatten(_)` on `Iterator` and `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.flat_map(_)` for `Iterator` or `_.and_then(_)` for `Option`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![vec![1]];
 +    /// let opt = Some(5);
 +    ///
 +    /// vec.iter().map(|x| x.iter()).flatten();
 +    /// opt.map(|x| Some(x * 2)).flatten();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![vec![1]];
 +    /// # let opt = Some(5);
 +    /// vec.iter().flat_map(|x| x.iter());
 +    /// opt.and_then(|x| Some(x * 2));
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub MAP_FLATTEN,
 +    complexity,
 +    "using combinations of `flatten` and `map` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter(_).map(_)` that can be written more simply
 +    /// as `filter_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `filter` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// (0_i32..10)
 +    ///     .filter(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0_i32..10).filter_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FILTER_MAP,
 +    complexity,
 +    "using `_.filter(_).map(_)` in a way that can be written more simply as `filter_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.find(_).map(_)` that can be written more simply
 +    /// as `find_map(_)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code in the `find` and `map` operations is poor style and
 +    /// less performant.
 +    ///
 +     /// ### Example
 +    /// ```rust
 +    /// (0_i32..10)
 +    ///     .find(|n| n.checked_add(1).is_some())
 +    ///     .map(|n| n.checked_add(1).unwrap());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0_i32..10).find_map(|n| n.checked_add(1));
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub MANUAL_FIND_MAP,
 +    complexity,
 +    "using `_.find(_).map(_)` in a way that can be written more simply as `find_map(_)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.filter_map(_).next()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.find_map(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    ///  (0..3).filter_map(|x| if x == 2 { Some(x) } else { None }).next();
 +    /// ```
 +    /// Can be written as
 +    ///
 +    /// ```rust
 +    ///  (0..3).find_map(|x| if x == 2 { Some(x) } else { None });
 +    /// ```
 +    #[clippy::version = "1.36.0"]
 +    pub FILTER_MAP_NEXT,
 +    pedantic,
 +    "using combination of `filter_map` and `next` which can usually be written as a single method call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `flat_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flat_map(|x| x);
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let iter = vec![vec![0]].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub FLAT_MAP_IDENTITY,
 +    complexity,
 +    "call to `flat_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for an iterator or string search (such as `find()`,
 +    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as:
 +    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
 +    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let vec = vec![1];
 +    /// vec.iter().find(|x| **x == 0).is_some();
 +    ///
 +    /// "hello world".find("world").is_none();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec![1];
 +    /// vec.iter().any(|x| *x == 0);
 +    ///
 +    /// # #[allow(unused)]
 +    /// !"hello world".contains("world");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SEARCH_IS_SOME,
 +    complexity,
 +    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.chars().next()` on a `str` to check
 +    /// if it starts with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.starts_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.chars().next() == Some('_') {};
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let name = "foo";
 +    /// if name.starts_with('_') {};
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_NEXT_CMP,
 +    style,
 +    "using `.chars().next()` to check if a string starts with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
 +    /// 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());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_else(String::new);
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.unwrap_or_default();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub OR_FUN_CALL,
 +    perf,
 +    "using any `*or` method with a function call, which suggests `*or_else`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.or(…).unwrap()` calls to Options and Results.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `.unwrap_or(…)` instead for clarity.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # type Error = &'static str;
 +    /// # let result: Result<&str, Error> = Err("error");
 +    /// let value = result.or::<Error>(Ok(fallback)).unwrap();
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.or(Some(fallback)).unwrap();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let fallback = "fallback";
 +    /// // Result
 +    /// # let result: Result<&str, &str> = Err("error");
 +    /// let value = result.unwrap_or(fallback);
 +    ///
 +    /// // Option
 +    /// # let option: Option<&str> = None;
 +    /// let value = option.unwrap_or(fallback);
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub OR_THEN_UNWRAP,
 +    complexity,
 +    "checks for `.or(…).unwrap()` calls to Options and Results."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `.expect(&format!(...))`, `.expect(foo(..))`,
 +    /// etc., and suggests to use `unwrap_or_else` instead
 +    ///
 +    /// ### Why is this bad?
 +    /// The function will always be called.
 +    ///
 +    /// ### Known problems
 +    /// If the function has side-effects, not calling it will
 +    /// change the semantics of the program, but you shouldn't rely on that anyway.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.expect(&format!("Err {}: {}", err_code, err_msg));
 +    ///
 +    /// // or
 +    ///
 +    /// # let foo = Some(String::new());
 +    /// foo.expect(format!("Err {}: {}", err_code, err_msg).as_str());
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let foo = Some(String::new());
 +    /// # let err_code = "418";
 +    /// # let err_msg = "I'm a teapot";
 +    /// foo.unwrap_or_else(|| panic!("Err {}: {}", err_code, err_msg));
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub EXPECT_FUN_CALL,
 +    perf,
 +    "using any `expect` method with a function call"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a `Copy` type.
 +    ///
 +    /// ### Why is this bad?
 +    /// The only reason `Copy` types implement `Clone` is for
 +    /// generics, not for using the `clone` method on a concrete type.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// 42u64.clone();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_COPY,
 +    complexity,
 +    "using `clone` on a `Copy` type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on a ref-counted pointer,
 +    /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
 +    /// function syntax instead (e.g., `Rc::clone(foo)`).
 +    ///
 +    /// ### Why is this bad?
 +    /// Calling '.clone()' on an Rc, Arc, or Weak
 +    /// can obscure the fact that only the pointer is being cloned, not the underlying
 +    /// data.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// let x = Rc::new(1);
 +    ///
 +    /// x.clone();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::rc::Rc;
 +    /// # let x = Rc::new(1);
 +    /// Rc::clone(&x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_ON_REF_PTR,
 +    restriction,
 +    "using 'clone' on a ref-counted pointer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.clone()` on an `&&T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Cloning an `&&T` copies the inner `&T`, instead of
 +    /// cloning the underlying `T`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn main() {
 +    ///     let x = vec![1];
 +    ///     let y = &&x;
 +    ///     let z = y.clone();
 +    ///     println!("{:p} {:p}", *y, z); // prints out the same pointer
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CLONE_DOUBLE_REF,
 +    correctness,
 +    "using `clone` on `&&T`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.to_string()` on an `&&T` where
 +    /// `T` implements `ToString` directly (like `&&str` or `&&String`).
 +    ///
 +    /// ### Why is this bad?
 +    /// This bypasses the specialized implementation of
 +    /// `ToString` and instead goes through the more expensive string formatting
 +    /// facilities.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Generic implementation for `T: Display` is used (slow)
 +    /// ["foo", "bar"].iter().map(|s| s.to_string());
 +    ///
 +    /// // OK, the specialized impl is used
 +    /// ["foo", "bar"].iter().map(|&s| s.to_string());
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub INEFFICIENT_TO_STRING,
 +    pedantic,
 +    "using `to_string` on `&&T` where `T: ToString`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `new` not returning a type that contains `Self`.
 +    ///
 +    /// ### Why is this bad?
 +    /// As a convention, `new` methods are used to make a new
 +    /// instance of a type.
 +    ///
 +    /// ### Example
 +    /// In an impl block:
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct NotAFoo;
 +    /// impl Foo {
 +    ///     fn new() -> NotAFoo {
 +    /// # NotAFoo
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// struct Bar(Foo);
 +    /// impl Foo {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new() -> Bar {
 +    /// # Bar(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// # struct Foo;
 +    /// # struct FooError;
 +    /// impl Foo {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Result<Foo, FooError> {
 +    /// # Ok(Foo)
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// Or in a trait definition:
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Bad. The type name must contain `Self`
 +    ///     fn new();
 +    /// }
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// pub trait Trait {
 +    ///     // Good. Return type contains `Self`
 +    ///     fn new() -> Self;
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEW_RET_NO_SELF,
 +    style,
 +    "not returning type containing `Self` in a `new` method"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string methods that receive a single-character
 +    /// `str` as an argument, e.g., `_.split("x")`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Performing these methods using a `char` is faster than
 +    /// using a `str`.
 +    ///
 +    /// ### Known problems
 +    /// Does not catch multi-byte unicode characters.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// _.split("x");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// _.split('x');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub SINGLE_CHAR_PATTERN,
 +    perf,
 +    "using a single-character str where a char could be used, e.g., `_.split(\"x\")`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `.step_by(0)` on iterators which panics.
 +    ///
 +    /// ### Why is this bad?
 +    /// This very much looks like an oversight. Use `panic!()` instead if you
 +    /// actually intend to panic.
 +    ///
 +    /// ### Example
 +    /// ```rust,should_panic
 +    /// for x in (0..100).step_by(0) {
 +    ///     //..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITERATOR_STEP_BY_ZERO,
 +    correctness,
 +    "using `Iterator::step_by(0)`, which will panic at runtime"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for indirect collection of populated `Option`
 +    ///
 +    /// ### Why is this bad?
 +    /// `Option` is like a collection of 0-1 things, so `flatten`
 +    /// automatically does this without suspicious-looking `unwrap` calls.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().filter(Option::is_some).map(Option::unwrap);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let _ = std::iter::empty::<Option<i32>>().flatten();
 +    /// ```
 +    #[clippy::version = "1.53.0"]
 +    pub OPTION_FILTER_MAP,
 +    complexity,
 +    "filtering `Option` for `Some` then force-unwrapping, which can be one type-safe operation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `iter.nth(0)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `iter.next()` is equivalent to
 +    /// `iter.nth(0)`, as they both consume the next element,
 +    ///  but is more readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().nth(0);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// # let mut s = HashSet::new();
 +    /// # s.insert(1);
 +    /// let x = s.iter().next();
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub ITER_NTH_ZERO,
 +    style,
 +    "replace `iter.nth(0)` with `iter.next()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.iter().nth()` (and the related
 +    /// `.iter_mut().nth()`) on standard library types with *O*(1) element access.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.get()` and `.get_mut()` are more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.get(3);
 +    /// let bad_slice = &some_vec[..].get(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_NTH,
 +    perf,
 +    "using `.iter().nth()` on a standard library type with O(1) element access"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.skip(x).next()` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.nth(x)` is cleaner
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().skip(3).next();
 +    /// let bad_slice = &some_vec[..].iter().skip(3).next();
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    /// let bad_vec = some_vec.iter().nth(3);
 +    /// let bad_slice = &some_vec[..].iter().nth(3);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_SKIP_NEXT,
 +    style,
 +    "using `.skip(x).next()` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.drain(..)` on `Vec` and `VecDeque` for iteration.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.into_iter()` is simpler with better performance.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let mut foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.drain(..).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::collections::HashSet;
 +    /// let foo = vec![0, 1, 2, 3];
 +    /// let bar: HashSet<usize> = foo.into_iter().collect();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ITER_WITH_DRAIN,
 +    nursery,
 +    "replace `.drain(..)` with `.into_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `x.get(x.len() - 1)` instead of
 +    /// `x.last()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `x.last()` is easier to read and has the same
 +    /// result.
 +    ///
 +    /// Note that using `x[x.len() - 1]` is semantically different from
 +    /// `x.last()`.  Indexing into the array will panic on out-of-bounds
 +    /// accesses, while `x.get()` and `x.last()` will return `None`.
 +    ///
 +    /// There is another lint (get_unwrap) that covers the case of using
 +    /// `x.get(index).unwrap()` instead of `x[index]`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.get(x.len() - 1);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = vec![2, 3, 5];
 +    /// let last_element = x.last();
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub GET_LAST_WITH_LEN,
 +    complexity,
 +    "Using `x.get(x.len() - 1)` when `x.last()` is correct and simpler"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.get().unwrap()` (or
 +    /// `.get_mut().unwrap`) on a standard library type which implements `Index`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the Index trait (`[]`) is more clear and more
 +    /// concise.
 +    ///
 +    /// ### Known problems
 +    /// Not a replacement for error handling: Using either
 +    /// `.unwrap()` or the Index trait (`[]`) carries the risk of causing a `panic`
 +    /// if the value being accessed is `None`. If the use of `.get().unwrap()` is a
 +    /// temporary placeholder for dealing with the `Option` type, then this does
 +    /// not mitigate the need for error handling. If there is a chance that `.get()`
 +    /// will be `None` in your program, then it is advisable that the `None` case
 +    /// is handled in a future refactor instead of using `.unwrap()` or the Index
 +    /// trait.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec.get(3).unwrap();
 +    /// *some_vec.get_mut(0).unwrap() = 1;
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let mut some_vec = vec![0, 1, 2, 3];
 +    /// let last = some_vec[3];
 +    /// some_vec[0] = 1;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub GET_UNWRAP,
 +    restriction,
 +    "using `.get().unwrap()` or `.get_mut().unwrap()` when using `[]` would work instead"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for occurrences where one vector gets extended instead of append
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `append` instead of `extend` is more concise and faster
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.extend(b.drain(..));
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let mut a = vec![1, 2, 3];
 +    /// let mut b = vec![4, 5, 6];
 +    ///
 +    /// a.append(&mut b);
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub EXTEND_WITH_DRAIN,
 +    perf,
 +    "using vec.append(&mut vec) to move the full range of a vector to another"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.extend(s.chars())` where s is a
 +    /// `&str` or `String`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.push_str(s)` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.extend(abc.chars());
 +    /// s.extend(def.chars());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let abc = "abc";
 +    /// let def = String::from("def");
 +    /// let mut s = String::new();
 +    /// s.push_str(abc);
 +    /// s.push_str(&def);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub STRING_EXTEND_CHARS,
 +    style,
 +    "using `x.extend(s.chars())` where s is a `&str` or `String`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.cloned().collect()` on slice to
 +    /// create a `Vec`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.to_vec()` is clearer
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s[..].iter().cloned().collect();
 +    /// ```
 +    /// The better use would be:
 +    /// ```rust
 +    /// let s = [1, 2, 3, 4, 5];
 +    /// let s2: Vec<isize> = s.to_vec();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub ITER_CLONED_COLLECT,
 +    style,
 +    "using `.cloned().collect()` on slice to create a `Vec`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.chars().last()` or
 +    /// `_.chars().next_back()` on a `str` to check if it ends with a given char.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.ends_with(_)`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.chars().last() == Some('_') || name.chars().next_back() == Some('-');
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let name = "_";
 +    /// name.ends_with('_') || name.ends_with('-');
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CHARS_LAST_CMP,
 +    style,
 +    "using `.chars().last()` or `.chars().next_back()` to check if a string ends with a char"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `.as_ref()` or `.as_mut()` where the
 +    /// types before and after the call are the same.
 +    ///
 +    /// ### Why is this bad?
 +    /// The call is unnecessary.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x.as_ref());
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// # fn do_stuff(x: &[i32]) {}
 +    /// let x: &[i32] = &[1, 2, 3, 4, 5];
 +    /// do_stuff(x);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_ASREF,
 +    complexity,
 +    "using `as_ref` where the types before and after the call are the same"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for using `fold` when a more succinct alternative exists.
 +    /// Specifically, this checks for `fold`s which could be replaced by `any`, `all`,
 +    /// `sum` or `product`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// (0..3).fold(false, |acc, x| acc || x > 2);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).any(|x| x > 2);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNNECESSARY_FOLD,
 +    style,
 +    "using `fold` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `filter_map` calls that could be replaced by `filter` or `map`.
 +    /// More specifically it checks if the closure provided is only performing one of the
 +    /// filter or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).filter_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).filter(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).filter_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1);
 +    /// ```
 +    #[clippy::version = "1.31.0"]
 +    pub UNNECESSARY_FILTER_MAP,
 +    complexity,
 +    "using `filter_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `find_map` calls that could be replaced by `find` or `map`. More
 +    /// specifically it checks if the closure provided is only performing one of the
 +    /// find or map operations and suggests the appropriate option.
 +    ///
 +    /// ### Why is this bad?
 +    /// Complexity. The intent is also clearer if only a single
 +    /// operation is being performed.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).find_map(|x| if x > 2 { Some(x) } else { None });
 +    ///
 +    /// // As there is no transformation of the argument this could be written as:
 +    /// let _ = (0..3).find(|&x| x > 2);
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// let _ = (0..4).find_map(|x| Some(x + 1));
 +    ///
 +    /// // As there is no conditional check on the argument this could be written as:
 +    /// let _ = (0..4).map(|x| x + 1).next();
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_FIND_MAP,
 +    complexity,
 +    "using `find_map` when a more succinct alternative exists"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `into_iter` calls on references which should be replaced by `iter`
 +    /// or `iter_mut`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. Calling `into_iter` on a reference will not move out its
 +    /// content into the resulting iterator, which is confusing. It is better just call `iter` or
 +    /// `iter_mut` directly.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).into_iter();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let vec = vec![3, 4, 5];
 +    /// (&vec).iter();
 +    /// ```
 +    #[clippy::version = "1.32.0"]
 +    pub INTO_ITER_ON_REF,
 +    style,
 +    "using `.into_iter()` on a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `map` followed by a `count`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It looks suspicious. Maybe `map` was confused with `filter`.
 +    /// If the `map` call is intentional, this should be rewritten
 +    /// using `inspect`. Or, if you intend to drive the iterator to
 +    /// completion, you can just use `for_each` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let _ = (0..3).map(|x| x + 2).count();
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub SUSPICIOUS_MAP,
 +    suspicious,
 +    "suspicious usage of map"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `MaybeUninit::uninit().assume_init()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// For most types, this is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// For now, we accept empty tuples and tuples / arrays
 +    /// of `MaybeUninit`. There may be other types that allow uninitialized
 +    /// data, but those are not yet rigorously defined.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // Beware the UB
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: usize = unsafe { MaybeUninit::uninit().assume_init() };
 +    /// ```
 +    ///
 +    /// Note that the following is OK:
 +    ///
 +    /// ```rust
 +    /// use std::mem::MaybeUninit;
 +    ///
 +    /// let _: [MaybeUninit<bool>; 5] = unsafe {
 +    ///     MaybeUninit::uninit().assume_init()
 +    /// };
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub UNINIT_ASSUMED_INIT,
 +    correctness,
 +    "`MaybeUninit::uninit().assume_init()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `.checked_add/sub(x).unwrap_or(MAX/MIN)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be written simply with `saturating_add/sub` methods.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.checked_add(y).unwrap_or(u32::MAX);
 +    /// let sub = x.checked_sub(y).unwrap_or(u32::MIN);
 +    /// ```
 +    ///
 +    /// can be written using dedicated methods for saturating addition/subtraction as:
 +    ///
 +    /// ```rust
 +    /// # let y: u32 = 0;
 +    /// # let x: u32 = 100;
 +    /// let add = x.saturating_add(y);
 +    /// let sub = x.saturating_sub(y);
 +    /// ```
 +    #[clippy::version = "1.39.0"]
 +    pub MANUAL_SATURATING_ARITHMETIC,
 +    style,
 +    "`.checked_add/sub(x).unwrap_or(MAX/MIN)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `offset(_)`, `wrapping_`{`add`, `sub`}, etc. on raw pointers to
 +    /// zero-sized types
 +    ///
 +    /// ### Why is this bad?
 +    /// This is a no-op, and likely unintended
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe { (&() as *const ()).offset(1) };
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub ZST_OFFSET,
 +    correctness,
 +    "Check for offset calculations on raw pointers to zero-sized types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `FileType::is_file()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// When people testing a file type with `FileType::is_file`
 +    /// they are testing whether a path is something they can get bytes from. But
 +    /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
 +    /// symlink in windows. Using `!FileType::is_dir()` is a better way to that intention.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if filetype.is_file() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    ///
 +    /// should be written as:
 +    ///
 +    /// ```rust
 +    /// # || {
 +    /// let metadata = std::fs::metadata("foo.txt")?;
 +    /// let filetype = metadata.file_type();
 +    ///
 +    /// if !filetype.is_dir() {
 +    ///     // read file
 +    /// }
 +    /// # Ok::<_, std::io::Error>(())
 +    /// # };
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub FILETYPE_IS_FILE,
 +    restriction,
 +    "`FileType::is_file` is not recommended to test for readable file type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.as_ref().map(Deref::deref)` or it's aliases (such as String::as_str).
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely as
 +    /// `_.as_deref()`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_ref().map(String::as_str)
 +    /// # ;
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// # let opt = Some("".to_string());
 +    /// opt.as_deref()
 +    /// # ;
 +    /// ```
 +    #[clippy::version = "1.42.0"]
 +    pub OPTION_AS_REF_DEREF,
 +    complexity,
 +    "using `as_ref().map(Deref::deref)`, which is more succinctly expressed as `as_deref()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `iter().next()` on a Slice or an Array
 +    ///
 +    /// ### Why is this bad?
 +    /// These can be shortened into `.get()`
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a[2..].iter().next();
 +    /// b.iter().next();
 +    /// ```
 +    /// should be written as:
 +    /// ```rust
 +    /// # let a = [1, 2, 3];
 +    /// # let b = vec![1, 2, 3];
 +    /// a.get(2);
 +    /// b.get(0);
 +    /// ```
 +    #[clippy::version = "1.46.0"]
 +    pub ITER_NEXT_SLICE,
 +    style,
 +    "using `.iter().next()` on a sliced array, which can be shortened to just `.get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when using `push_str`/`insert_str` with a single-character string literal
 +    /// where `push`/`insert` with a `char` would work fine.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's less clear that we are pushing a single character.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert_str(0, "R");
 +    /// string.push_str("R");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let mut string = String::new();
 +    /// string.insert(0, 'R');
 +    /// string.push('R');
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub SINGLE_CHAR_ADD_STR,
 +    style,
 +    "`push_str()` or `insert_str()` used with a single-character string literal as parameter"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// As the counterpart to `or_fun_call`, this lint looks for unnecessary
 +    /// lazily evaluated closures on `Option` and `Result`.
 +    ///
 +    /// This lint suggests changing the following functions, when eager evaluation results in
 +    /// simpler code:
 +    ///  - `unwrap_or_else` to `unwrap_or`
 +    ///  - `and_then` to `and`
 +    ///  - `or_else` to `or`
 +    ///  - `get_or_insert_with` to `get_or_insert`
 +    ///  - `ok_or_else` to `ok_or`
 +    ///
 +    /// ### Why is this bad?
 +    /// Using eager evaluation is shorter and simpler in some cases.
 +    ///
 +    /// ### Known problems
 +    /// It is possible, but not recommended for `Deref` and `Index` to have
 +    /// side effects. Eagerly evaluating them can change the semantics of the program.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // example code where clippy issues a warning
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or_else(|| 42);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let opt: Option<u32> = None;
 +    ///
 +    /// opt.unwrap_or(42);
 +    /// ```
 +    #[clippy::version = "1.48.0"]
 +    pub UNNECESSARY_LAZY_EVALUATIONS,
 +    style,
 +    "using unnecessary lazy evaluation, which can be replaced with simpler eager evaluation"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `_.map(_).collect::<Result<(), _>()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using `try_for_each` instead is more readable and idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// (0..3).map(|t| Err(t)).collect::<Result<(), _>>();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// (0..3).try_for_each(|t| Err(t));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MAP_COLLECT_RESULT_UNIT,
 +    style,
 +    "using `.map(_).collect::<Result<(),_>()`, which can be replaced with `try_for_each`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `from_iter()` function calls on types that implement the `FromIterator`
 +    /// trait.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is recommended style to use collect. See
 +    /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v = Vec::from_iter(five_fives);
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let five_fives = std::iter::repeat(5).take(5);
 +    ///
 +    /// let v: Vec<i32> = five_fives.collect();
 +    ///
 +    /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub FROM_ITER_INSTEAD_OF_COLLECT,
 +    pedantic,
 +    "use `.collect()` instead of `::from_iter()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `inspect().for_each()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is the same as performing the computation
 +    /// inside `inspect` at the beginning of the closure in `for_each`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .inspect(|&x| println!("inspect the number: {}", x))
 +    /// .for_each(|&x| {
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    /// Can be written as
 +    /// ```rust
 +    /// [1,2,3,4,5].iter()
 +    /// .for_each(|&x| {
 +    ///     println!("inspect the number: {}", x);
 +    ///     assert!(x >= 0);
 +    /// });
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub INSPECT_FOR_EACH,
 +    complexity,
 +    "using `.inspect().for_each()`, which can be replaced with `.for_each()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usage of `filter_map(|x| x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability, this can be written more concisely by using `flatten`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.filter_map(|x| x);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let iter = vec![Some(1)].into_iter();
 +    /// iter.flatten();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub FILTER_MAP_IDENTITY,
 +    complexity,
 +    "call to `filter_map` where `flatten` is sufficient"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for instances of `map(f)` where `f` is the identity function.
 +    ///
 +    /// ### Why is this bad?
 +    /// It can be written more concisely without the call to `map`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| x).map(|x| 2*x).collect();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = [1, 2, 3];
 +    /// let y: Vec<_> = x.iter().map(|x| 2*x).collect();
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub MAP_IDENTITY,
 +    complexity,
 +    "using iterator.map(|x| x)"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.bytes().nth()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.as_bytes().get()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".bytes().nth(3);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # #[allow(unused)]
 +    /// "Hello".as_bytes().get(3);
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub BYTES_NTH,
 +    style,
 +    "replace `.bytes().nth()` with `.as_bytes().get()`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the usage of `_.to_owned()`, `vec.to_vec()`, or similar when calling `_.clone()` would be clearer.
 +    ///
 +    /// ### Why is this bad?
 +    /// These methods do the same thing as `_.clone()` but may be confusing as
 +    /// to why we are calling `to_vec` on something that is already a `Vec` or calling `to_owned` on something that is already owned.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.to_vec();
 +    /// let c = a.to_owned();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = vec![1, 2, 3];
 +    /// let b = a.clone();
 +    /// let c = a.clone();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub IMPLICIT_CLONE,
 +    pedantic,
 +    "implicitly cloning a value by invoking a function on its dereferenced type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for the use of `.iter().count()`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.len()` is more efficient and more
 +    /// readable.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # #![allow(unused)]
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.iter().count();
 +    /// &some_vec[..].iter().count();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let some_vec = vec![0, 1, 2, 3];
 +    ///
 +    /// some_vec.len();
 +    /// &some_vec[..].len();
 +    /// ```
 +    #[clippy::version = "1.52.0"]
 +    pub ITER_COUNT,
 +    complexity,
 +    "replace `.iter().count()` with `.len()`"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for the usage of `_.to_owned()`, on a `Cow<'_, _>`.
++    ///
++    /// ### Why is this bad?
++    /// Calling `to_owned()` on a `Cow` creates a clone of the `Cow`
++    /// itself, without taking ownership of the `Cow` contents (i.e.
++    /// it's equivalent to calling `Cow::clone`).
++    /// The similarly named `into_owned` method, on the other hand,
++    /// clones the `Cow` contents, effectively turning any `Cow::Borrowed`
++    /// into a `Cow::Owned`.
++    ///
++    /// Given the potential ambiguity, consider replacing `to_owned`
++    /// with `clone` for better readability or, if getting a `Cow::Owned`
++    /// was the original intent, using `into_owned` instead.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # use std::borrow::Cow;
++    /// let s = "Hello world!";
++    /// let cow = Cow::Borrowed(s);
++    ///
++    /// let data = cow.to_owned();
++    /// assert!(matches!(data, Cow::Borrowed(_)))
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # use std::borrow::Cow;
++    /// let s = "Hello world!";
++    /// let cow = Cow::Borrowed(s);
++    ///
++    /// let data = cow.clone();
++    /// assert!(matches!(data, Cow::Borrowed(_)))
++    /// ```
++    /// or
++    /// ```rust
++    /// # use std::borrow::Cow;
++    /// let s = "Hello world!";
++    /// let cow = Cow::Borrowed(s);
++    ///
++    /// let data = cow.into_owned();
++    /// assert!(matches!(data, String))
++    /// ```
++    #[clippy::version = "1.65.0"]
++    pub SUSPICIOUS_TO_OWNED,
++    suspicious,
++    "calls to `to_owned` on a `Cow<'_, _>` might not do what they are expected"
++}
++
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to [`splitn`]
 +    /// (https://doc.rust-lang.org/std/primitive.str.html#method.splitn) and
 +    /// related functions with either zero or one splits.
 +    ///
 +    /// ### Why is this bad?
 +    /// These calls don't actually split the value and are
 +    /// likely to be intended as a different number.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(1, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let s = "";
 +    /// for x in s.splitn(2, ":") {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub SUSPICIOUS_SPLITN,
 +    correctness,
 +    "checks for `.splitn(0, ..)` and `.splitn(1, ..)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for manual implementations of `str::repeat`
 +    ///
 +    /// ### Why is this bad?
 +    /// These are both harder to read, as well as less performant.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x: String = std::iter::repeat('x').take(10).collect();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x: String = "x".repeat(10);
 +    /// ```
 +    #[clippy::version = "1.54.0"]
 +    pub MANUAL_STR_REPEAT,
 +    perf,
 +    "manual implementation of `str::repeat`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn(2, _)`
 +    ///
 +    /// ### Why is this bad?
 +    /// `split_once` is both clearer in intent and slightly more efficient.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.splitn(2, '=').next_tuple()?;
 +    /// let value = s.splitn(2, '=').nth(1)?;
 +    ///
 +    /// let mut parts = s.splitn(2, '=');
 +    /// let key = parts.next()?;
 +    /// let value = parts.next()?;
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let s = "key=value=add";
 +    /// let (key, value) = s.split_once('=')?;
 +    /// let value = s.split_once('=')?.1;
 +    ///
 +    /// let (key, value) = s.split_once('=')?;
 +    /// ```
 +    ///
 +    /// ### Limitations
 +    /// The multiple statement variant currently only detects `iter.next()?`/`iter.next().unwrap()`
 +    /// in two separate `let` statements that immediately follow the `splitn()`
 +    #[clippy::version = "1.57.0"]
 +    pub MANUAL_SPLIT_ONCE,
 +    complexity,
 +    "replace `.splitn(2, pat)` with `.split_once(pat)`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `str::splitn` (or `str::rsplitn`) where using `str::split` would be the same.
 +    /// ### Why is this bad?
 +    /// The function `split` is simpler and there is no performance difference in these cases, considering
 +    /// that both functions return a lazy iterator.
 +    /// ### Example
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.splitn(3, '=').next().unwrap();
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let str = "key=value=add";
 +    /// let _ = str.split('=').next().unwrap();
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub NEEDLESS_SPLITN,
 +    complexity,
 +    "usages of `str::splitn` that can be replaced with `str::split`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary calls to [`ToOwned::to_owned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html#tymethod.to_owned)
 +    /// and other `to_owned`-like functions.
 +    ///
 +    /// ### Why is this bad?
 +    /// The unnecessary calls result in useless allocations.
 +    ///
 +    /// ### Known problems
 +    /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an
 +    /// owned copy of a resource and the resource is later used mutably. See
 +    /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy().to_string());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let path = std::path::Path::new("x");
 +    /// foo(&path.to_string_lossy());
 +    /// fn foo(s: &str) {}
 +    /// ```
 +    #[clippy::version = "1.59.0"]
 +    pub UNNECESSARY_TO_OWNED,
 +    perf,
 +    "unnecessary calls to `to_owned`-like functions"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
 +    ///
 +    /// ### Why is this bad?
 +    /// `.collect::<String>()` is more concise and might be more performant
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<Vec<String>>().join("");
 +    /// println!("{}", output);
 +    /// ```
 +    /// The correct use would be:
 +    /// ```rust
 +    /// let vector = vec!["hello",  "world"];
 +    /// let output = vector.iter().map(|item| item.to_uppercase()).collect::<String>();
 +    /// println!("{}", output);
 +    /// ```
 +    /// ### Known problems
 +    /// While `.collect::<String>()` is sometimes more performant, there are cases where
 +    /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
 +    /// will prevent loop unrolling and will result in a negative performance impact.
 +    ///
 +    /// Additionally, differences have been observed between aarch64 and x86_64 assembly output,
 +    /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
 +    #[clippy::version = "1.61.0"]
 +    pub UNNECESSARY_JOIN,
 +    pedantic,
 +    "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
 +    /// for example, `Option<&T>::as_deref()` returns the same type.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code and improving readability.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let a = Some(&1);
 +    /// let b = a;
 +    /// ```
 +    #[clippy::version = "1.57.0"]
 +    pub NEEDLESS_OPTION_AS_DEREF,
 +    complexity,
 +    "no-op use of `deref` or `deref_mut` method to `Option`."
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
 +    /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
 +    /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
 +    ///
 +    /// ### Why is this bad?
 +    /// `is_digit(..)` is slower and requires specifying the radix.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_digit(10);
 +    /// c.is_digit(16);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let c: char = '6';
 +    /// c.is_ascii_digit();
 +    /// c.is_ascii_hexdigit();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub IS_DIGIT_ASCII_RADIX,
 +    style,
 +    "use of `char::is_digit(..)` with literal radix of 10 or 16"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calling `take` function after `as_ref`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Redundant code. `take` writes `None` to its argument.
 +    /// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref().take();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = Some(3);
 +    /// x.as_ref();
 +    /// ```
 +    #[clippy::version = "1.62.0"]
 +    pub NEEDLESS_OPTION_TAKE,
 +    complexity,
 +    "using `.as_ref().take()` on a temporary value"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `replace` statements which have no effect.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's either a mistake or confusing.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// "1234".replace("12", "12");
 +    /// "1234".replacen("12", "12", 1);
 +    /// ```
-             } else if contains_ty(ret_ty, self_ty) {
++    #[clippy::version = "1.63.0"]
 +    pub NO_EFFECT_REPLACE,
 +    suspicious,
 +    "replace with no effect"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for usages of `.then_some(..).unwrap_or(..)`
 +    ///
 +    /// ### Why is this bad?
 +    /// This can be written more clearly with `if .. else ..`
 +    ///
 +    /// ### Limitations
 +    /// This lint currently only looks for usages of
 +    /// `.then_some(..).unwrap_or(..)`, but will be expanded
 +    /// to account for similar patterns.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = true;
 +    /// x.then_some("a").unwrap_or("b");
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = true;
 +    /// if x { "a" } else { "b" };
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub OBFUSCATED_IF_ELSE,
 +    style,
 +    "use of `.then_some(..).unwrap_or(..)` can be written \
 +    more clearly with `if .. else ..`"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on collections containing a single item
++    ///
++    /// ### Why is this bad?
++    ///
++    /// It is simpler to use the once function from the standard library:
++    ///
++    /// ### Example
++    ///
++    /// ```rust
++    /// let a = [123].iter();
++    /// let b = Some(123).into_iter();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::iter;
++    /// let a = iter::once(&123);
++    /// let b = iter::once(123);
++    /// ```
++    ///
++    /// ### Known problems
++    ///
++    /// The type of the resulting iterator might become incompatible with its usage
++    #[clippy::version = "1.64.0"]
++    pub ITER_ON_SINGLE_ITEMS,
++    nursery,
++    "Iterator for array of length 1"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Checks for calls to `iter`, `iter_mut` or `into_iter` on empty collections
++    ///
++    /// ### Why is this bad?
++    ///
++    /// It is simpler to use the empty function from the standard library:
++    ///
++    /// ### Example
++    ///
++    /// ```rust
++    /// use std::{slice, option};
++    /// let a: slice::Iter<i32> = [].iter();
++    /// let f: option::IntoIter<i32> = None.into_iter();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::iter;
++    /// let a: iter::Empty<i32> = iter::empty();
++    /// let b: iter::Empty<i32> = iter::empty();
++    /// ```
++    ///
++    /// ### Known problems
++    ///
++    /// The type of the resulting iterator might become incompatible with its usage
++    #[clippy::version = "1.64.0"]
++    pub ITER_ON_EMPTY_COLLECTIONS,
++    nursery,
++    "Iterator for empty array"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for naive byte counts
++    ///
++    /// ### Why is this bad?
++    /// The [`bytecount`](https://crates.io/crates/bytecount)
++    /// crate has methods to count your bytes faster, especially for large slices.
++    ///
++    /// ### Known problems
++    /// If you have predominantly small slices, the
++    /// `bytecount::count(..)` method may actually be slower. However, if you can
++    /// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
++    /// faster in those cases.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let vec = vec![1_u8];
++    /// let count = vec.iter().filter(|x| **x == 0u8).count();
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust,ignore
++    /// # let vec = vec![1_u8];
++    /// let count = bytecount::count(&vec, 0u8);
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub NAIVE_BYTECOUNT,
++    pedantic,
++    "use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// It checks for `str::bytes().count()` and suggests replacing it with
++    /// `str::len()`.
++    ///
++    /// ### Why is this bad?
++    /// `str::bytes().count()` is longer and may not be as performant as using
++    /// `str::len()`.
++    ///
++    /// ### Example
++    /// ```rust
++    /// "hello".bytes().count();
++    /// String::from("hello").bytes().count();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// "hello".len();
++    /// String::from("hello").len();
++    /// ```
++    #[clippy::version = "1.62.0"]
++    pub BYTES_COUNT_TO_LEN,
++    complexity,
++    "Using `bytes().count()` when `len()` performs the same functionality"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for calls to `ends_with` with possible file extensions
++    /// and suggests to use a case-insensitive approach instead.
++    ///
++    /// ### Why is this bad?
++    /// `ends_with` is case-sensitive and may not detect files with a valid extension.
++    ///
++    /// ### Example
++    /// ```rust
++    /// fn is_rust_file(filename: &str) -> bool {
++    ///     filename.ends_with(".rs")
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// fn is_rust_file(filename: &str) -> bool {
++    ///     let filename = std::path::Path::new(filename);
++    ///     filename.extension()
++    ///         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
++    /// }
++    /// ```
++    #[clippy::version = "1.51.0"]
++    pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
++    pedantic,
++    "Checks for calls to ends_with with case-sensitive file extensions"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for using `x.get(0)` instead of
++    /// `x.first()`.
++    ///
++    /// ### Why is this bad?
++    /// Using `x.first()` is easier to read and has the same
++    /// result.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let x = vec![2, 3, 5];
++    /// let first_element = x.get(0);
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// let x = vec![2, 3, 5];
++    /// let first_element = x.first();
++    /// ```
++    #[clippy::version = "1.63.0"]
++    pub GET_FIRST,
++    style,
++    "Using `x.get(0)` when `x.first()` is simpler"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///
++    /// Finds patterns that reimplement `Option::ok_or`.
++    ///
++    /// ### Why is this bad?
++    ///
++    /// Concise code helps focusing on behavior instead of boilerplate.
++    ///
++    /// ### Examples
++    /// ```rust
++    /// let foo: Option<i32> = None;
++    /// foo.map_or(Err("error"), |v| Ok(v));
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// let foo: Option<i32> = None;
++    /// foo.ok_or("error");
++    /// ```
++    #[clippy::version = "1.49.0"]
++    pub MANUAL_OK_OR,
++    pedantic,
++    "finds patterns that can be encoded more concisely with `Option::ok_or`"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for usage of `map(|x| x.clone())` or
++    /// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
++    /// and suggests `cloned()` or `copied()` instead
++    ///
++    /// ### Why is this bad?
++    /// Readability, this can be written more concisely
++    ///
++    /// ### Example
++    /// ```rust
++    /// let x = vec![42, 43];
++    /// let y = x.iter();
++    /// let z = y.map(|i| *i);
++    /// ```
++    ///
++    /// The correct use would be:
++    ///
++    /// ```rust
++    /// let x = vec![42, 43];
++    /// let y = x.iter();
++    /// let z = y.cloned();
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub MAP_CLONE,
++    style,
++    "using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for instances of `map_err(|_| Some::Enum)`
++    ///
++    /// ### Why is this bad?
++    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
++    ///
++    /// ### Example
++    /// Before:
++    /// ```rust
++    /// use std::fmt;
++    ///
++    /// #[derive(Debug)]
++    /// enum Error {
++    ///     Indivisible,
++    ///     Remainder(u8),
++    /// }
++    ///
++    /// impl fmt::Display for Error {
++    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++    ///         match self {
++    ///             Error::Indivisible => write!(f, "could not divide input by three"),
++    ///             Error::Remainder(remainder) => write!(
++    ///                 f,
++    ///                 "input is not divisible by three, remainder = {}",
++    ///                 remainder
++    ///             ),
++    ///         }
++    ///     }
++    /// }
++    ///
++    /// impl std::error::Error for Error {}
++    ///
++    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
++    ///     input
++    ///         .parse::<i32>()
++    ///         .map_err(|_| Error::Indivisible)
++    ///         .map(|v| v % 3)
++    ///         .and_then(|remainder| {
++    ///             if remainder == 0 {
++    ///                 Ok(())
++    ///             } else {
++    ///                 Err(Error::Remainder(remainder as u8))
++    ///             }
++    ///         })
++    /// }
++    ///  ```
++    ///
++    ///  After:
++    ///  ```rust
++    /// use std::{fmt, num::ParseIntError};
++    ///
++    /// #[derive(Debug)]
++    /// enum Error {
++    ///     Indivisible(ParseIntError),
++    ///     Remainder(u8),
++    /// }
++    ///
++    /// impl fmt::Display for Error {
++    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
++    ///         match self {
++    ///             Error::Indivisible(_) => write!(f, "could not divide input by three"),
++    ///             Error::Remainder(remainder) => write!(
++    ///                 f,
++    ///                 "input is not divisible by three, remainder = {}",
++    ///                 remainder
++    ///             ),
++    ///         }
++    ///     }
++    /// }
++    ///
++    /// impl std::error::Error for Error {
++    ///     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
++    ///         match self {
++    ///             Error::Indivisible(source) => Some(source),
++    ///             _ => None,
++    ///         }
++    ///     }
++    /// }
++    ///
++    /// fn divisible_by_3(input: &str) -> Result<(), Error> {
++    ///     input
++    ///         .parse::<i32>()
++    ///         .map_err(Error::Indivisible)
++    ///         .map(|v| v % 3)
++    ///         .and_then(|remainder| {
++    ///             if remainder == 0 {
++    ///                 Ok(())
++    ///             } else {
++    ///                 Err(Error::Remainder(remainder as u8))
++    ///             }
++    ///         })
++    /// }
++    /// ```
++    #[clippy::version = "1.48.0"]
++    pub MAP_ERR_IGNORE,
++    restriction,
++    "`map_err` should not ignore the original error"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for `&mut Mutex::lock` calls
++    ///
++    /// ### Why is this bad?
++    /// `Mutex::lock` is less efficient than
++    /// calling `Mutex::get_mut`. In addition you also have a statically
++    /// guarantee that the mutex isn't locked, instead of just a runtime
++    /// guarantee.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::sync::{Arc, Mutex};
++    ///
++    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
++    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
++    ///
++    /// let mut value = value_mutex.lock().unwrap();
++    /// *value += 1;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// use std::sync::{Arc, Mutex};
++    ///
++    /// let mut value_rc = Arc::new(Mutex::new(42_u8));
++    /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
++    ///
++    /// let value = value_mutex.get_mut().unwrap();
++    /// *value += 1;
++    /// ```
++    #[clippy::version = "1.49.0"]
++    pub MUT_MUTEX_LOCK,
++    style,
++    "`&mut Mutex::lock` does unnecessary locking"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for duplicate open options as well as combinations
++    /// that make no sense.
++    ///
++    /// ### Why is this bad?
++    /// In the best case, the code will be harder to read than
++    /// necessary. I don't know the worst case.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::fs::OpenOptions;
++    ///
++    /// OpenOptions::new().read(true).truncate(true);
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub NONSENSICAL_OPEN_OPTIONS,
++    correctness,
++    "nonsensical combination of options for opening a file"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    ///* Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
++    /// calls on `PathBuf` that can cause overwrites.
++    ///
++    /// ### Why is this bad?
++    /// Calling `push` with a root path at the start can overwrite the
++    /// previous defined path.
++    ///
++    /// ### Example
++    /// ```rust
++    /// use std::path::PathBuf;
++    ///
++    /// let mut x = PathBuf::from("/foo");
++    /// x.push("/bar");
++    /// assert_eq!(x, PathBuf::from("/bar"));
++    /// ```
++    /// Could be written:
++    ///
++    /// ```rust
++    /// use std::path::PathBuf;
++    ///
++    /// let mut x = PathBuf::from("/foo");
++    /// x.push("bar");
++    /// assert_eq!(x, PathBuf::from("/foo/bar"));
++    /// ```
++    #[clippy::version = "1.36.0"]
++    pub PATH_BUF_PUSH_OVERWRITE,
++    nursery,
++    "calling `push` with file system root on `PathBuf` can overwrite it"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for zipping a collection with the range of
++    /// `0.._.len()`.
++    ///
++    /// ### Why is this bad?
++    /// The code is better expressed with `.enumerate()`.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # let x = vec![1];
++    /// let _ = x.iter().zip(0..x.len());
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// # let x = vec![1];
++    /// let _ = x.iter().enumerate();
++    /// ```
++    #[clippy::version = "pre 1.29.0"]
++    pub RANGE_ZIP_WITH_LEN,
++    complexity,
++    "zipping iterator with a range when `enumerate()` would do"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for usage of `.repeat(1)` and suggest the following method for each types.
++    /// - `.to_string()` for `str`
++    /// - `.clone()` for `String`
++    /// - `.to_vec()` for `slice`
++    ///
++    /// The lint will evaluate constant expressions and values as arguments of `.repeat(..)` and emit a message if
++    /// they are equivalent to `1`. (Related discussion in [rust-clippy#7306](https://github.com/rust-lang/rust-clippy/issues/7306))
++    ///
++    /// ### Why is this bad?
++    /// For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning
++    /// the string is the intention behind this, `clone()` should be used.
++    ///
++    /// ### Example
++    /// ```rust
++    /// fn main() {
++    ///     let x = String::from("hello world").repeat(1);
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// fn main() {
++    ///     let x = String::from("hello world").clone();
++    /// }
++    /// ```
++    #[clippy::version = "1.47.0"]
++    pub REPEAT_ONCE,
++    complexity,
++    "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// When sorting primitive values (integers, bools, chars, as well
++    /// as arrays, slices, and tuples of such items), it is typically better to
++    /// use an unstable sort than a stable sort.
++    ///
++    /// ### Why is this bad?
++    /// Typically, using a stable sort consumes more memory and cpu cycles.
++    /// Because values which compare equal are identical, preserving their
++    /// relative order (the guarantee that a stable sort provides) means
++    /// nothing, while the extra costs still apply.
++    ///
++    /// ### Known problems
++    ///
++    /// As pointed out in
++    /// [issue #8241](https://github.com/rust-lang/rust-clippy/issues/8241),
++    /// a stable sort can instead be significantly faster for certain scenarios
++    /// (eg. when a sorted vector is extended with new data and resorted).
++    ///
++    /// For more information and benchmarking results, please refer to the
++    /// issue linked above.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let mut vec = vec![2, 1, 3];
++    /// vec.sort();
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// let mut vec = vec![2, 1, 3];
++    /// vec.sort_unstable();
++    /// ```
++    #[clippy::version = "1.47.0"]
++    pub STABLE_SORT_PRIMITIVE,
++    pedantic,
++    "use of sort() when sort_unstable() is equivalent"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Detects `().hash(_)`.
++    ///
++    /// ### Why is this bad?
++    /// Hashing a unit value doesn't do anything as the implementation of `Hash` for `()` is a no-op.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # use std::hash::Hash;
++    /// # use std::collections::hash_map::DefaultHasher;
++    /// # enum Foo { Empty, WithValue(u8) }
++    /// # use Foo::*;
++    /// # let mut state = DefaultHasher::new();
++    /// # let my_enum = Foo::Empty;
++    /// match my_enum {
++    ///       Empty => ().hash(&mut state),
++    ///       WithValue(x) => x.hash(&mut state),
++    /// }
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # use std::hash::Hash;
++    /// # use std::collections::hash_map::DefaultHasher;
++    /// # enum Foo { Empty, WithValue(u8) }
++    /// # use Foo::*;
++    /// # let mut state = DefaultHasher::new();
++    /// # let my_enum = Foo::Empty;
++    /// match my_enum {
++    ///       Empty => 0_u8.hash(&mut state),
++    ///       WithValue(x) => x.hash(&mut state),
++    /// }
++    /// ```
++    #[clippy::version = "1.58.0"]
++    pub UNIT_HASH,
++    correctness,
++    "hashing a unit value, which does nothing"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Detects uses of `Vec::sort_by` passing in a closure
++    /// which compares the two arguments, either directly or indirectly.
++    ///
++    /// ### Why is this bad?
++    /// It is more clear to use `Vec::sort_by_key` (or `Vec::sort` if
++    /// possible) than to use `Vec::sort_by` and a more complicated
++    /// closure.
++    ///
++    /// ### Known problems
++    /// If the suggested `Vec::sort_by_key` uses Reverse and it isn't already
++    /// imported by a use statement, then it will need to be added manually.
++    ///
++    /// ### Example
++    /// ```rust
++    /// # struct A;
++    /// # impl A { fn foo(&self) {} }
++    /// # let mut vec: Vec<A> = Vec::new();
++    /// vec.sort_by(|a, b| a.foo().cmp(&b.foo()));
++    /// ```
++    /// Use instead:
++    /// ```rust
++    /// # struct A;
++    /// # impl A { fn foo(&self) {} }
++    /// # let mut vec: Vec<A> = Vec::new();
++    /// vec.sort_by_key(|a| a.foo());
++    /// ```
++    #[clippy::version = "1.46.0"]
++    pub UNNECESSARY_SORT_BY,
++    complexity,
++    "Use of `Vec::sort_by` when `Vec::sort_by_key` or `Vec::sort` would be clearer"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Finds occurrences of `Vec::resize(0, an_int)`
++    ///
++    /// ### Why is this bad?
++    /// This is probably an argument inversion mistake.
++    ///
++    /// ### Example
++    /// ```rust
++    /// vec!(1, 2, 3, 4, 5).resize(0, 5)
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// vec!(1, 2, 3, 4, 5).clear()
++    /// ```
++    #[clippy::version = "1.46.0"]
++    pub VEC_RESIZE_TO_ZERO,
++    correctness,
++    "emptying a vector with `resize(0, an_int)` instead of `clear()` is probably an argument inversion mistake"
++}
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for use of File::read_to_end and File::read_to_string.
++    ///
++    /// ### Why is this bad?
++    /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
++    /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
++    ///
++    /// ### Example
++    /// ```rust,no_run
++    /// # use std::io::Read;
++    /// # use std::fs::File;
++    /// let mut f = File::open("foo.txt").unwrap();
++    /// let mut bytes = Vec::new();
++    /// f.read_to_end(&mut bytes).unwrap();
++    /// ```
++    /// Can be written more concisely as
++    /// ```rust,no_run
++    /// # use std::fs;
++    /// let mut bytes = fs::read("foo.txt").unwrap();
++    /// ```
++    #[clippy::version = "1.44.0"]
++    pub VERBOSE_FILE_READS,
++    restriction,
++    "use of `File::read_to_end` or `File::read_to_string`"
++}
++
 +pub struct Methods {
 +    avoid_breaking_exported_api: bool,
 +    msrv: Option<RustcVersion>,
 +    allow_expect_in_tests: bool,
 +    allow_unwrap_in_tests: bool,
 +}
 +
 +impl Methods {
 +    #[must_use]
 +    pub fn new(
 +        avoid_breaking_exported_api: bool,
 +        msrv: Option<RustcVersion>,
 +        allow_expect_in_tests: bool,
 +        allow_unwrap_in_tests: bool,
 +    ) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +            msrv,
 +            allow_expect_in_tests,
 +            allow_unwrap_in_tests,
 +        }
 +    }
 +}
 +
 +impl_lint_pass!(Methods => [
 +    UNWRAP_USED,
 +    EXPECT_USED,
 +    SHOULD_IMPLEMENT_TRAIT,
 +    WRONG_SELF_CONVENTION,
 +    OK_EXPECT,
 +    UNWRAP_OR_ELSE_DEFAULT,
 +    MAP_UNWRAP_OR,
 +    RESULT_MAP_OR_INTO_OPTION,
 +    OPTION_MAP_OR_NONE,
 +    BIND_INSTEAD_OF_MAP,
 +    OR_FUN_CALL,
 +    OR_THEN_UNWRAP,
 +    EXPECT_FUN_CALL,
 +    CHARS_NEXT_CMP,
 +    CHARS_LAST_CMP,
 +    CLONE_ON_COPY,
 +    CLONE_ON_REF_PTR,
 +    CLONE_DOUBLE_REF,
++    COLLAPSIBLE_STR_REPLACE,
 +    ITER_OVEREAGER_CLONED,
 +    CLONED_INSTEAD_OF_COPIED,
 +    FLAT_MAP_OPTION,
 +    INEFFICIENT_TO_STRING,
 +    NEW_RET_NO_SELF,
 +    SINGLE_CHAR_PATTERN,
 +    SINGLE_CHAR_ADD_STR,
 +    SEARCH_IS_SOME,
 +    FILTER_NEXT,
 +    SKIP_WHILE_NEXT,
 +    FILTER_MAP_IDENTITY,
 +    MAP_IDENTITY,
 +    MANUAL_FILTER_MAP,
 +    MANUAL_FIND_MAP,
 +    OPTION_FILTER_MAP,
 +    FILTER_MAP_NEXT,
 +    FLAT_MAP_IDENTITY,
 +    MAP_FLATTEN,
 +    ITERATOR_STEP_BY_ZERO,
 +    ITER_NEXT_SLICE,
 +    ITER_COUNT,
 +    ITER_NTH,
 +    ITER_NTH_ZERO,
 +    BYTES_NTH,
 +    ITER_SKIP_NEXT,
 +    GET_UNWRAP,
 +    GET_LAST_WITH_LEN,
 +    STRING_EXTEND_CHARS,
 +    ITER_CLONED_COLLECT,
 +    ITER_WITH_DRAIN,
 +    USELESS_ASREF,
 +    UNNECESSARY_FOLD,
 +    UNNECESSARY_FILTER_MAP,
 +    UNNECESSARY_FIND_MAP,
 +    INTO_ITER_ON_REF,
 +    SUSPICIOUS_MAP,
 +    UNINIT_ASSUMED_INIT,
 +    MANUAL_SATURATING_ARITHMETIC,
 +    ZST_OFFSET,
 +    FILETYPE_IS_FILE,
 +    OPTION_AS_REF_DEREF,
 +    UNNECESSARY_LAZY_EVALUATIONS,
 +    MAP_COLLECT_RESULT_UNIT,
 +    FROM_ITER_INSTEAD_OF_COLLECT,
 +    INSPECT_FOR_EACH,
 +    IMPLICIT_CLONE,
++    SUSPICIOUS_TO_OWNED,
 +    SUSPICIOUS_SPLITN,
 +    MANUAL_STR_REPEAT,
 +    EXTEND_WITH_DRAIN,
 +    MANUAL_SPLIT_ONCE,
 +    NEEDLESS_SPLITN,
 +    UNNECESSARY_TO_OWNED,
 +    UNNECESSARY_JOIN,
 +    ERR_EXPECT,
 +    NEEDLESS_OPTION_AS_DEREF,
 +    IS_DIGIT_ASCII_RADIX,
 +    NEEDLESS_OPTION_TAKE,
 +    NO_EFFECT_REPLACE,
 +    OBFUSCATED_IF_ELSE,
++    ITER_ON_SINGLE_ITEMS,
++    ITER_ON_EMPTY_COLLECTIONS,
++    NAIVE_BYTECOUNT,
++    BYTES_COUNT_TO_LEN,
++    CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
++    GET_FIRST,
++    MANUAL_OK_OR,
++    MAP_CLONE,
++    MAP_ERR_IGNORE,
++    MUT_MUTEX_LOCK,
++    NONSENSICAL_OPEN_OPTIONS,
++    PATH_BUF_PUSH_OVERWRITE,
++    RANGE_ZIP_WITH_LEN,
++    REPEAT_ONCE,
++    STABLE_SORT_PRIMITIVE,
++    UNIT_HASH,
++    UNNECESSARY_SORT_BY,
++    VEC_RESIZE_TO_ZERO,
++    VERBOSE_FILE_READS,
 +]);
 +
 +/// Extracts a method call name, args, and `Span` of the method name.
 +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> {
 +    if let ExprKind::MethodCall(path, args, _) = recv.kind {
 +        if !args.iter().any(|e| e.span.from_expansion()) {
 +            let name = path.ident.name.as_str();
 +            return Some((name, args, path.ident.span));
 +        }
 +    }
 +    None
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for Methods {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        self.check_methods(cx, expr);
 +
 +        match expr.kind {
 +            hir::ExprKind::Call(func, args) => {
 +                from_iter_instead_of_collect::check(cx, expr, args, func);
 +            },
 +            hir::ExprKind::MethodCall(method_call, args, _) => {
 +                let method_span = method_call.ident.span;
 +                or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
 +                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), args);
 +                clone_on_copy::check(cx, expr, method_call.ident.name, args);
 +                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
 +                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
 +                single_char_add_str::check(cx, expr, args);
 +                into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, args);
 +                single_char_pattern::check(cx, expr, method_call.ident.name, args);
 +                unnecessary_to_owned::check(cx, expr, method_call.ident.name, args, self.msrv);
 +            },
 +            hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => {
 +                let mut info = BinaryExprInfo {
 +                    expr,
 +                    chain: lhs,
 +                    other: rhs,
 +                    eq: op.node == hir::BinOpKind::Eq,
 +                };
 +                lint_binary_expr_with_method_call(cx, &mut info);
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    #[allow(clippy::too_many_lines)]
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
 +        if in_external_macro(cx.sess(), impl_item.span) {
 +            return;
 +        }
 +        let name = impl_item.ident.name.as_str();
 +        let parent = cx.tcx.hir().get_parent_item(impl_item.hir_id());
 +        let item = cx.tcx.hir().expect_item(parent);
 +        let self_ty = cx.tcx.type_of(item.def_id);
 +
 +        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 +        if_chain! {
 +            if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
 +            if let Some(first_arg) = iter_input_pats(sig.decl, cx.tcx.hir().body(id)).next();
 +
 +            let method_sig = cx.tcx.fn_sig(impl_item.def_id);
 +            let method_sig = cx.tcx.erase_late_bound_regions(method_sig);
 +
 +            let first_arg_ty = method_sig.inputs().iter().next();
 +
 +            // check conventions w.r.t. conversion method names and predicates
 +            if let Some(first_arg_ty) = first_arg_ty;
 +
 +            then {
 +                // if this impl block implements a trait, lint in trait definition instead
 +                if !implements_trait && cx.access_levels.is_exported(impl_item.def_id) {
 +                    // check missing trait implementations
 +                    for method_config in &TRAIT_METHODS {
 +                        if name == method_config.method_name &&
 +                            sig.decl.inputs.len() == method_config.param_count &&
 +                            method_config.output_type.matches(&sig.decl.output) &&
 +                            method_config.self_kind.matches(cx, self_ty, *first_arg_ty) &&
 +                            fn_header_equals(method_config.fn_header, sig.header) &&
 +                            method_config.lifetime_param_cond(impl_item)
 +                        {
 +                            span_lint_and_help(
 +                                cx,
 +                                SHOULD_IMPLEMENT_TRAIT,
 +                                impl_item.span,
 +                                &format!(
 +                                    "method `{}` can be confused for the standard trait method `{}::{}`",
 +                                    method_config.method_name,
 +                                    method_config.trait_name,
 +                                    method_config.method_name
 +                                ),
 +                                None,
 +                                &format!(
 +                                    "consider implementing the trait `{}` or choosing a less ambiguous method name",
 +                                    method_config.trait_name
 +                                )
 +                            );
 +                        }
 +                    }
 +                }
 +
 +                if sig.decl.implicit_self.has_implicit_self()
 +                    && !(self.avoid_breaking_exported_api
 +                        && cx.access_levels.is_exported(impl_item.def_id))
 +                {
 +                    wrong_self_convention::check(
 +                        cx,
 +                        name,
 +                        self_ty,
 +                        *first_arg_ty,
 +                        first_arg.pat.span,
 +                        implements_trait,
 +                        false
 +                    );
 +                }
 +            }
 +        }
 +
 +        // if this impl block implements a trait, lint in trait definition instead
 +        if implements_trait {
 +            return;
 +        }
 +
 +        if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
 +            let ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +            // walk the return type and check for Self (this does not check associated types)
 +            if let Some(self_adt) = self_ty.ty_adt_def() {
 +                if contains_adt_constructor(ret_ty, self_adt) {
 +                    return;
 +                }
-                         } else if contains_ty(assoc_ty, self_ty) {
++            } else if ret_ty.contains(self_ty) {
 +                return;
 +            }
 +
 +            // if return type is impl trait, check the associated types
 +            if let ty::Opaque(def_id, _) = *ret_ty.kind() {
 +                // one of the associated types must be Self
 +                for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
 +                    if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
 +                        let assoc_ty = match projection_predicate.term {
 +                            ty::Term::Ty(ty) => ty,
 +                            ty::Term::Const(_c) => continue,
 +                        };
 +                        // walk the associated type and check for Self
 +                        if let Some(self_adt) = self_ty.ty_adt_def() {
 +                            if contains_adt_constructor(assoc_ty, self_adt) {
 +                                return;
 +                            }
-             if !contains_ty(ret_ty, self_ty);
++                        } else if assoc_ty.contains(self_ty) {
 +                            return;
 +                        }
 +                    }
 +                }
 +            }
 +
 +            if name == "new" && ret_ty != self_ty {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    impl_item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
 +        if in_external_macro(cx.tcx.sess, item.span) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let TraitItemKind::Fn(ref sig, _) = item.kind;
 +            if sig.decl.implicit_self.has_implicit_self();
 +            if let Some(first_arg_ty) = sig.decl.inputs.iter().next();
 +
 +            then {
 +                let first_arg_span = first_arg_ty.span;
 +                let first_arg_ty = hir_ty_to_ty(cx.tcx, first_arg_ty);
 +                let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
 +                wrong_self_convention::check(
 +                    cx,
 +                    item.ident.name.as_str(),
 +                    self_ty,
 +                    first_arg_ty,
 +                    first_arg_span,
 +                    false,
 +                    true
 +                );
 +            }
 +        }
 +
 +        if_chain! {
 +            if item.ident.name == sym::new;
 +            if let TraitItemKind::Fn(_, _) = item.kind;
 +            let ret_ty = return_ty(cx, item.hir_id());
 +            let self_ty = TraitRef::identity(cx.tcx, item.def_id.to_def_id()).self_ty().skip_binder();
-                 ("count", []) => match method_call(recv) {
++            if !ret_ty.contains(self_ty);
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    NEW_RET_NO_SELF,
 +                    item.span,
 +                    "methods called `new` usually return `Self`",
 +                );
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +impl Methods {
 +    #[allow(clippy::too_many_lines)]
 +    fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if let Some((name, [recv, args @ ..], span)) = method_call(expr) {
 +            match (name, args) {
 +                ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
 +                    zst_offset::check(cx, expr, recv);
 +                },
 +                ("and_then", [arg]) => {
 +                    let biom_option_linted = bind_instead_of_map::OptionAndThenSome::check(cx, expr, recv, arg);
 +                    let biom_result_linted = bind_instead_of_map::ResultAndThenOk::check(cx, expr, recv, arg);
 +                    if !biom_option_linted && !biom_result_linted {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
 +                    }
 +                },
 +                ("as_deref" | "as_deref_mut", []) => {
 +                    needless_option_as_deref::check(cx, expr, recv, name);
 +                },
 +                ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
 +                ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
 +                ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
 +                ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, self.msrv),
 +                ("collect", []) => match method_call(recv) {
 +                    Some((name @ ("cloned" | "copied"), [recv2], _)) => {
 +                        iter_cloned_collect::check(cx, name, expr, recv2);
 +                    },
 +                    Some(("map", [m_recv, m_arg], _)) => {
 +                        map_collect_result_unit::check(cx, expr, m_recv, m_arg, recv);
 +                    },
 +                    Some(("take", [take_self_arg, take_arg], _)) => {
 +                        if meets_msrv(self.msrv, msrvs::STR_REPEAT) {
 +                            manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg);
 +                        }
 +                    },
 +                    _ => {},
 +                },
-                     _ => expect_used::check(cx, expr, recv, self.allow_expect_in_tests),
++                ("count", []) if is_trait_method(cx, expr, sym::Iterator) => match method_call(recv) {
 +                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, true, false),
 +                    Some((name2 @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => {
 +                        iter_count::check(cx, expr, recv2, name2);
 +                    },
 +                    Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg),
++                    Some(("filter", [recv2, arg], _)) => bytecount::check(cx, expr, recv2, arg),
++                    Some(("bytes", [recv2], _)) => bytes_count_to_len::check(cx, expr, recv, recv2),
 +                    _ => {},
 +                },
 +                ("drain", [arg]) => {
 +                    iter_with_drain::check(cx, expr, recv, span, arg);
 +                },
++                ("ends_with", [arg]) => {
++                    if let ExprKind::MethodCall(_, _, span) = expr.kind {
++                        case_sensitive_file_extension_comparisons::check(cx, expr, span, recv, arg);
++                    }
++                },
 +                ("expect", [_]) => match method_call(recv) {
 +                    Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
 +                    Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span),
-                 ("get", [arg]) => get_last_with_len::check(cx, expr, recv, arg),
++                    _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests),
 +                },
++                ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests),
 +                ("extend", [arg]) => {
 +                    string_extend_chars::check(cx, expr, recv, arg);
 +                    extend_with_drain::check(cx, expr, recv, arg);
 +                },
 +                ("filter_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                    filter_map_identity::check(cx, expr, arg, span);
 +                },
 +                ("find_map", [arg]) => {
 +                    unnecessary_filter_map::check(cx, expr, arg, name);
 +                },
 +                ("flat_map", [arg]) => {
 +                    flat_map_identity::check(cx, expr, arg, span);
 +                    flat_map_option::check(cx, expr, arg, span);
 +                },
 +                ("flatten", []) => match method_call(recv) {
 +                    Some(("map", [recv, map_arg], map_span)) => map_flatten::check(cx, expr, recv, map_arg, map_span),
 +                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, true),
 +                    _ => {},
 +                },
 +                ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span),
 +                ("for_each", [_]) => {
 +                    if let Some(("inspect", [_, _], span2)) = method_call(recv) {
 +                        inspect_for_each::check(cx, expr, span2);
 +                    }
 +                },
-                 ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
++                ("get", [arg]) => {
++                    get_first::check(cx, expr, recv, arg);
++                    get_last_with_len::check(cx, expr, recv, arg);
++                },
 +                ("get_or_insert_with", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"),
++                ("hash", [arg]) => {
++                    unit_hash::check(cx, expr, recv, arg);
++                },
 +                ("is_file", []) => filetype_is_file::check(cx, expr, recv),
 +                ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv),
 +                ("is_none", []) => check_is_some_is_none(cx, expr, recv, false),
 +                ("is_some", []) => check_is_some_is_none(cx, expr, recv, true),
++                ("iter" | "iter_mut" | "into_iter", []) => {
++                    iter_on_single_or_empty_collections::check(cx, expr, name, recv);
++                },
 +                ("join", [join_arg]) => {
 +                    if let Some(("collect", _, span)) = method_call(recv) {
 +                        unnecessary_join::check(cx, expr, recv, join_arg, span);
 +                    }
 +                },
 +                ("last", []) | ("skip", [_]) => {
 +                    if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
++                ("lock", []) => {
++                    mut_mutex_lock::check(cx, expr, recv, span);
++                },
 +                (name @ ("map" | "map_err"), [m_arg]) => {
++                    if name == "map" {
++                        map_clone::check(cx, expr, recv, m_arg, self.msrv);
++                    } else {
++                        map_err_ignore::check(cx, expr, m_arg);
++                    }
 +                    if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
 +                        match (name, args) {
 +                            ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, self.msrv),
 +                            ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv),
 +                            ("filter", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false);
 +                            },
 +                            ("find", [f_arg]) => {
 +                                filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, true);
 +                            },
 +                            _ => {},
 +                        }
 +                    }
 +                    map_identity::check(cx, expr, recv, m_arg, name, span);
 +                },
-                 ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
++                ("map_or", [def, map]) => {
++                    option_map_or_none::check(cx, expr, recv, def, map);
++                    manual_ok_or::check(cx, expr, recv, def, map);
++                },
 +                ("next", []) => {
 +                    if let Some((name2, [recv2, args2 @ ..], _)) = method_call(recv) {
 +                        match (name2, args2) {
 +                            ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                            ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg),
 +                            ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv),
 +                            ("iter", []) => iter_next_slice::check(cx, expr, recv2),
 +                            ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg),
 +                            ("skip_while", [_]) => skip_while_next::check(cx, expr),
 +                            _ => {},
 +                        }
 +                    }
 +                },
 +                ("nth", [n_arg]) => match method_call(recv) {
 +                    Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg),
 +                    Some(("cloned", [recv2], _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false),
 +                    Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false),
 +                    Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true),
 +                    _ => iter_nth_zero::check(cx, expr, recv, n_arg),
 +                },
 +                ("ok_or_else", [arg]) => unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"),
++                ("open", [_]) => {
++                    open_options::check(cx, expr, recv);
++                },
 +                ("or_else", [arg]) => {
 +                    if !bind_instead_of_map::ResultOrElseErrInfo::check(cx, expr, recv, arg) {
 +                        unnecessary_lazy_eval::check(cx, expr, recv, arg, "or");
 +                    }
 +                },
++                ("push", [arg]) => {
++                    path_buf_push_overwrite::check(cx, expr, arg);
++                },
++                ("read_to_end", [_]) => {
++                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_END_MSG);
++                },
++                ("read_to_string", [_]) => {
++                    verbose_file_reads::check(cx, expr, recv, verbose_file_reads::READ_TO_STRING_MSG);
++                },
++                ("repeat", [arg]) => {
++                    repeat_once::check(cx, expr, recv, arg);
++                },
++                (name @ ("replace" | "replacen"), [arg1, arg2] | [arg1, arg2, _]) => {
++                    no_effect_replace::check(cx, expr, arg1, arg2);
++
++                    // Check for repeated `str::replace` calls to perform `collapsible_str_replace` lint
++                    if name == "replace" && let Some(("replace", ..)) = method_call(recv) {
++                        collapsible_str_replace::check(cx, expr, arg1, arg2);
++                    }
++                },
++                ("resize", [count_arg, default_arg]) => {
++                    vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
++                },
++                ("sort", []) => {
++                    stable_sort_primitive::check(cx, expr, recv);
++                },
++                ("sort_by", [arg]) => {
++                    unnecessary_sort_by::check(cx, expr, recv, arg, false);
++                },
++                ("sort_unstable_by", [arg]) => {
++                    unnecessary_sort_by::check(cx, expr, recv, arg, true);
++                },
 +                ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                        str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv);
 +                    }
 +                },
 +                ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
 +                    if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) {
 +                        suspicious_splitn::check(cx, name, expr, recv, count);
 +                    }
 +                },
 +                ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
 +                ("take", [_arg]) => {
 +                    if let Some((name2, [recv2, args2 @ ..], _span2)) = method_call(recv) {
 +                        if let ("cloned", []) = (name2, args2) {
 +                            iter_overeager_cloned::check(cx, expr, recv, recv2, false, false);
 +                        }
 +                    }
 +                },
 +                ("take", []) => needless_option_take::check(cx, expr, recv),
 +                ("then", [arg]) => {
 +                    if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
 +                        return;
 +                    }
 +                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
 +                },
-                     unwrap_used::check(cx, expr, recv, self.allow_unwrap_in_tests);
++                ("to_owned", []) => {
++                    if !suspicious_to_owned::check(cx, expr, recv) {
++                        implicit_clone::check(cx, name, expr, recv);
++                    }
++                },
++                ("to_os_string" | "to_path_buf" | "to_vec", []) => {
 +                    implicit_clone::check(cx, name, expr, recv);
 +                },
 +                ("unwrap", []) => {
 +                    match method_call(recv) {
 +                        Some(("get", [recv, get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, false);
 +                        },
 +                        Some(("get_mut", [recv, get_arg], _)) => {
 +                            get_unwrap::check(cx, expr, recv, get_arg, true);
 +                        },
 +                        Some(("or", [recv, or_arg], or_span)) => {
 +                            or_then_unwrap::check(cx, expr, recv, or_arg, or_span);
 +                        },
 +                        _ => {},
 +                    }
-                 ("replace" | "replacen", [arg1, arg2] | [arg1, arg2, _]) => {
-                     no_effect_replace::check(cx, expr, arg1, arg2);
++                    unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests);
 +                },
++                ("unwrap_err", []) => unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests),
 +                ("unwrap_or", [u_arg]) => match method_call(recv) {
 +                    Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => {
 +                        manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]);
 +                    },
 +                    Some(("map", [m_recv, m_arg], span)) => {
 +                        option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span);
 +                    },
 +                    Some(("then_some", [t_recv, t_arg], _)) => {
 +                        obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg);
 +                    },
 +                    _ => {},
 +                },
 +                ("unwrap_or_else", [u_arg]) => match method_call(recv) {
 +                    Some(("map", [recv, map_arg], _))
 +                        if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, self.msrv) => {},
 +                    _ => {
 +                        unwrap_or_else_default::check(cx, expr, recv, u_arg);
 +                        unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or");
 +                    },
 +                },
++                ("zip", [arg]) => {
++                    if let ExprKind::MethodCall(name, [iter_recv], _) = recv.kind
++                        && name.ident.name == sym::iter
++                    {
++                        range_zip_with_len::check(cx, expr, iter_recv, arg);
++                    }
 +                },
 +                _ => {},
 +            }
 +        }
 +    }
 +}
 +
 +fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) {
 +    if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) {
 +        search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span);
 +    }
 +}
 +
 +/// Used for `lint_binary_expr_with_method_call`.
 +#[derive(Copy, Clone)]
 +struct BinaryExprInfo<'a> {
 +    expr: &'a hir::Expr<'a>,
 +    chain: &'a hir::Expr<'a>,
 +    other: &'a hir::Expr<'a>,
 +    eq: bool,
 +}
 +
 +/// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 +fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
 +    macro_rules! lint_with_both_lhs_and_rhs {
 +        ($func:expr, $cx:expr, $info:ident) => {
 +            if !$func($cx, $info) {
 +                ::std::mem::swap(&mut $info.chain, &mut $info.other);
 +                if $func($cx, $info) {
 +                    return;
 +                }
 +            }
 +        };
 +    }
 +
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
 +    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 +}
 +
 +const FN_HEADER: hir::FnHeader = hir::FnHeader {
 +    unsafety: hir::Unsafety::Normal,
 +    constness: hir::Constness::NotConst,
 +    asyncness: hir::IsAsync::NotAsync,
 +    abi: rustc_target::spec::abi::Abi::Rust,
 +};
 +
 +struct ShouldImplTraitCase {
 +    trait_name: &'static str,
 +    method_name: &'static str,
 +    param_count: usize,
 +    fn_header: hir::FnHeader,
 +    // implicit self kind expected (none, self, &self, ...)
 +    self_kind: SelfKind,
 +    // checks against the output type
 +    output_type: OutType,
 +    // certain methods with explicit lifetimes can't implement the equivalent trait method
 +    lint_explicit_lifetime: bool,
 +}
 +impl ShouldImplTraitCase {
 +    const fn new(
 +        trait_name: &'static str,
 +        method_name: &'static str,
 +        param_count: usize,
 +        fn_header: hir::FnHeader,
 +        self_kind: SelfKind,
 +        output_type: OutType,
 +        lint_explicit_lifetime: bool,
 +    ) -> ShouldImplTraitCase {
 +        ShouldImplTraitCase {
 +            trait_name,
 +            method_name,
 +            param_count,
 +            fn_header,
 +            self_kind,
 +            output_type,
 +            lint_explicit_lifetime,
 +        }
 +    }
 +
 +    fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool {
 +        self.lint_explicit_lifetime
 +            || !impl_item.generics.params.iter().any(|p| {
 +                matches!(
 +                    p.kind,
 +                    hir::GenericParamKind::Lifetime {
 +                        kind: hir::LifetimeParamKind::Explicit
 +                    }
 +                )
 +            })
 +    }
 +}
 +
 +#[rustfmt::skip]
 +const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
 +    ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::convert::AsRef", "as_ref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::BitAnd", "bitand",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitOr", "bitor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::BitXor", "bitxor",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::borrow::Borrow", "borrow",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::borrow::BorrowMut", "borrow_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::clone::Clone", "clone",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::cmp::Ord", "cmp",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Any, true),
 +    // FIXME: default doesn't work
 +    ShouldImplTraitCase::new("std::default::Default", "default",  0,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Deref", "deref",  1,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::DerefMut", "deref_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::Div", "div",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Drop", "drop",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::cmp::PartialEq", "eq",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Bool, true),
 +    ShouldImplTraitCase::new("std::iter::FromIterator", "from_iter",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::str::FromStr", "from_str",  1,  FN_HEADER,  SelfKind::No,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::hash::Hash", "hash",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Unit, true),
 +    ShouldImplTraitCase::new("std::ops::Index", "index",  2,  FN_HEADER,  SelfKind::Ref,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::ops::IndexMut", "index_mut",  2,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
 +    ShouldImplTraitCase::new("std::iter::IntoIterator", "into_iter",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Mul", "mul",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Neg", "neg",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::iter::Iterator", "next",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Any, false),
 +    ShouldImplTraitCase::new("std::ops::Not", "not",  1,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Rem", "rem",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shl", "shl",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Shr", "shr",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +    ShouldImplTraitCase::new("std::ops::Sub", "sub",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
 +];
 +
 +#[derive(Clone, Copy, PartialEq, Eq, Debug)]
 +enum SelfKind {
 +    Value,
 +    Ref,
 +    RefMut,
 +    No,
 +}
 +
 +impl SelfKind {
 +    fn matches<'a>(self, cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +        fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if ty == parent_ty {
 +                true
 +            } else if ty.is_box() {
 +                ty.boxed_ty() == parent_ty
 +            } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
 +                if let ty::Adt(_, substs) = ty.kind() {
 +                    substs.types().next().map_or(false, |t| t == parent_ty)
 +                } else {
 +                    false
 +                }
 +            } else {
 +                false
 +            }
 +        }
 +
 +        fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            if let ty::Ref(_, t, m) = *ty.kind() {
 +                return m == mutability && t == parent_ty;
 +            }
 +
 +            let trait_path = match mutability {
 +                hir::Mutability::Not => &paths::ASREF_TRAIT,
 +                hir::Mutability::Mut => &paths::ASMUT_TRAIT,
 +            };
 +
 +            let trait_def_id = match get_trait_def_id(cx, trait_path) {
 +                Some(did) => did,
 +                None => return false,
 +            };
 +            implements_trait(cx, ty, trait_def_id, &[parent_ty.into()])
 +        }
 +
 +        fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
 +            !matches_value(cx, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty)
 +                && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty)
 +        }
 +
 +        match self {
 +            Self::Value => matches_value(cx, parent_ty, ty),
 +            Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty),
 +            Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty),
 +            Self::No => matches_none(cx, parent_ty, ty),
 +        }
 +    }
 +
 +    #[must_use]
 +    fn description(self) -> &'static str {
 +        match self {
 +            Self::Value => "`self` by value",
 +            Self::Ref => "`self` by reference",
 +            Self::RefMut => "`self` by mutable reference",
 +            Self::No => "no `self`",
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Copy)]
 +enum OutType {
 +    Unit,
 +    Bool,
 +    Any,
 +    Ref,
 +}
 +
 +impl OutType {
 +    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
 +        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
 +        match (self, ty) {
 +            (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
 +            (Self::Unit, &hir::FnRetTy::Return(ty)) if is_unit(ty) => true,
 +            (Self::Bool, &hir::FnRetTy::Return(ty)) if is_bool(ty) => true,
 +            (Self::Any, &hir::FnRetTy::Return(ty)) if !is_unit(ty) => true,
 +            (Self::Ref, &hir::FnRetTy::Return(ty)) => matches!(ty.kind, hir::TyKind::Rptr(_, _)),
 +            _ => false,
 +        }
 +    }
 +}
 +
 +fn is_bool(ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        matches!(path.res, Res::PrimTy(PrimTy::Bool))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn fn_header_equals(expected: hir::FnHeader, actual: hir::FnHeader) -> bool {
 +    expected.constness == actual.constness
 +        && expected.unsafety == actual.unsafety
 +        && expected.asyncness == actual.asyncness
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd8458a222e2922e1d68c3d26e9b37fc0f084d20
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,30 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::is_type_diagnostic_item;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, Mutability};
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++use rustc_span::{sym, Span};
++
++use super::MUT_MUTEX_LOCK;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
++    if_chain! {
++        if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
++        then {
++            span_lint_and_sugg(
++                cx,
++                MUT_MUTEX_LOCK,
++                name_span,
++                "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
++                "change this to",
++                "get_mut".to_owned(),
++                Applicability::MaybeIncorrect,
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c3112823e3469eeb576f601f8ed71f31a19b5dd9
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,178 @@@
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::paths;
++use clippy_utils::ty::match_type;
++use rustc_ast::ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_span::source_map::{Span, Spanned};
++
++use super::NONSENSICAL_OPEN_OPTIONS;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
++    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
++        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
++        && match_type(cx, cx.tcx.type_of(impl_id), &paths::OPEN_OPTIONS)
++    {
++        let mut options = Vec::new();
++        get_open_options(cx, recv, &mut options);
++        check_open_options(cx, &options, e.span);
++    }
++}
++
++#[derive(Debug, PartialEq, Eq, Clone, Copy)]
++enum Argument {
++    True,
++    False,
++    Unknown,
++}
++
++#[derive(Debug)]
++enum OpenOption {
++    Write,
++    Read,
++    Truncate,
++    Create,
++    Append,
++}
++
++fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec<(OpenOption, Argument)>) {
++    if let ExprKind::MethodCall(path, arguments, _) = argument.kind {
++        let obj_ty = cx.typeck_results().expr_ty(&arguments[0]).peel_refs();
++
++        // Only proceed if this is a call on some object of type std::fs::OpenOptions
++        if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 2 {
++            let argument_option = match arguments[1].kind {
++                ExprKind::Lit(ref span) => {
++                    if let Spanned {
++                        node: LitKind::Bool(lit),
++                        ..
++                    } = *span
++                    {
++                        if lit { Argument::True } else { Argument::False }
++                    } else {
++                        // The function is called with a literal which is not a boolean literal.
++                        // This is theoretically possible, but not very likely.
++                        return;
++                    }
++                },
++                _ => Argument::Unknown,
++            };
++
++            match path.ident.as_str() {
++                "create" => {
++                    options.push((OpenOption::Create, argument_option));
++                },
++                "append" => {
++                    options.push((OpenOption::Append, argument_option));
++                },
++                "truncate" => {
++                    options.push((OpenOption::Truncate, argument_option));
++                },
++                "read" => {
++                    options.push((OpenOption::Read, argument_option));
++                },
++                "write" => {
++                    options.push((OpenOption::Write, argument_option));
++                },
++                _ => (),
++            }
++
++            get_open_options(cx, &arguments[0], options);
++        }
++    }
++}
++
++fn check_open_options(cx: &LateContext<'_>, options: &[(OpenOption, Argument)], span: Span) {
++    let (mut create, mut append, mut truncate, mut read, mut write) = (false, false, false, false, false);
++    let (mut create_arg, mut append_arg, mut truncate_arg, mut read_arg, mut write_arg) =
++        (false, false, false, false, false);
++    // This code is almost duplicated (oh, the irony), but I haven't found a way to
++    // unify it.
++
++    for option in options {
++        match *option {
++            (OpenOption::Create, arg) => {
++                if create {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `create` is called more than once",
++                    );
++                } else {
++                    create = true;
++                }
++                create_arg = create_arg || (arg == Argument::True);
++            },
++            (OpenOption::Append, arg) => {
++                if append {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `append` is called more than once",
++                    );
++                } else {
++                    append = true;
++                }
++                append_arg = append_arg || (arg == Argument::True);
++            },
++            (OpenOption::Truncate, arg) => {
++                if truncate {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `truncate` is called more than once",
++                    );
++                } else {
++                    truncate = true;
++                }
++                truncate_arg = truncate_arg || (arg == Argument::True);
++            },
++            (OpenOption::Read, arg) => {
++                if read {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `read` is called more than once",
++                    );
++                } else {
++                    read = true;
++                }
++                read_arg = read_arg || (arg == Argument::True);
++            },
++            (OpenOption::Write, arg) => {
++                if write {
++                    span_lint(
++                        cx,
++                        NONSENSICAL_OPEN_OPTIONS,
++                        span,
++                        "the method `write` is called more than once",
++                    );
++                } else {
++                    write = true;
++                }
++                write_arg = write_arg || (arg == Argument::True);
++            },
++        }
++    }
++
++    if read && truncate && read_arg && truncate_arg && !(write && write_arg) {
++        span_lint(
++            cx,
++            NONSENSICAL_OPEN_OPTIONS,
++            span,
++            "file opened with `truncate` and `read`",
++        );
++    }
++    if append && truncate && append_arg && truncate_arg {
++        span_lint(
++            cx,
++            NONSENSICAL_OPEN_OPTIONS,
++            span,
++            "file opened with `append` and `truncate`",
++        );
++    }
++}
index 6c641af59f92b9c6a3f1b6941c01305f8b8bd464,0000000000000000000000000000000000000000..3c4002a3aef99b86fc175c86b343c9530cfac29e
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,139 @@@
-                 (expr.span.with_lo(unwrap_recv.span.hi()), String::from("")),
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_copy;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use rustc_data_structures::fx::FxHashSet;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_path, Visitor};
 +use rustc_hir::{self, HirId, Path};
 +use rustc_lint::LateContext;
 +use rustc_middle::hir::nested_filter;
 +use rustc_span::source_map::Span;
 +use rustc_span::{sym, Symbol};
 +
 +use super::MAP_UNWRAP_OR;
 +
 +/// lint use of `map().unwrap_or()` for `Option`s
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &rustc_hir::Expr<'_>,
 +    recv: &rustc_hir::Expr<'_>,
 +    map_arg: &'tcx rustc_hir::Expr<'_>,
 +    unwrap_recv: &rustc_hir::Expr<'_>,
 +    unwrap_arg: &'tcx rustc_hir::Expr<'_>,
 +    map_span: Span,
 +) {
 +    // lint if the caller of `map()` is an `Option`
 +    if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option) {
 +        if !is_copy(cx, cx.typeck_results().expr_ty(unwrap_arg)) {
 +            // Do not lint if the `map` argument uses identifiers in the `map`
 +            // argument that are also used in the `unwrap_or` argument
 +
 +            let mut unwrap_visitor = UnwrapVisitor {
 +                cx,
 +                identifiers: FxHashSet::default(),
 +            };
 +            unwrap_visitor.visit_expr(unwrap_arg);
 +
 +            let mut map_expr_visitor = MapExprVisitor {
 +                cx,
 +                identifiers: unwrap_visitor.identifiers,
 +                found_identifier: false,
 +            };
 +            map_expr_visitor.visit_expr(map_arg);
 +
 +            if map_expr_visitor.found_identifier {
 +                return;
 +            }
 +        }
 +
 +        if unwrap_arg.span.ctxt() != map_span.ctxt() {
 +            return;
 +        }
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +        // get snippet for unwrap_or()
 +        let unwrap_snippet = snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability);
 +        // lint message
 +        // comparing the snippet from source to raw text ("None") below is safe
 +        // because we already have checked the type.
 +        let arg = if unwrap_snippet == "None" { "None" } else { "<a>" };
 +        let unwrap_snippet_none = unwrap_snippet == "None";
 +        let suggest = if unwrap_snippet_none {
 +            "and_then(<f>)"
 +        } else {
 +            "map_or(<a>, <f>)"
 +        };
 +        let msg = &format!(
 +            "called `map(<f>).unwrap_or({})` on an `Option` value. \
 +            This can be done more directly by calling `{}` instead",
 +            arg, suggest
 +        );
 +
 +        span_lint_and_then(cx, MAP_UNWRAP_OR, expr.span, msg, |diag| {
 +            let map_arg_span = map_arg.span;
 +
 +            let mut suggestion = vec![
 +                (
 +                    map_span,
 +                    String::from(if unwrap_snippet_none { "and_then" } else { "map_or" }),
 +                ),
++                (expr.span.with_lo(unwrap_recv.span.hi()), String::new()),
 +            ];
 +
 +            if !unwrap_snippet_none {
 +                suggestion.push((map_arg_span.with_hi(map_arg_span.lo()), format!("{}, ", unwrap_snippet)));
 +            }
 +
 +            diag.multipart_suggestion(&format!("use `{}` instead", suggest), suggestion, applicability);
 +        });
 +    }
 +}
 +
 +struct UnwrapVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    identifiers: FxHashSet<Symbol>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for UnwrapVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
 +        self.identifiers.insert(ident(path));
 +        walk_path(self, path);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +struct MapExprVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    identifiers: FxHashSet<Symbol>,
 +    found_identifier: bool,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for MapExprVisitor<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _id: HirId) {
 +        if self.identifiers.contains(&ident(path)) {
 +            self.found_identifier = true;
 +            return;
 +        }
 +        walk_path(self, path);
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +fn ident(path: &Path<'_>) -> Symbol {
 +    path.segments
 +        .last()
 +        .expect("segments should be composed of at least 1 element")
 +        .ident
 +        .name
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0cc28c0dcb3d044c7b8a0d0ca86cf637fabbe306
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,37 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::ty::is_type_diagnostic_item;
++use if_chain::if_chain;
++use rustc_ast::ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_span::symbol::sym;
++use std::path::{Component, Path};
++
++use super::PATH_BUF_PUSH_OVERWRITE;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::PathBuf);
++        if let ExprKind::Lit(ref lit) = arg.kind;
++        if let LitKind::Str(ref path_lit, _) = lit.node;
++        if let pushed_path = Path::new(path_lit.as_str());
++        if let Some(pushed_path_lit) = pushed_path.to_str();
++        if pushed_path.has_root();
++        if let Some(root) = pushed_path.components().next();
++        if root == Component::RootDir;
++        then {
++            span_lint_and_sugg(
++                cx,
++                PATH_BUF_PUSH_OVERWRITE,
++                lit.span,
++                "calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
++                "try",
++                format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..00a2a0d14d1132946ef80749581d3e0e12644ea8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,34 @@@
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::source::snippet;
++use clippy_utils::{higher, SpanlessEq};
++use clippy_utils::{is_integer_const, is_trait_method};
++use if_chain::if_chain;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::RANGE_ZIP_WITH_LEN;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, zip_arg: &'tcx Expr<'_>) {
++    if_chain! {
++        if is_trait_method(cx, expr, sym::Iterator);
++        // range expression in `.zip()` call: `0..x.len()`
++        if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
++        if is_integer_const(cx, start, 0);
++        // `.len()` call
++        if let ExprKind::MethodCall(len_path, [len_recv], _) = end.kind;
++        if len_path.ident.name == sym::len;
++        // `.iter()` and `.len()` called on same `Path`
++        if let ExprKind::Path(QPath::Resolved(_, iter_path)) = recv.kind;
++        if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_recv.kind;
++        if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
++        then {
++            span_lint(cx,
++                RANGE_ZIP_WITH_LEN,
++                expr.span,
++                &format!("it is more idiomatic to use `{}.iter().enumerate()`",
++                    snippet(cx, recv.span, "_"))
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0a14f9216ab383f44f197a25820471a84c09f29f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,52 @@@
++use clippy_utils::consts::{constant_context, Constant};
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::source::snippet;
++use clippy_utils::ty::is_type_diagnostic_item;
++use rustc_errors::Applicability;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::REPEAT_ONCE;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    recv: &'tcx Expr<'_>,
++    repeat_arg: &'tcx Expr<'_>,
++) {
++    if constant_context(cx, cx.typeck_results()).expr(repeat_arg) == Some(Constant::Int(1)) {
++        let ty = cx.typeck_results().expr_ty(recv).peel_refs();
++        if ty.is_str() {
++            span_lint_and_sugg(
++                cx,
++                REPEAT_ONCE,
++                expr.span,
++                "calling `repeat(1)` on str",
++                "consider using `.to_string()` instead",
++                format!("{}.to_string()", snippet(cx, recv.span, r#""...""#)),
++                Applicability::MachineApplicable,
++            );
++        } else if ty.builtin_index().is_some() {
++            span_lint_and_sugg(
++                cx,
++                REPEAT_ONCE,
++                expr.span,
++                "calling `repeat(1)` on slice",
++                "consider using `.to_vec()` instead",
++                format!("{}.to_vec()", snippet(cx, recv.span, r#""...""#)),
++                Applicability::MachineApplicable,
++            );
++        } else if is_type_diagnostic_item(cx, ty, sym::String) {
++            span_lint_and_sugg(
++                cx,
++                REPEAT_ONCE,
++                expr.span,
++                "calling `repeat(1)` on a string literal",
++                "consider using `.clone()` instead",
++                format!("{}.clone()", snippet(cx, recv.span, r#""...""#)),
++                Applicability::MachineApplicable,
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91951c65bb3095aebbeefb7fb105b35f53c05047
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,31 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::is_slice_of_primitives;
++use clippy_utils::source::snippet_with_context;
++use rustc_errors::Applicability;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++
++use super::STABLE_SORT_PRIMITIVE;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
++    if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
++        && let Some(impl_id) = cx.tcx.impl_of_method(method_id)
++        && cx.tcx.type_of(impl_id).is_slice()
++        && let Some(slice_type) = is_slice_of_primitives(cx, recv)
++    {
++        span_lint_and_then(
++            cx,
++            STABLE_SORT_PRIMITIVE,
++            e.span,
++            &format!("used `sort` on primitive type `{}`", slice_type),
++            |diag| {
++                let mut app = Applicability::MachineApplicable;
++                let recv_snip = snippet_with_context(cx, recv.span, e.span.ctxt(), "..", &mut app).0;
++                diag.span_suggestion(e.span, "try", format!("{}.sort_unstable()", recv_snip), app);
++                diag.note(
++                    "an unstable sort typically performs faster without any observable difference for this data type",
++                );
++            },
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6b306fbf0085541a3bad26b1ecab72a036a65be1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,36 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::is_diag_trait_item;
++use clippy_utils::source::snippet_with_context;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir as hir;
++use rustc_lint::LateContext;
++use rustc_middle::ty;
++use rustc_span::sym;
++
++use super::SUSPICIOUS_TO_OWNED;
++
++pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -> bool {
++    if_chain! {
++        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if is_diag_trait_item(cx, method_def_id, sym::ToOwned);
++        let input_type = cx.typeck_results().expr_ty(expr);
++        if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind();
++        if cx.tcx.is_diagnostic_item(sym::Cow, adt.did());
++        then {
++            let mut app = Applicability::MaybeIncorrect;
++            let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
++            span_lint_and_sugg(
++                cx,
++                SUSPICIOUS_TO_OWNED,
++                expr.span,
++                &format!("this `to_owned` call clones the {0} itself and does not cause the {0} contents to become owned", input_type),
++                "consider using, depending on intent",
++                format!("{0}.clone()` or `{0}.into_owned()", recv_snip),
++                app,
++            );
++            return true;
++        }
++    }
++    false
++}
index 77d21f1d3730c66afeb7ac64f42ea735dd1e611e,0000000000000000000000000000000000000000..a1c6294737cf87225631efca472c0ed5f03e93cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,26 @@@
- use clippy_utils::{is_expr_diagnostic_item, ty::is_uninit_value_valid_for_ty};
 +use clippy_utils::diagnostics::span_lint;
-         if is_expr_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
++use clippy_utils::{is_path_diagnostic_item, ty::is_uninit_value_valid_for_ty};
 +use if_chain::if_chain;
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::UNINIT_ASSUMED_INIT;
 +
 +/// lint for `MaybeUninit::uninit().assume_init()` (we already have the latter)
 +pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
 +    if_chain! {
 +        if let hir::ExprKind::Call(callee, args) = recv.kind;
 +        if args.is_empty();
++        if is_path_diagnostic_item(cx, callee, sym::maybe_uninit_uninit);
 +        if !is_uninit_value_valid_for_ty(cx, cx.typeck_results().expr_ty_adjusted(expr));
 +        then {
 +            span_lint(
 +                cx,
 +                UNINIT_ASSUMED_INIT,
 +                expr.span,
 +                "this call for this type may be undefined behavior"
 +            );
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3c7955bc46981bae4c0d989d3d9c5cbbf5c715ea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::is_trait_method;
++use clippy_utils::source::snippet;
++use rustc_errors::Applicability;
++use rustc_hir::Expr;
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::UNIT_HASH;
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
++    if is_trait_method(cx, expr, sym::Hash) && cx.typeck_results().expr_ty(recv).is_unit() {
++        span_lint_and_then(
++            cx,
++            UNIT_HASH,
++            expr.span,
++            "this call to `hash` on the unit type will do nothing",
++            |diag| {
++                diag.span_suggestion(
++                    expr.span,
++                    "remove the call to `hash` or consider using",
++                    format!("0_u8.hash({})", snippet(cx, arg.span, ".."),),
++                    Applicability::MaybeIncorrect,
++                );
++                diag.note("the implementation of `Hash` for `()` is a no-op");
++            },
++        );
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1966990bd774f929a37551329aff18832c326c17
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,224 @@@
++use clippy_utils::diagnostics::span_lint_and_sugg;
++use clippy_utils::is_trait_method;
++use clippy_utils::sugg::Sugg;
++use clippy_utils::ty::implements_trait;
++use if_chain::if_chain;
++use rustc_errors::Applicability;
++use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
++use rustc_lint::LateContext;
++use rustc_middle::ty::{self, subst::GenericArgKind};
++use rustc_span::sym;
++use rustc_span::symbol::Ident;
++use std::iter;
++
++use super::UNNECESSARY_SORT_BY;
++
++enum LintTrigger {
++    Sort(SortDetection),
++    SortByKey(SortByKeyDetection),
++}
++
++struct SortDetection {
++    vec_name: String,
++}
++
++struct SortByKeyDetection {
++    vec_name: String,
++    closure_arg: String,
++    closure_body: String,
++    reverse: bool,
++}
++
++/// Detect if the two expressions are mirrored (identical, except one
++/// contains a and the other replaces it with b)
++fn mirrored_exprs(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(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(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(left_expr, a_ident, right_expr, b_ident)
++                && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(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(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(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(left_left, a_ident, right_left, b_ident)
++                && mirrored_exprs(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(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(left, a_ident, right, b_ident),
++        (ExprKind::DropTemps(left_block), ExprKind::DropTemps(right_block)) => {
++            mirrored_exprs(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(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(left_expr, a_ident, right_expr, b_ident),
++        (_, ExprKind::AddrOf(_, Mutability::Not, right_expr)) => mirrored_exprs(a_expr, a_ident, right_expr, b_ident),
++        (ExprKind::AddrOf(_, Mutability::Not, left_expr), _) => mirrored_exprs(left_expr, a_ident, b_expr, b_ident),
++        _ => false,
++    }
++}
++
++fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) -> Option<LintTrigger> {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if cx.tcx.type_of(impl_id).is_slice();
++        if let ExprKind::Closure(&Closure { body, .. }) = arg.kind;
++        if let closure_body = cx.tcx.hir().body(body);
++        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, [left_expr, right_expr], _) = closure_body.value.kind;
++        if method_path.ident.name == sym::cmp;
++        if is_trait_method(cx, &closure_body.value, sym::Ord);
++        then {
++            let (closure_body, closure_arg, reverse) = if mirrored_exprs(
++                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(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, recv, "..").to_string();
++
++            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 }));
++                }
++            }
++
++            if !expr_borrows(cx, left_expr) {
++                return Some(LintTrigger::SortByKey(SortByKeyDetection {
++                    vec_name,
++                    closure_arg,
++                    closure_body,
++                    reverse,
++                }));
++            }
++        }
++    }
++
++    None
++}
++
++fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
++    let ty = cx.typeck_results().expr_ty(expr);
++    matches!(ty.kind(), ty::Ref(..)) || ty.walk().any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
++}
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    recv: &'tcx Expr<'_>,
++    arg: &'tcx Expr<'_>,
++    is_unstable: bool,
++) {
++    match detect_lint(cx, expr, recv, arg) {
++        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 is_unstable { "_unstable" } else { "" },
++                trigger.closure_arg,
++                if trigger.reverse {
++                    format!("std::cmp::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 is_unstable { "_unstable" } else { "" },
++            ),
++            Applicability::MachineApplicable,
++        ),
++        None => {},
++    }
++}
index b3276f1394ed2f7bcb0f4bcd3ad5bea8322717ce,0000000000000000000000000000000000000000..44bf84352943853bddfa26628e7515617a93e14f
mode 100644,000000..100644
--- /dev/null
@@@ -1,431 -1,0 +1,432 @@@
-     contains_ty, get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs,
 +use super::implicit_clone::is_clone_like;
 +use super::unnecessary_iter_cloned::{self, is_into_iter};
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::source::snippet_opt;
 +use clippy_utils::ty::{
- use clippy_utils::{meets_msrv, msrvs};
++    get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
 +};
-             implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
 +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
++use clippy_utils::{meets_msrv, msrvs};
 +use rustc_errors::Applicability;
 +use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::Mutability;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
 +use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
 +use rustc_semver::RustcVersion;
 +use rustc_span::{sym, Symbol};
 +use std::cmp::max;
 +
 +use super::UNNECESSARY_TO_OWNED;
 +
 +pub fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    args: &'tcx [Expr<'tcx>],
 +    msrv: Option<RustcVersion>,
 +) {
 +    if_chain! {
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        if let [receiver] = args;
 +        then {
 +            if is_cloned_or_copied(cx, method_name, method_def_id) {
 +                unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
 +            } else if is_to_owned_like(cx, method_name, method_def_id) {
 +                // At this point, we know the call is of a `to_owned`-like function. The functions
 +                // `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
 +                // based on its context, that is, whether it is a referent in an `AddrOf` expression, an
 +                // argument in a `into_iter` call, or an argument in the call of some other function.
 +                if check_addr_of_expr(cx, expr, method_name, method_def_id, receiver) {
 +                    return;
 +                }
 +                if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
 +                    return;
 +                }
 +                check_other_call_arg(cx, expr, method_name, receiver);
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks whether `expr` is a referent in an `AddrOf` expression and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +#[allow(clippy::too_many_lines)]
 +fn check_addr_of_expr(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    method_def_id: DefId,
 +    receiver: &Expr<'_>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind;
 +        let adjustments = cx.typeck_results().expr_adjustments(parent).iter().collect::<Vec<_>>();
 +        if let
 +            // For matching uses of `Cow::from`
 +            [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching uses of arrays
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Pointer(_),
 +                    target: target_ty,
 +                },
 +            ]
 +            // For matching everything else
 +            | [
 +                Adjustment {
 +                    kind: Adjust::Deref(None),
 +                    target: referent_ty,
 +                },
 +                Adjustment {
 +                    kind: Adjust::Deref(Some(OverloadedDeref { .. })),
 +                    ..
 +                },
 +                Adjustment {
 +                    kind: Adjust::Borrow(_),
 +                    target: target_ty,
 +                },
 +            ] = adjustments[..];
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty);
 +        let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty);
 +        // Only flag cases satisfying at least one of the following three conditions:
 +        // * the referent and receiver types are distinct
 +        // * the referent/receiver type is a copyable array
 +        // * the method is `Cow::into_owned`
 +        // This restriction is to ensure there is no overlap between `redundant_clone` and this
 +        // lint. It also avoids the following false positive:
 +        //  https://github.com/rust-lang/rust-clippy/issues/8759
 +        //   Arrays are a bit of a corner case. Non-copyable arrays are handled by
 +        // `redundant_clone`, but copyable arrays are not.
 +        if *referent_ty != receiver_ty
 +            || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
 +            || is_cow_into_owned(cx, method_name, method_def_id);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
 +                span_lint_and_sugg(
 +                    cx,
 +                    UNNECESSARY_TO_OWNED,
 +                    parent.span,
 +                    &format!("unnecessary use of `{}`", method_name),
 +                    "use",
 +                    format!(
 +                        "{:&>width$}{}",
 +                        "",
 +                        receiver_snippet,
 +                        width = n_target_refs - n_receiver_refs
 +                    ),
 +                    Applicability::MachineApplicable,
 +                );
 +                return true;
 +            }
 +            if_chain! {
 +                if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +                if implements_trait(cx, receiver_ty, deref_trait_id, &[]);
 +                if get_associated_type(cx, receiver_ty, deref_trait_id, "Target") == Some(target_ty);
 +                then {
 +                    if n_receiver_refs > 0 {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            parent.span,
 +                            &format!("unnecessary use of `{}`", method_name),
 +                            "use",
 +                            receiver_snippet,
 +                            Applicability::MachineApplicable,
 +                        );
 +                    } else {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_TO_OWNED,
 +                            expr.span.with_lo(receiver.span.hi()),
 +                            &format!("unnecessary use of `{}`", method_name),
 +                            "remove this",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    }
 +                    return true;
 +                }
 +            }
 +            if_chain! {
 +                if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +                if implements_trait(cx, receiver_ty, as_ref_trait_id, &[GenericArg::from(target_ty)]);
 +                then {
 +                    span_lint_and_sugg(
 +                        cx,
 +                        UNNECESSARY_TO_OWNED,
 +                        parent.span,
 +                        &format!("unnecessary use of `{}`", method_name),
 +                        "use",
 +                        format!("{}.as_ref()", receiver_snippet),
 +                        Applicability::MachineApplicable,
 +                    );
 +                    return true;
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 +/// call of a `to_owned`-like function is unnecessary.
 +fn check_into_iter_call_arg(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    method_name: Symbol,
 +    receiver: &Expr<'_>,
 +    msrv: Option<RustcVersion>,
 +) -> bool {
 +    if_chain! {
 +        if let Some(parent) = get_parent_expr(cx, expr);
 +        if let Some(callee_def_id) = fn_def_id(cx, parent);
 +        if is_into_iter(cx, callee_def_id);
 +        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 +        let parent_ty = cx.typeck_results().expr_ty(parent);
 +        if implements_trait(cx, parent_ty, iterator_trait_id, &[]);
 +        if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
 +                return true;
 +            }
 +            let cloned_or_copied = if is_copy(cx, item_ty) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
 +                "copied"
 +            } else {
 +                "cloned"
 +            };
 +            // The next suggestion may be incorrect because the removal of the `to_owned`-like
 +            // function could cause the iterator to hold a reference to a resource that is used
 +            // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148.
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                parent.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{}.iter().{}()", receiver_snippet, cloned_or_copied),
 +                Applicability::MaybeIncorrect,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks whether `expr` is an argument in a function call and, if so, determines whether its call
 +/// of a `to_owned`-like function is unnecessary.
 +fn check_other_call_arg<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    method_name: Symbol,
 +    receiver: &'tcx Expr<'tcx>,
 +) -> bool {
 +    if_chain! {
 +        if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
 +        if let Some((callee_def_id, call_substs, call_args)) = get_callee_substs_and_args(cx, maybe_call);
 +        let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
 +        if let Some(i) = call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id);
 +        if let Some(input) = fn_sig.inputs().get(i);
 +        let (input, n_refs) = peel_mid_ty_refs(*input);
 +        if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
 +        if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
 +        if let [trait_predicate] = trait_predicates
 +            .iter()
 +            .filter(|trait_predicate| trait_predicate.def_id() != sized_def_id)
 +            .collect::<Vec<_>>()[..];
 +        if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
 +        if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
 +        let receiver_ty = cx.typeck_results().expr_ty(receiver);
 +        // If the callee has type parameters, they could appear in `projection_predicate.ty` or the
 +        // types of `trait_predicate.trait_ref.substs`.
 +        if if trait_predicate.def_id() == deref_trait_id {
 +            if let [projection_predicate] = projection_predicates[..] {
 +                let normalized_ty =
 +                    cx.tcx
 +                        .subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
 +                implements_trait(cx, receiver_ty, deref_trait_id, &[])
 +                    && get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
 +                        .map_or(false, |ty| ty::Term::Ty(ty) == normalized_ty)
 +            } else {
 +                false
 +            }
 +        } else if trait_predicate.def_id() == as_ref_trait_id {
 +            let composed_substs = compose_substs(
 +                cx,
 +                &trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
 +                call_substs,
 +            );
-             || !contains_ty(fn_sig.output(), input);
++            // if `expr` is a `String` and generic target is [u8], skip
++            // (https://github.com/rust-lang/rust-clippy/issues/9317).
++            if let [subst] = composed_substs[..]
++                && let GenericArgKind::Type(arg_ty) = subst.unpack()
++                && arg_ty.is_slice()
++                && let inner_ty = arg_ty.builtin_index().unwrap()
++                && let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
++                && let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
++                && is_type_diagnostic_item(cx, self_ty, sym::String) {
++                false
++            } else {
++                implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
++            }
 +        } else {
 +            false
 +        };
 +        // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
 +        // `Target = T`.
 +        if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
 +        let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
 +        // If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
 +        // `T` must not be instantiated with a reference
 +        // (https://github.com/rust-lang/rust-clippy/issues/8507).
 +        if (n_refs == 0 && !receiver_ty.is_ref())
 +            || trait_predicate.def_id() != as_ref_trait_id
-     for (predicate, _) in cx.tcx.predicates_of(callee_def_id).predicates.iter() {
-         // `substs` should have 1 + n elements. The first is the type on the left hand side of an
-         // `as`. The remaining n are trait parameters.
-         let is_input_substs = |substs: SubstsRef<'tcx>| {
-             if_chain! {
-                 if let Some(arg) = substs.iter().next();
-                 if let GenericArgKind::Type(arg_ty) = arg.unpack();
-                 if arg_ty == input;
-                 then { true } else { false }
-             }
-         };
++            || !fn_sig.output().contains(input);
 +        if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
 +        then {
 +            span_lint_and_sugg(
 +                cx,
 +                UNNECESSARY_TO_OWNED,
 +                maybe_arg.span,
 +                &format!("unnecessary use of `{}`", method_name),
 +                "use",
 +                format!("{:&>width$}{}", "", receiver_snippet, width = n_refs),
 +                Applicability::MachineApplicable,
 +            );
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +/// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such
 +/// expression found (if any) along with the immediately prior expression.
 +fn skip_addr_of_ancestors<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    mut expr: &'tcx Expr<'tcx>,
 +) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
 +    while let Some(parent) = get_parent_expr(cx, expr) {
 +        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = parent.kind {
 +            expr = parent;
 +        } else {
 +            return Some((parent, expr));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether an expression is a function or method call and, if so, returns its `DefId`,
 +/// `Substs`, and arguments.
 +fn get_callee_substs_and_args<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> {
 +    if_chain! {
 +        if let ExprKind::Call(callee, args) = expr.kind;
 +        let callee_ty = cx.typeck_results().expr_ty(callee);
 +        if let ty::FnDef(callee_def_id, _) = callee_ty.kind();
 +        then {
 +            let substs = cx.typeck_results().node_substs(callee.hir_id);
 +            return Some((*callee_def_id, substs, args));
 +        }
 +    }
 +    if_chain! {
 +        if let ExprKind::MethodCall(_, args, _) = expr.kind;
 +        if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
 +        then {
 +            let substs = cx.typeck_results().node_substs(expr.hir_id);
 +            return Some((method_def_id, substs, args));
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type.
 +fn get_input_traits_and_projections<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    callee_def_id: DefId,
 +    input: Ty<'tcx>,
 +) -> (Vec<TraitPredicate<'tcx>>, Vec<ProjectionPredicate<'tcx>>) {
 +    let mut trait_predicates = Vec::new();
 +    let mut projection_predicates = Vec::new();
-                 if is_input_substs(trait_predicate.trait_ref.substs) {
++    for predicate in cx.tcx.param_env(callee_def_id).caller_bounds() {
 +        match predicate.kind().skip_binder() {
 +            PredicateKind::Trait(trait_predicate) => {
-                 if is_input_substs(projection_predicate.projection_ty.substs) {
++                if trait_predicate.trait_ref.self_ty() == input {
 +                    trait_predicates.push(trait_predicate);
 +                }
 +            },
 +            PredicateKind::Projection(projection_predicate) => {
++                if projection_predicate.projection_ty.self_ty() == input {
 +                    projection_predicates.push(projection_predicate);
 +                }
 +            },
 +            _ => {},
 +        }
 +    }
 +    (trait_predicates, projection_predicates)
 +}
 +
 +/// Composes two substitutions by applying the latter to the types of the former.
 +fn compose_substs<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    left: &[GenericArg<'tcx>],
 +    right: SubstsRef<'tcx>,
 +) -> Vec<GenericArg<'tcx>> {
 +    left.iter()
 +        .map(|arg| {
 +            if let GenericArgKind::Type(arg_ty) = arg.unpack() {
 +                let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
 +                GenericArg::from(normalized_ty)
 +            } else {
 +                *arg
 +            }
 +        })
 +        .collect()
 +}
 +
 +/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
 +fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    (method_name.as_str() == "cloned" || method_name.as_str() == "copied")
 +        && is_diag_trait_item(cx, method_def_id, sym::Iterator)
 +}
 +
 +/// Returns true if the named method can be used to convert the receiver to its "owned"
 +/// representation.
 +fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    is_clone_like(cx, method_name.as_str(), method_def_id)
 +        || is_cow_into_owned(cx, method_name, method_def_id)
 +        || is_to_string(cx, method_name, method_def_id)
 +}
 +
 +/// Returns true if the named method is `Cow::into_owned`.
 +fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
 +}
 +
 +/// Returns true if the named method is `ToString::to_string`.
 +fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
 +    method_name == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString)
 +}
index ce1a52e5480afb81226859153023754301935f84,0000000000000000000000000000000000000000..ee17f2d7889ee9f93b6f619bf0100d982a62ec3e
mode 100644,000000..100644
--- /dev/null
@@@ -1,45 -1,0 +1,53 @@@
- /// lint use of `unwrap()` for `Option`s and `Result`s
- pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_unwrap_in_tests: bool) {
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{is_in_test_function, is_lint_allowed};
 +use rustc_hir as hir;
 +use rustc_lint::LateContext;
 +use rustc_span::sym;
 +
 +use super::{EXPECT_USED, UNWRAP_USED};
 +
-     let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
++/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`.
++pub(super) fn check(
++    cx: &LateContext<'_>,
++    expr: &hir::Expr<'_>,
++    recv: &hir::Expr<'_>,
++    is_err: bool,
++    allow_unwrap_in_tests: bool,
++) {
 +    let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
 +
-         Some((UNWRAP_USED, "a Result", "Err", "an "))
++    let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
 +        Some((UNWRAP_USED, "an Option", "None", ""))
 +    } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
-                 using `expect()` to provide a better panic message"
++        Some((UNWRAP_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
 +    } else {
 +        None
 +    };
 +
++    let method_suffix = if is_err { "_err" } else { "" };
++
 +    if allow_unwrap_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
 +        return;
 +    }
 +
 +    if let Some((lint, kind, none_value, none_prefix)) = mess {
 +        let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) {
 +            format!(
 +                "if you don't want to handle the `{none_value}` case gracefully, consider \
-             &format!("used `unwrap()` on `{kind}` value"),
++                using `expect{method_suffix}()` to provide a better panic message"
 +            )
 +        } else {
 +            format!("if this value is {none_prefix}`{none_value}`, it will panic")
 +        };
 +
 +        span_lint_and_help(
 +            cx,
 +            lint,
 +            expr.span,
++            &format!("used `unwrap{method_suffix}()` on `{kind}` value"),
 +            None,
 +            &help,
 +        );
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..02d8364cb2959c17d1471a6c3479e6c26b7c7c4e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,45 @@@
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::ty::is_type_diagnostic_item;
++use if_chain::if_chain;
++use rustc_ast::LitKind;
++use rustc_errors::Applicability;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_span::source_map::Spanned;
++use rustc_span::{sym, Span};
++
++use super::VEC_RESIZE_TO_ZERO;
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    count_arg: &'tcx Expr<'_>,
++    default_arg: &'tcx Expr<'_>,
++    name_span: Span,
++) {
++    if_chain! {
++        if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
++        if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
++        if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Vec);
++        if let ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = count_arg.kind;
++        if let ExprKind::Lit(Spanned { node: LitKind::Int(..), .. }) = default_arg.kind;
++        then {
++            let method_call_span = expr.span.with_lo(name_span.lo());
++            span_lint_and_then(
++                cx,
++                VEC_RESIZE_TO_ZERO,
++                expr.span,
++                "emptying a vector with `resize`",
++                |db| {
++                    db.help("the arguments may be inverted...");
++                    db.span_suggestion(
++                        method_call_span,
++                        "...or you can empty the vector with",
++                        "clear()".to_string(),
++                        Applicability::MaybeIncorrect,
++                    );
++                },
++            );
++        }
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2fe5ae9a9ad8fda3a9c80f8143ac603764d1ad8b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,28 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::is_trait_method;
++use clippy_utils::ty::is_type_diagnostic_item;
++use rustc_hir::{Expr, ExprKind, QPath};
++use rustc_lint::LateContext;
++use rustc_span::sym;
++
++use super::VERBOSE_FILE_READS;
++
++pub(super) const READ_TO_END_MSG: (&str, &str) = ("use of `File::read_to_end`", "consider using `fs::read` instead");
++pub(super) const READ_TO_STRING_MSG: (&str, &str) = (
++    "use of `File::read_to_string`",
++    "consider using `fs::read_to_string` instead",
++);
++
++pub(super) fn check<'tcx>(
++    cx: &LateContext<'tcx>,
++    expr: &'tcx Expr<'_>,
++    recv: &'tcx Expr<'_>,
++    (msg, help): (&str, &str),
++) {
++    if is_trait_method(cx, expr, sym::IoRead)
++        && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
++        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
++    {
++        span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
++    }
++}
index df044538fe19d36b825bcd5d858c9833a6308961,0000000000000000000000000000000000000000..7c4ae746e90e91894f9c604dadc53254cb48fb52
mode 100644,000000..100644
--- /dev/null
@@@ -1,52 -1,0 +1,52 @@@
-         "".to_string(),
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use rustc_ast::ast::{Pat, PatKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::EarlyContext;
 +use rustc_span::source_map::Span;
 +
 +use super::UNNEEDED_WILDCARD_PATTERN;
 +
 +pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
 +    if let PatKind::TupleStruct(_, _, ref patterns) | PatKind::Tuple(ref patterns) = pat.kind {
 +        if let Some(rest_index) = patterns.iter().position(|pat| pat.is_rest()) {
 +            if let Some((left_index, left_pat)) = patterns[..rest_index]
 +                .iter()
 +                .rev()
 +                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
 +                .enumerate()
 +                .last()
 +            {
 +                span_lint(cx, left_pat.span.until(patterns[rest_index].span), left_index == 0);
 +            }
 +
 +            if let Some((right_index, right_pat)) = patterns[rest_index + 1..]
 +                .iter()
 +                .take_while(|pat| matches!(pat.kind, PatKind::Wild))
 +                .enumerate()
 +                .last()
 +            {
 +                span_lint(
 +                    cx,
 +                    patterns[rest_index].span.shrink_to_hi().to(right_pat.span),
 +                    right_index == 0,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn span_lint(cx: &EarlyContext<'_>, span: Span, only_one: bool) {
 +    span_lint_and_sugg(
 +        cx,
 +        UNNEEDED_WILDCARD_PATTERN,
 +        span,
 +        if only_one {
 +            "this pattern is unneeded as the `..` pattern can match that element"
 +        } else {
 +            "these patterns are unneeded as the `..` pattern can match those elements"
 +        },
 +        if only_one { "remove it" } else { "remove them" },
++        String::new(),
 +        Applicability::MachineApplicable,
 +    );
 +}
index f763e0d24c9444206a8d14b599a411f3067d610e,0000000000000000000000000000000000000000..020efeaebf02905dd46bb0b0a7ef0c5fa7f91853
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,122 @@@
-     #[clippy::version = "1.62.0"]
 +use clippy_utils::diagnostics::span_lint_and_help;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::{GenericArg, Item, ItemKind, QPath, Ty, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::GenericParamDefKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for type parameters which are positioned inconsistently between
 +    /// a type definition and impl block. Specifically, a parameter in an impl
 +    /// block which has the same name as a parameter in the type def, but is in
 +    /// a different place.
 +    ///
 +    /// ### Why is this bad?
 +    /// Type parameters are determined by their position rather than name.
 +    /// Naming type parameters inconsistently may cause you to refer to the
 +    /// wrong type parameter.
 +    ///
 +    /// ### Limitations
 +    /// This lint only applies to impl blocks with simple generic params, e.g.
 +    /// `A`. If there is anything more complicated, such as a tuple, it will be
 +    /// ignored.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo<A, B> {
 +    ///     x: A,
 +    ///     y: B,
 +    /// }
 +    /// // inside the impl, B refers to Foo::A
 +    /// impl<B, A> Foo<B, A> {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// struct Foo<A, B> {
 +    ///     x: A,
 +    ///     y: B,
 +    /// }
 +    /// impl<A, B> Foo<A, B> {}
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub MISMATCHING_TYPE_PARAM_ORDER,
 +    pedantic,
 +    "type parameter positioned inconsistently between type def and impl block"
 +}
 +declare_lint_pass!(TypeParamMismatch => [MISMATCHING_TYPE_PARAM_ORDER]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        if_chain! {
 +            if !item.span.from_expansion();
 +            if let ItemKind::Impl(imp) = &item.kind;
 +            if let TyKind::Path(QPath::Resolved(_, path)) = &imp.self_ty.kind;
 +            if let Some(segment) = path.segments.iter().next();
 +            if let Some(generic_args) = segment.args;
 +            if !generic_args.args.is_empty();
 +            then {
 +                // get the name and span of the generic parameters in the Impl
 +                let mut impl_params = Vec::new();
 +                for p in generic_args.args.iter() {
 +                    match p {
 +                        GenericArg::Type(Ty {kind: TyKind::Path(QPath::Resolved(_, path)), ..}) =>
 +                            impl_params.push((path.segments[0].ident.to_string(), path.span)),
 +                        GenericArg::Type(_) => return,
 +                        _ => (),
 +                    };
 +                }
 +
 +                // find the type that the Impl is for
 +                // only lint on struct/enum/union for now
 +                let defid = match path.res {
 +                    Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, defid) => defid,
 +                    _ => return,
 +                };
 +
 +                // get the names of the generic parameters in the type
 +                let type_params = &cx.tcx.generics_of(defid).params;
 +                let type_param_names: Vec<_> = type_params.iter()
 +                .filter_map(|p|
 +                    match p.kind {
 +                        GenericParamDefKind::Type {..} => Some(p.name.to_string()),
 +                        _ => None,
 +                    }
 +                ).collect();
 +                // hashmap of name -> index for mismatch_param_name
 +                let type_param_names_hashmap: FxHashMap<&String, usize> =
 +                    type_param_names.iter().enumerate().map(|(i, param)| (param, i)).collect();
 +
 +                let type_name = segment.ident;
 +                for (i, (impl_param_name, impl_param_span)) in impl_params.iter().enumerate() {
 +                    if mismatch_param_name(i, impl_param_name, &type_param_names_hashmap) {
 +                        let msg = format!("`{}` has a similarly named generic type parameter `{}` in its declaration, but in a different order",
 +                                          type_name, impl_param_name);
 +                        let help = format!("try `{}`, or a name that does not conflict with `{}`'s generic params",
 +                                           type_param_names[i], type_name);
 +                        span_lint_and_help(
 +                            cx,
 +                            MISMATCHING_TYPE_PARAM_ORDER,
 +                            *impl_param_span,
 +                            &msg,
 +                            None,
 +                            &help
 +                        );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +// Checks if impl_param_name is the same as one of type_param_names,
 +// and is in a different position
 +fn mismatch_param_name(i: usize, impl_param_name: &String, type_param_names: &FxHashMap<&String, usize>) -> bool {
 +    if let Some(j) = type_param_names.get(impl_param_name) {
 +        if i != *j {
 +            return true;
 +        }
 +    }
 +    false
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..81eb1a085aea9163eca1c0029766d95f99d9d280
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,65 @@@
++use clippy_utils::diagnostics::span_lint;
++use rustc_ast::ast::{Expr, ExprKind, Stmt, StmtKind};
++use rustc_lint::{EarlyContext, EarlyLintPass};
++use rustc_session::{declare_lint_pass, declare_tool_lint};
++
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for nested assignments.
++    ///
++    /// ### Why is this bad?
++    /// While this is in most cases already a type mismatch,
++    /// the result of an assignment being `()` can throw off people coming from languages like python or C,
++    /// where such assignments return a copy of the assigned value.
++    ///
++    /// ### Example
++    /// ```rust
++    ///# let (a, b);
++    /// a = b = 42;
++    /// ```
++    /// Use instead:
++    /// ```rust
++    ///# let (a, b);
++    /// b = 42;
++    /// a = b;
++    /// ```
++    #[clippy::version = "1.65.0"]
++    pub MULTI_ASSIGNMENTS,
++    suspicious,
++    "instead of using `a = b = c;` use `a = c; b = c;`"
++}
++
++declare_lint_pass!(MultiAssignments => [MULTI_ASSIGNMENTS]);
++
++fn strip_paren_blocks(expr: &Expr) -> &Expr {
++    match &expr.kind {
++        ExprKind::Paren(e) => strip_paren_blocks(e),
++        ExprKind::Block(b, _) => {
++            if let [
++                Stmt {
++                    kind: StmtKind::Expr(e),
++                    ..
++                },
++            ] = &b.stmts[..]
++            {
++                strip_paren_blocks(e)
++            } else {
++                expr
++            }
++        },
++        _ => expr,
++    }
++}
++
++impl EarlyLintPass for MultiAssignments {
++    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
++        if let ExprKind::Assign(target, source, _) = &expr.kind {
++            if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind {
++                span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
++            };
++            if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind {
++                span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively");
++            }
++        };
++    }
++}
index 413a740be25a529c0fe8fb64361dd38d567de634,0000000000000000000000000000000000000000..774a3540d1e0c41bef19b00f44b955664de5c743
mode 100644,000000..100644
--- /dev/null
@@@ -1,660 -1,0 +1,402 @@@
- use std::collections::VecDeque;
- use clippy_utils::diagnostics::span_lint_and_sugg;
- use clippy_utils::is_lint_allowed;
- use itertools::{izip, Itertools};
- use rustc_ast::{walk_list, Label, Mutability};
- use rustc_data_structures::fx::{FxHashMap, FxHashSet};
++use clippy_utils::diagnostics::span_lint_and_then;
++use clippy_utils::{get_expr_use_or_unification_node, get_parent_node, path_def_id, path_to_local, path_to_local_id};
++use core::cell::Cell;
++use rustc_data_structures::fx::FxHashMap;
 +use rustc_errors::Applicability;
- use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
- use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
- use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
- use rustc_hir::{
-     Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
-     PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
- };
++use rustc_hir::hir_id::HirIdMap;
++use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKind, TraitItem, TraitItemKind};
 +use rustc_lint::{LateContext, LateLintPass};
- use rustc_middle::ty;
- use rustc_middle::ty::{Ty, TyCtxt, TypeckResults};
- use rustc_session::{declare_lint_pass, declare_tool_lint};
- use rustc_span::symbol::kw;
- use rustc_span::symbol::Ident;
++use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
++use rustc_middle::ty::{self, ConstKind};
++use rustc_session::{declare_tool_lint, impl_lint_pass};
++use rustc_span::symbol::{kw, Ident};
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for arguments that are only used in recursion with no side-effects.
 +    ///
 +    /// ### Why is this bad?
 +    /// It could contain a useless calculation and can make function simpler.
 +    ///
 +    /// The arguments can be involved in calculations and assignments but as long as
 +    /// the calculations have no side-effects (function calls or mutating dereference)
 +    /// and the assigned variables are also only in recursion, it is useless.
 +    ///
 +    /// ### Known problems
 +    /// Too many code paths in the linting code are currently untested and prone to produce false
 +    /// positives or are prone to have performance implications.
 +    ///
 +    /// In some cases, this would not catch all useless arguments.
 +    ///
 +    /// ```rust
 +    /// fn foo(a: usize, b: usize) -> usize {
 +    ///     let f = |x| x + 1;
 +    ///
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         foo(a - 1, f(b))
 +    ///     }
 +    /// }
 +    /// ```
 +    ///
 +    /// For example, the argument `b` is only used in recursion, but the lint would not catch it.
 +    ///
 +    /// List of some examples that can not be caught:
 +    /// - binary operation of non-primitive types
 +    /// - closure usage
 +    /// - some `break` relative operations
 +    /// - struct pattern binding
 +    ///
 +    /// Also, when you recurse the function name with path segments, it is not possible to detect.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn f(a: usize, b: usize) -> usize {
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         f(a - 1, b + 1)
 +    ///     }
 +    /// }
 +    /// # fn main() {
 +    /// #     print!("{}", f(1, 1));
 +    /// # }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn f(a: usize) -> usize {
 +    ///     if a == 0 {
 +    ///         1
 +    ///     } else {
 +    ///         f(a - 1)
 +    ///     }
 +    /// }
 +    /// # fn main() {
 +    /// #     print!("{}", f(1));
 +    /// # }
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub ONLY_USED_IN_RECURSION,
-     nursery,
++    complexity,
 +    "arguments that is only used in recursion can be removed"
 +}
- declare_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
- impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
-     fn check_fn(
-         &mut self,
-         cx: &LateContext<'tcx>,
-         kind: FnKind<'tcx>,
-         decl: &'tcx rustc_hir::FnDecl<'tcx>,
-         body: &'tcx Body<'tcx>,
-         _: Span,
-         id: HirId,
-     ) {
-         if is_lint_allowed(cx, ONLY_USED_IN_RECURSION, id) {
-             return;
-         }
-         if let FnKind::ItemFn(ident, ..) | FnKind::Method(ident, ..) = kind {
-             let def_id = id.owner.to_def_id();
-             let data = cx.tcx.def_path(def_id).data;
-             if data.len() > 1 {
-                 match data.get(data.len() - 2) {
-                     Some(DisambiguatedDefPathData {
-                         data: DefPathData::Impl,
-                         disambiguator,
-                     }) if *disambiguator != 0 => return,
-                     _ => {},
-                 }
-             }
-             let has_self = !matches!(decl.implicit_self, ImplicitSelfKind::None);
-             let ty_res = cx.typeck_results();
-             let param_span = body
-                 .params
-                 .iter()
-                 .flat_map(|param| {
-                     let mut v = Vec::new();
-                     param.pat.each_binding(|_, hir_id, span, ident| {
-                         v.push((hir_id, span, ident));
-                     });
-                     v
-                 })
-                 .skip(if has_self { 1 } else { 0 })
-                 .filter(|(_, _, ident)| !ident.name.as_str().starts_with('_'))
-                 .collect_vec();
-             let params = body.params.iter().map(|param| param.pat).collect();
-             let mut visitor = SideEffectVisit {
-                 graph: FxHashMap::default(),
-                 has_side_effect: FxHashSet::default(),
-                 ret_vars: Vec::new(),
-                 contains_side_effect: false,
-                 break_vars: FxHashMap::default(),
-                 params,
-                 fn_ident: ident,
-                 fn_def_id: def_id,
-                 is_method: matches!(kind, FnKind::Method(..)),
-                 has_self,
-                 ty_res,
-                 tcx: cx.tcx,
-                 visited_exprs: FxHashSet::default(),
-             };
-             visitor.visit_expr(&body.value);
-             let vars = std::mem::take(&mut visitor.ret_vars);
-             // this would set the return variables to side effect
-             visitor.add_side_effect(vars);
-             let mut queue = visitor.has_side_effect.iter().copied().collect::<VecDeque<_>>();
-             // a simple BFS to check all the variables that have side effect
-             while let Some(id) = queue.pop_front() {
-                 if let Some(next) = visitor.graph.get(&id) {
-                     for i in next {
-                         if !visitor.has_side_effect.contains(i) {
-                             visitor.has_side_effect.insert(*i);
-                             queue.push_back(*i);
-                         }
-                     }
-                 }
-             }
-             for (id, span, ident) in param_span {
-                 // if the variable is not used in recursion, it would be marked as unused
-                 if !visitor.has_side_effect.contains(&id) {
-                     let mut queue = VecDeque::new();
-                     let mut visited = FxHashSet::default();
-                     queue.push_back(id);
-                     // a simple BFS to check the graph can reach to itself
-                     // if it can't, it means the variable is never used in recursion
-                     while let Some(id) = queue.pop_front() {
-                         if let Some(next) = visitor.graph.get(&id) {
-                             for i in next {
-                                 if !visited.contains(i) {
-                                     visited.insert(id);
-                                     queue.push_back(*i);
-                                 }
-                             }
-                         }
-                     }
++impl_lint_pass!(OnlyUsedInRecursion => [ONLY_USED_IN_RECURSION]);
++
++#[derive(Clone, Copy)]
++enum FnKind {
++    Fn,
++    TraitFn,
++    // This is a hack. Ideally we would store a `SubstsRef<'tcx>` type here, but a lint pass must be `'static`.
++    // Substitutions are, however, interned. This allows us to store the pointer as a `usize` when comparing for
++    // equality.
++    ImplTraitFn(usize),
++}
 +
-                     if visited.contains(&id) {
-                         span_lint_and_sugg(
-                             cx,
-                             ONLY_USED_IN_RECURSION,
-                             span,
-                             "parameter is only used in recursion",
-                             "if this is intentional, prefix with an underscore",
-                             format!("_{}", ident.name.as_str()),
-                             Applicability::MaybeIncorrect,
-                         );
-                     }
-                 }
-             }
++struct Param {
++    /// The function this is a parameter for.
++    fn_id: DefId,
++    fn_kind: FnKind,
++    /// The index of this parameter.
++    idx: usize,
++    ident: Ident,
++    /// Whether this parameter should be linted. Set by `Params::flag_for_linting`.
++    apply_lint: Cell<bool>,
++    /// All the uses of this parameter.
++    uses: Vec<Usage>,
++}
++impl Param {
++    fn new(fn_id: DefId, fn_kind: FnKind, idx: usize, ident: Ident) -> Self {
++        Self {
++            fn_id,
++            fn_kind,
++            idx,
++            ident,
++            apply_lint: Cell::new(true),
++            uses: Vec::new(),
 +        }
 +    }
 +}
 +
- pub fn is_primitive(ty: Ty<'_>) -> bool {
-     let ty = ty.peel_refs();
-     ty.is_primitive() || ty.is_str()
++#[derive(Debug)]
++struct Usage {
++    span: Span,
++    idx: usize,
 +}
- pub fn is_array(ty: Ty<'_>) -> bool {
-     let ty = ty.peel_refs();
-     ty.is_array() || ty.is_array_slice()
++impl Usage {
++    fn new(span: Span, idx: usize) -> Self {
++        Self { span, idx }
++    }
 +}
 +
- /// This builds the graph of side effect.
- /// The edge `a -> b` means if `a` has side effect, `b` will have side effect.
- ///
- /// There are some example in following code:
- /// ```rust, ignore
- /// let b = 1;
- /// let a = b; // a -> b
- /// let (c, d) = (a, b); // c -> b, d -> b
- ///
- /// let e = if a == 0 { // e -> a
- ///     c // e -> c
- /// } else {
- ///     d // e -> d
- /// };
- /// ```
- pub struct SideEffectVisit<'tcx> {
-     graph: FxHashMap<HirId, FxHashSet<HirId>>,
-     has_side_effect: FxHashSet<HirId>,
-     // bool for if the variable was dereferenced from mutable reference
-     ret_vars: Vec<(HirId, bool)>,
-     contains_side_effect: bool,
-     // break label
-     break_vars: FxHashMap<Ident, Vec<(HirId, bool)>>,
-     params: Vec<&'tcx Pat<'tcx>>,
-     fn_ident: Ident,
-     fn_def_id: DefId,
-     is_method: bool,
-     has_self: bool,
-     ty_res: &'tcx TypeckResults<'tcx>,
-     tcx: TyCtxt<'tcx>,
-     visited_exprs: FxHashSet<HirId>,
++/// The parameters being checked by the lint, indexed by both the parameter's `HirId` and the
++/// `DefId` of the function paired with the parameter's index.
++#[derive(Default)]
++struct Params {
++    params: Vec<Param>,
++    by_id: HirIdMap<usize>,
++    by_fn: FxHashMap<(DefId, usize), usize>,
 +}
- impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
-     fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
-         match s.kind {
-             StmtKind::Local(Local {
-                 pat, init: Some(init), ..
-             }) => {
-                 self.visit_pat_expr(pat, init, false);
-             },
-             StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
-                 walk_stmt(self, s);
-             },
-             StmtKind::Local(_) => {},
-         }
-         self.ret_vars.clear();
++impl Params {
++    fn insert(&mut self, param: Param, id: HirId) {
++        let idx = self.params.len();
++        self.by_id.insert(id, idx);
++        self.by_fn.insert((param.fn_id, param.idx), idx);
++        self.params.push(param);
 +    }
 +
-     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-         if !self.visited_exprs.insert(ex.hir_id) {
-             return;
-         }
-         match ex.kind {
-             ExprKind::Array(exprs) | ExprKind::Tup(exprs) => {
-                 self.ret_vars = exprs
-                     .iter()
-                     .flat_map(|expr| {
-                         self.visit_expr(expr);
-                         std::mem::take(&mut self.ret_vars)
-                     })
-                     .collect();
-             },
-             ExprKind::Call(callee, args) => self.visit_fn(callee, args),
-             ExprKind::MethodCall(path, args, _) => self.visit_method_call(path, args),
-             ExprKind::Binary(_, lhs, rhs) => {
-                 self.visit_bin_op(lhs, rhs);
-             },
-             ExprKind::Unary(op, expr) => self.visit_un_op(op, expr),
-             ExprKind::Let(Let { pat, init, .. }) => self.visit_pat_expr(pat, init, false),
-             ExprKind::If(bind, then_expr, else_expr) => {
-                 self.visit_if(bind, then_expr, else_expr);
-             },
-             ExprKind::Match(expr, arms, _) => self.visit_match(expr, arms),
-             // since analysing the closure is not easy, just set all variables in it to side-effect
-             ExprKind::Closure(&Closure { body, .. }) => {
-                 let body = self.tcx.hir().body(body);
-                 self.visit_body(body);
-                 let vars = std::mem::take(&mut self.ret_vars);
-                 self.add_side_effect(vars);
-             },
-             ExprKind::Loop(block, label, _, _) | ExprKind::Block(block, label) => {
-                 self.visit_block_label(block, label);
-             },
-             ExprKind::Assign(bind, expr, _) => {
-                 self.visit_assign(bind, expr);
-             },
-             ExprKind::AssignOp(_, bind, expr) => {
-                 self.visit_assign(bind, expr);
-                 self.visit_bin_op(bind, expr);
-             },
-             ExprKind::Field(expr, _) => {
-                 self.visit_expr(expr);
-                 if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
-                     self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
-                 }
-             },
-             ExprKind::Index(expr, index) => {
-                 self.visit_expr(expr);
-                 let mut vars = std::mem::take(&mut self.ret_vars);
-                 self.visit_expr(index);
-                 self.ret_vars.append(&mut vars);
-                 if !is_array(self.ty_res.expr_ty(expr)) {
-                     self.add_side_effect(self.ret_vars.clone());
-                 } else if matches!(self.ty_res.expr_ty(expr).kind(), ty::Ref(_, _, Mutability::Mut)) {
-                     self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
-                 }
-             },
-             ExprKind::Break(dest, Some(expr)) => {
-                 self.visit_expr(expr);
-                 if let Some(label) = dest.label {
-                     self.break_vars
-                         .entry(label.ident)
-                         .or_insert(Vec::new())
-                         .append(&mut self.ret_vars);
-                 }
-                 self.contains_side_effect = true;
-             },
-             ExprKind::Ret(Some(expr)) => {
-                 self.visit_expr(expr);
-                 let vars = std::mem::take(&mut self.ret_vars);
-                 self.add_side_effect(vars);
-                 self.contains_side_effect = true;
-             },
-             ExprKind::Break(_, None) | ExprKind::Continue(_) | ExprKind::Ret(None) => {
-                 self.contains_side_effect = true;
-             },
-             ExprKind::Struct(_, exprs, expr) => {
-                 let mut ret_vars = exprs
-                     .iter()
-                     .flat_map(|field| {
-                         self.visit_expr(field.expr);
-                         std::mem::take(&mut self.ret_vars)
-                     })
-                     .collect();
-                 walk_list!(self, visit_expr, expr);
-                 self.ret_vars.append(&mut ret_vars);
-             },
-             _ => walk_expr(self, ex),
++    fn remove_by_id(&mut self, id: HirId) {
++        if let Some(param) = self.get_by_id_mut(id) {
++            param.uses = Vec::new();
++            let key = (param.fn_id, param.idx);
++            self.by_fn.remove(&key);
++            self.by_id.remove(&id);
 +        }
 +    }
 +
-     fn visit_path(&mut self, path: &'tcx Path<'tcx>, _id: HirId) {
-         if let Res::Local(id) = path.res {
-             self.ret_vars.push((id, false));
-         }
++    fn get_by_id_mut(&mut self, id: HirId) -> Option<&mut Param> {
++        self.params.get_mut(*self.by_id.get(&id)?)
 +    }
- }
 +
- impl<'tcx> SideEffectVisit<'tcx> {
-     fn visit_assign(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
-         // Just support array and tuple unwrapping for now.
-         //
-         // ex) `(a, b) = (c, d);`
-         // The graph would look like this:
-         //   a -> c
-         //   b -> d
-         //
-         // This would minimize the connection of the side-effect graph.
-         match (&lhs.kind, &rhs.kind) {
-             (ExprKind::Array(lhs), ExprKind::Array(rhs)) | (ExprKind::Tup(lhs), ExprKind::Tup(rhs)) => {
-                 // if not, it is a compile error
-                 debug_assert!(lhs.len() == rhs.len());
-                 izip!(*lhs, *rhs).for_each(|(lhs, rhs)| self.visit_assign(lhs, rhs));
-             },
-             // in other assigns, we have to connect all each other
-             // because they can be connected somehow
-             _ => {
-                 self.visit_expr(lhs);
-                 let lhs_vars = std::mem::take(&mut self.ret_vars);
-                 self.visit_expr(rhs);
-                 let rhs_vars = std::mem::take(&mut self.ret_vars);
-                 self.connect_assign(&lhs_vars, &rhs_vars, false);
-             },
-         }
++    fn get_by_fn(&self, id: DefId, idx: usize) -> Option<&Param> {
++        self.params.get(*self.by_fn.get(&(id, idx))?)
 +    }
 +
-     fn visit_block_label(&mut self, block: &'tcx Block<'tcx>, label: Option<Label>) {
-         self.visit_block(block);
-         let _ = label.and_then(|label| {
-             self.break_vars
-                 .remove(&label.ident)
-                 .map(|mut break_vars| self.ret_vars.append(&mut break_vars))
-         });
-     }
-     fn visit_bin_op(&mut self, lhs: &'tcx Expr<'tcx>, rhs: &'tcx Expr<'tcx>) {
-         self.visit_expr(lhs);
-         let mut ret_vars = std::mem::take(&mut self.ret_vars);
-         self.visit_expr(rhs);
-         self.ret_vars.append(&mut ret_vars);
-         // the binary operation between non primitive values are overloaded operators
-         // so they can have side-effects
-         if !is_primitive(self.ty_res.expr_ty(lhs)) || !is_primitive(self.ty_res.expr_ty(rhs)) {
-             self.ret_vars.iter().for_each(|id| {
-                 self.has_side_effect.insert(id.0);
-             });
-             self.contains_side_effect = true;
-         }
++    fn clear(&mut self) {
++        self.params.clear();
++        self.by_id.clear();
++        self.by_fn.clear();
 +    }
 +
-     fn visit_un_op(&mut self, op: UnOp, expr: &'tcx Expr<'tcx>) {
-         self.visit_expr(expr);
-         let ty = self.ty_res.expr_ty(expr);
-         // dereferencing a reference has no side-effect
-         if !is_primitive(ty) && !matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(..))) {
-             self.add_side_effect(self.ret_vars.clone());
-         }
-         if matches!((op, ty.kind()), (UnOp::Deref, ty::Ref(_, _, Mutability::Mut))) {
-             self.ret_vars.iter_mut().for_each(|(_, b)| *b = true);
++    /// Sets the `apply_lint` flag on each parameter.
++    fn flag_for_linting(&mut self) {
++        // Stores the list of parameters currently being resolved. Needed to avoid cycles.
++        let mut eval_stack = Vec::new();
++        for param in &self.params {
++            self.try_disable_lint_for_param(param, &mut eval_stack);
 +        }
 +    }
 +
-     fn visit_pat_expr(&mut self, pat: &'tcx Pat<'tcx>, expr: &'tcx Expr<'tcx>, connect_self: bool) {
-         match (&pat.kind, &expr.kind) {
-             (PatKind::Tuple(pats, _), ExprKind::Tup(exprs)) => {
-                 self.ret_vars = izip!(*pats, *exprs)
-                     .flat_map(|(pat, expr)| {
-                         self.visit_pat_expr(pat, expr, connect_self);
-                         std::mem::take(&mut self.ret_vars)
-                     })
-                     .collect();
-             },
-             (PatKind::Slice(front_exprs, _, back_exprs), ExprKind::Array(exprs)) => {
-                 let mut vars = izip!(*front_exprs, *exprs)
-                     .flat_map(|(pat, expr)| {
-                         self.visit_pat_expr(pat, expr, connect_self);
-                         std::mem::take(&mut self.ret_vars)
-                     })
-                     .collect();
-                 self.ret_vars = izip!(back_exprs.iter().rev(), exprs.iter().rev())
-                     .flat_map(|(pat, expr)| {
-                         self.visit_pat_expr(pat, expr, connect_self);
-                         std::mem::take(&mut self.ret_vars)
-                     })
-                     .collect();
-                 self.ret_vars.append(&mut vars);
-             },
-             _ => {
-                 let mut lhs_vars = Vec::new();
-                 pat.each_binding(|_, id, _, _| lhs_vars.push((id, false)));
-                 self.visit_expr(expr);
-                 let rhs_vars = std::mem::take(&mut self.ret_vars);
-                 self.connect_assign(&lhs_vars, &rhs_vars, connect_self);
-                 self.ret_vars = rhs_vars;
-             },
++    // Use by calling `flag_for_linting`.
++    fn try_disable_lint_for_param(&self, param: &Param, eval_stack: &mut Vec<usize>) -> bool {
++        if !param.apply_lint.get() {
++            true
++        } else if param.uses.is_empty() {
++            // Don't lint on unused parameters.
++            param.apply_lint.set(false);
++            true
++        } else if eval_stack.contains(&param.idx) {
++            // Already on the evaluation stack. Returning false will continue to evaluate other dependencies.
++            false
++        } else {
++            eval_stack.push(param.idx);
++            // Check all cases when used at a different parameter index.
++            // Needed to catch cases like: `fn f(x: u32, y: u32) { f(y, x) }`
++            for usage in param.uses.iter().filter(|u| u.idx != param.idx) {
++                if self
++                    .get_by_fn(param.fn_id, usage.idx)
++                    // If the parameter can't be found, then it's used for more than just recursion.
++                    .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
++                {
++                    param.apply_lint.set(false);
++                    eval_stack.pop();
++                    return true;
++                }
++            }
++            eval_stack.pop();
++            false
 +        }
 +    }
++}
 +
-     fn visit_fn(&mut self, callee: &'tcx Expr<'tcx>, args: &'tcx [Expr<'tcx>]) {
-         self.visit_expr(callee);
-         let mut ret_vars = std::mem::take(&mut self.ret_vars);
-         self.add_side_effect(ret_vars.clone());
-         let mut is_recursive = false;
-         if_chain! {
-             if !self.has_self;
-             if let ExprKind::Path(QPath::Resolved(_, path)) = callee.kind;
-             if let Res::Def(DefKind::Fn, def_id) = path.res;
-             if self.fn_def_id == def_id;
-             then {
-                 is_recursive = true;
-             }
-         }
++#[derive(Default)]
++pub struct OnlyUsedInRecursion {
++    /// Track the top-level body entered. Needed to delay reporting when entering nested bodies.
++    entered_body: Option<HirId>,
++    params: Params,
++}
 +
-         if_chain! {
-             if !self.has_self && self.is_method;
-             if let ExprKind::Path(QPath::TypeRelative(ty, segment)) = callee.kind;
-             if segment.ident == self.fn_ident;
-             if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-             if let Res::SelfTy{ .. } = path.res;
-             then {
-                 is_recursive = true;
-             }
++impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
++    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
++        if body.value.span.from_expansion() {
++            return;
 +        }
-         if is_recursive {
-             izip!(self.params.clone(), args).for_each(|(pat, expr)| {
-                 self.visit_pat_expr(pat, expr, true);
-                 self.ret_vars.clear();
-             });
-         } else {
-             // This would set arguments used in closure that does not have side-effect.
-             // Closure itself can be detected whether there is a side-effect, but the
-             // value of variable that is holding closure can change.
-             // So, we just check the variables.
-             self.ret_vars = args
-                 .iter()
-                 .flat_map(|expr| {
-                     self.visit_expr(expr);
-                     std::mem::take(&mut self.ret_vars)
-                 })
-                 .collect_vec()
-                 .into_iter()
-                 .map(|id| {
-                     self.has_side_effect.insert(id.0);
-                     id
-                 })
-                 .collect();
-             self.contains_side_effect = true;
++        // `skip_params` is either `0` or `1` to skip the `self` parameter in trait functions.
++        // It can't be renamed, and it can't be removed without removing it from multiple functions.
++        let (fn_id, fn_kind, skip_params) = match get_parent_node(cx.tcx, body.value.hir_id) {
++            Some(Node::Item(i)) => (i.def_id.to_def_id(), FnKind::Fn, 0),
++            Some(Node::TraitItem(&TraitItem {
++                kind: TraitItemKind::Fn(ref sig, _),
++                def_id,
++                ..
++            })) => (
++                def_id.to_def_id(),
++                FnKind::TraitFn,
++                if sig.decl.implicit_self.has_implicit_self() {
++                    1
++                } else {
++                    0
++                },
++            ),
++            Some(Node::ImplItem(&ImplItem {
++                kind: ImplItemKind::Fn(ref sig, _),
++                def_id,
++                ..
++            })) => {
++                #[allow(trivial_casts)]
++                if let Some(Node::Item(item)) = get_parent_node(cx.tcx, cx.tcx.hir().local_def_id_to_hir_id(def_id))
++                    && let Some(trait_ref) = cx.tcx.impl_trait_ref(item.def_id)
++                    && let Some(trait_item_id) = cx.tcx.associated_item(def_id).trait_item_def_id
++                {
++                    (
++                        trait_item_id,
++                        FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
++                        if sig.decl.implicit_self.has_implicit_self() {
++                            1
++                        } else {
++                            0
++                        },
++                    )
++                } else {
++                    (def_id.to_def_id(), FnKind::Fn, 0)
++                }
++            },
++            _ => return,
++        };
++        body.params
++            .iter()
++            .enumerate()
++            .skip(skip_params)
++            .filter_map(|(idx, p)| match p.pat.kind {
++                PatKind::Binding(_, id, ident, None) if !ident.as_str().starts_with('_') => {
++                    Some((id, Param::new(fn_id, fn_kind, idx, ident)))
++                },
++                _ => None,
++            })
++            .for_each(|(id, param)| self.params.insert(param, id));
++        if self.entered_body.is_none() {
++            self.entered_body = Some(body.value.hir_id);
 +        }
-         self.ret_vars.append(&mut ret_vars);
 +    }
 +
-     fn visit_method_call(&mut self, path: &'tcx PathSegment<'tcx>, args: &'tcx [Expr<'tcx>]) {
-         if_chain! {
-             if self.is_method;
-             if path.ident == self.fn_ident;
-             if let ExprKind::Path(QPath::Resolved(_, path)) = args.first().unwrap().kind;
-             if let Res::Local(..) = path.res;
-             let ident = path.segments.last().unwrap().ident;
-             if ident.name == kw::SelfLower;
-             then {
-                 izip!(self.params.clone(), args.iter())
-                     .for_each(|(pat, expr)| {
-                         self.visit_pat_expr(pat, expr, true);
-                         self.ret_vars.clear();
-                     });
-             } else {
-                 self.ret_vars = args
-                     .iter()
-                     .flat_map(|expr| {
-                         self.visit_expr(expr);
-                         std::mem::take(&mut self.ret_vars)
-                     })
-                     .collect_vec()
-                     .into_iter()
-                     .map(|a| {
-                         self.has_side_effect.insert(a.0);
-                         a
-                     })
-                     .collect();
-                 self.contains_side_effect = true;
++    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) {
++        if let Some(id) = path_to_local(e)
++            && let Some(param) = self.params.get_by_id_mut(id)
++        {
++            let typeck = cx.typeck_results();
++            let span = e.span;
++            let mut e = e;
++            loop {
++                match get_expr_use_or_unification_node(cx.tcx, e) {
++                    None | Some((Node::Stmt(_), _)) => return,
++                    Some((Node::Expr(parent), child_id)) => match parent.kind {
++                        // Recursive call. Track which index the parameter is used in.
++                        ExprKind::Call(callee, args)
++                            if path_def_id(cx, callee).map_or(false, |id| {
++                                id == param.fn_id
++                                    && has_matching_substs(param.fn_kind, typeck.node_substs(callee.hir_id))
++                            }) =>
++                        {
++                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
++                                param.uses.push(Usage::new(span, idx));
++                            }
++                            return;
++                        },
++                        ExprKind::MethodCall(_, args, _)
++                            if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
++                                id == param.fn_id
++                                    && has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
++                            }) =>
++                        {
++                            if let Some(idx) = args.iter().position(|arg| arg.hir_id == child_id) {
++                                param.uses.push(Usage::new(span, idx));
++                            }
++                            return;
++                        },
++                        // Assignment to a parameter is fine.
++                        ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
++                            return;
++                        },
++                        // Parameter update e.g. `x = x + 1`
++                        ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs)
++                            if rhs.hir_id == child_id && path_to_local_id(lhs, id) =>
++                        {
++                            return;
++                        },
++                        // Side-effect free expressions. Walk to the parent expression.
++                        ExprKind::Binary(_, lhs, rhs)
++                            if typeck.expr_ty(lhs).is_primitive() && typeck.expr_ty(rhs).is_primitive() =>
++                        {
++                            e = parent;
++                            continue;
++                        },
++                        ExprKind::Unary(_, arg) if typeck.expr_ty(arg).is_primitive() => {
++                            e = parent;
++                            continue;
++                        },
++                        ExprKind::AddrOf(..) | ExprKind::Cast(..) => {
++                            e = parent;
++                            continue;
++                        },
++                        // Only allow field accesses without auto-deref
++                        ExprKind::Field(..) if typeck.adjustments().get(child_id).is_none() => {
++                            e = parent;
++                            continue
++                        }
++                        _ => (),
++                    },
++                    _ => (),
++                }
++                self.params.remove_by_id(id);
++                return;
 +            }
 +        }
 +    }
 +
-     fn visit_if(&mut self, bind: &'tcx Expr<'tcx>, then_expr: &'tcx Expr<'tcx>, else_expr: Option<&'tcx Expr<'tcx>>) {
-         let contains_side_effect = self.contains_side_effect;
-         self.contains_side_effect = false;
-         self.visit_expr(bind);
-         let mut vars = std::mem::take(&mut self.ret_vars);
-         self.visit_expr(then_expr);
-         let mut then_vars = std::mem::take(&mut self.ret_vars);
-         walk_list!(self, visit_expr, else_expr);
-         if self.contains_side_effect {
-             self.add_side_effect(vars.clone());
-         }
-         self.contains_side_effect |= contains_side_effect;
-         self.ret_vars.append(&mut vars);
-         self.ret_vars.append(&mut then_vars);
-     }
-     fn visit_match(&mut self, expr: &'tcx Expr<'tcx>, arms: &'tcx [Arm<'tcx>]) {
-         self.visit_expr(expr);
-         let mut expr_vars = std::mem::take(&mut self.ret_vars);
-         self.ret_vars = arms
-             .iter()
-             .flat_map(|arm| {
-                 let contains_side_effect = self.contains_side_effect;
-                 self.contains_side_effect = false;
-                 // this would visit `expr` multiple times
-                 // but couldn't think of a better way
-                 self.visit_pat_expr(arm.pat, expr, false);
-                 let mut vars = std::mem::take(&mut self.ret_vars);
-                 let _ = arm.guard.as_ref().map(|guard| {
-                     self.visit_expr(match guard {
-                         Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => expr,
-                     });
-                     vars.append(&mut self.ret_vars);
-                 });
-                 self.visit_expr(arm.body);
-                 if self.contains_side_effect {
-                     self.add_side_effect(vars.clone());
-                     self.add_side_effect(expr_vars.clone());
++    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'tcx>) {
++        if self.entered_body == Some(body.value.hir_id) {
++            self.entered_body = None;
++            self.params.flag_for_linting();
++            for param in &self.params.params {
++                if param.apply_lint.get() {
++                    span_lint_and_then(
++                        cx,
++                        ONLY_USED_IN_RECURSION,
++                        param.ident.span,
++                        "parameter is only used in recursion",
++                        |diag| {
++                            if param.ident.name != kw::SelfLower {
++                                diag.span_suggestion(
++                                    param.ident.span,
++                                    "if this is intentional, prefix it with an underscore",
++                                    format!("_{}", param.ident.name),
++                                    Applicability::MaybeIncorrect,
++                                );
++                            }
++                            diag.span_note(
++                                param.uses.iter().map(|x| x.span).collect::<Vec<_>>(),
++                                "parameter used here",
++                            );
++                        },
++                    );
 +                }
-                 self.contains_side_effect |= contains_side_effect;
-                 vars.append(&mut self.ret_vars);
-                 vars
-             })
-             .collect();
-         self.ret_vars.append(&mut expr_vars);
-     }
-     fn connect_assign(&mut self, lhs: &[(HirId, bool)], rhs: &[(HirId, bool)], connect_self: bool) {
-         // if mutable dereference is on assignment it can have side-effect
-         // (this can lead to parameter mutable dereference and change the original value)
-         // too hard to detect whether this value is from parameter, so this would all
-         // check mutable dereference assignment to side effect
-         lhs.iter().filter(|(_, b)| *b).for_each(|(id, _)| {
-             self.has_side_effect.insert(*id);
-             self.contains_side_effect = true;
-         });
-         // there is no connection
-         if lhs.is_empty() || rhs.is_empty() {
-             return;
-         }
-         // by connected rhs in cycle, the connections would decrease
-         // from `n * m` to `n + m`
-         // where `n` and `m` are length of `lhs` and `rhs`.
-         // unwrap is possible since rhs is not empty
-         let rhs_first = rhs.first().unwrap();
-         for (id, _) in lhs.iter() {
-             if connect_self || *id != rhs_first.0 {
-                 self.graph
-                     .entry(*id)
-                     .or_insert_with(FxHashSet::default)
-                     .insert(rhs_first.0);
 +            }
++            self.params.clear();
 +        }
-         let rhs = rhs.iter();
-         izip!(rhs.clone().cycle().skip(1), rhs).for_each(|(from, to)| {
-             if connect_self || from.0 != to.0 {
-                 self.graph.entry(from.0).or_insert_with(FxHashSet::default).insert(to.0);
-             }
-         });
 +    }
++}
 +
-     fn add_side_effect(&mut self, v: Vec<(HirId, bool)>) {
-         for (id, _) in v {
-             self.has_side_effect.insert(id);
-             self.contains_side_effect = true;
-         }
++fn has_matching_substs(kind: FnKind, substs: SubstsRef<'_>) -> bool {
++    match kind {
++        FnKind::Fn => true,
++        FnKind::TraitFn => substs.iter().enumerate().all(|(idx, subst)| match subst.unpack() {
++            GenericArgKind::Lifetime(_) => true,
++            GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
++            GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
++        }),
++        #[allow(trivial_casts)]
++        FnKind::ImplTraitFn(expected_substs) => substs as *const _ as usize == expected_substs,
 +    }
 +}
index 44f153cffac511401f43c871690590fd3d29da43,0000000000000000000000000000000000000000..9602d0d1d2ea1a725963b38f8762a4744c2c4328
mode 100644,000000..100644
--- /dev/null
@@@ -1,186 -1,0 +1,261 @@@
- use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::sugg::Sugg;
- use rustc_hir::LangItem::OptionSome;
- use rustc_hir::{def::Res, BindingAnnotation, Expr, ExprKind, Mutability, PatKind, Path, QPath, UnOp};
 +use clippy_utils::{
 +    can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_lang_ctor, peel_blocks,
 +    peel_hir_expr_while, CaptureKind,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
- use rustc_span::sym;
++use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
++use rustc_hir::{
++    def::Res, Arm, BindingAnnotation, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp,
++};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
-     /// Lints usage of `if let Some(v) = ... { y } else { x }` which is more
 +
 +declare_clippy_lint! {
 +    /// ### What it does
- /// Returns true iff the given expression is the result of calling `Result::ok`
- fn is_result_ok(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
-     if let ExprKind::MethodCall(path, &[ref receiver], _) = &expr.kind {
-         path.ident.name.as_str() == "ok"
-             && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Result)
-     } else {
-         false
-     }
- }
- /// A struct containing information about occurrences of the
- /// `if let Some(..) = .. else` construct that this lint detects.
- struct OptionIfLetElseOccurrence {
++    /// Lints usage of `if let Some(v) = ... { y } else { x }` and
++    /// `match .. { Some(v) => y, None/_ => x }` which are more
 +    /// idiomatically done with `Option::map_or` (if the else bit is a pure
 +    /// expression) or `Option::map_or_else` (if the else bit is an impure
 +    /// expression).
 +    ///
 +    /// ### Why is this bad?
 +    /// Using the dedicated functions of the `Option` type is clearer and
 +    /// more concise than an `if let` expression.
 +    ///
 +    /// ### Known problems
 +    /// This lint uses a deliberately conservative metric for checking
 +    /// if the inside of either body contains breaks or continues which will
 +    /// cause it to not suggest a fix if either block contains a loop with
 +    /// continues or breaks contained within the loop.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let optional: Option<u32> = Some(0);
 +    /// # fn do_complicated_function() -> u32 { 5 };
 +    /// let _ = if let Some(foo) = optional {
 +    ///     foo
 +    /// } else {
 +    ///     5
 +    /// };
++    /// let _ = match optional {
++    ///     Some(val) => val + 1,
++    ///     None => 5
++    /// };
 +    /// let _ = if let Some(foo) = optional {
 +    ///     foo
 +    /// } else {
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// };
 +    /// ```
 +    ///
 +    /// should be
 +    ///
 +    /// ```rust
 +    /// # let optional: Option<u32> = Some(0);
 +    /// # fn do_complicated_function() -> u32 { 5 };
 +    /// let _ = optional.map_or(5, |foo| foo);
++    /// let _ = optional.map_or(5, |val| val + 1);
 +    /// let _ = optional.map_or_else(||{
 +    ///     let y = do_complicated_function();
 +    ///     y*y
 +    /// }, |foo| foo);
 +    /// ```
++    // FIXME: Before moving this lint out of nursery, the lint name needs to be updated. It now also
++    // covers matches and `Result`.
 +    #[clippy::version = "1.47.0"]
 +    pub OPTION_IF_LET_ELSE,
 +    nursery,
 +    "reimplementation of Option::map_or"
 +}
 +
 +declare_lint_pass!(OptionIfLetElse => [OPTION_IF_LET_ELSE]);
 +
- /// If this expression is the option if let/else construct we're detecting, then
- /// this function returns an `OptionIfLetElseOccurrence` struct with details if
- /// this construct is found, or None if this construct is not found.
- fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurrence> {
++/// A struct containing information about occurrences of construct that this lint detects
++///
++/// Such as:
++///
++/// ```ignore
++/// if let Some(..) = {..} else {..}
++/// ```
++/// or
++/// ```ignore
++/// match x {
++///     Some(..) => {..},
++///     None/_ => {..}
++/// }
++/// ```
++struct OptionOccurence {
 +    option: String,
 +    method_sugg: String,
 +    some_expr: String,
 +    none_expr: String,
 +}
 +
 +fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: bool, as_mut: bool) -> String {
 +    format!(
 +        "{}{}",
 +        Sugg::hir_with_macro_callsite(cx, cond_expr, "..").maybe_par(),
 +        if as_mut {
 +            ".as_mut()"
 +        } else if as_ref {
 +            ".as_ref()"
 +        } else {
 +            ""
 +        }
 +    )
 +}
 +
-         if !expr.span.from_expansion(); // Don't lint macros, because it behaves weirdly
-         if !in_constant(cx, expr.hir_id);
-         if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
-             = higher::IfLet::hir(cx, expr);
-         if !is_else_clause(cx.tcx, expr);
-         if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
-         if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
-         if is_lang_ctor(cx, struct_qpath, OptionSome);
-         if let PatKind::Binding(bind_annotation, _, id, None) = &inner_pat.kind;
++fn try_get_option_occurence<'tcx>(
++    cx: &LateContext<'tcx>,
++    pat: &Pat<'tcx>,
++    expr: &Expr<'_>,
++    if_then: &'tcx Expr<'_>,
++    if_else: &'tcx Expr<'_>,
++) -> Option<OptionOccurence> {
++    let cond_expr = match expr.kind {
++        ExprKind::Unary(UnOp::Deref, inner_expr) | ExprKind::AddrOf(_, _, inner_expr) => inner_expr,
++        _ => expr,
++    };
++    let inner_pat = try_get_inner_pat(cx, pat)?;
 +    if_chain! {
++        if let PatKind::Binding(bind_annotation, _, id, None) = inner_pat.kind;
 +        if let Some(some_captures) = can_move_expr_to_closure(cx, if_then);
 +        if let Some(none_captures) = can_move_expr_to_closure(cx, if_else);
 +        if some_captures
 +            .iter()
 +            .filter_map(|(id, &c)| none_captures.get(id).map(|&c2| (c, c2)))
 +            .all(|(x, y)| x.is_imm_ref() && y.is_imm_ref());
-             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
 +        then {
-             let (as_ref, as_mut) = match &let_expr.kind {
++            let capture_mut = if bind_annotation == BindingAnnotation::Mutable { "mut " } else { "" };
 +            let some_body = peel_blocks(if_then);
 +            let none_body = peel_blocks(if_else);
 +            let method_sugg = if eager_or_lazy::switch_to_eager_eval(cx, none_body) { "map_or" } else { "map_or_else" };
 +            let capture_name = id.name.to_ident_string();
-                 _ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
-             };
-             let cond_expr = match let_expr.kind {
-                 // Pointer dereferencing happens automatically, so we can omit it in the suggestion
-                 ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
-                 _ => let_expr,
++            let (as_ref, as_mut) = match &expr.kind {
 +                ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
 +                ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
-             Some(OptionIfLetElseOccurrence {
++                _ => (bind_annotation == BindingAnnotation::Ref, bind_annotation == BindingAnnotation::RefMut),
 +            };
++
 +            // Check if captures the closure will need conflict with borrows made in the scrutinee.
 +            // TODO: check all the references made in the scrutinee expression. This will require interacting
 +            // with the borrow checker. Currently only `<local>[.<field>]*` is checked for.
 +            if as_ref || as_mut {
 +                let e = peel_hir_expr_while(cond_expr, |e| match e.kind {
 +                    ExprKind::Field(e, _) | ExprKind::AddrOf(_, _, e) => Some(e),
 +                    _ => None,
 +                });
 +                if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
 +                    match some_captures.get(local_id)
 +                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
 +                    {
 +                        Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
 +                        Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
 +                        Some(CaptureKind::Ref(Mutability::Not)) | None => (),
 +                    }
 +                }
 +            }
-             })
++
++            return Some(OptionOccurence {
 +                option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
 +                method_sugg: method_sugg.to_string(),
 +                some_expr: format!("|{}{}| {}", capture_mut, capture_name, Sugg::hir_with_macro_callsite(cx, some_body, "..")),
 +                none_expr: format!("{}{}", if method_sugg == "map_or" { "" } else { "|| " }, Sugg::hir_with_macro_callsite(cx, none_body, "..")),
-         }
++            });
++        }
++    }
++
++    None
++}
++
++fn try_get_inner_pat<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option<&'tcx Pat<'tcx>> {
++    if let PatKind::TupleStruct(ref qpath, [inner_pat], ..) = pat.kind {
++        if is_lang_ctor(cx, qpath, OptionSome) || is_lang_ctor(cx, qpath, ResultOk) {
++            return Some(inner_pat);
++        }
++    }
++    None
++}
++
++/// If this expression is the option if let/else construct we're detecting, then
++/// this function returns an `OptionOccurence` struct with details if
++/// this construct is found, or None if this construct is not found.
++fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
++    if let Some(higher::IfLet {
++        let_pat,
++        let_expr,
++        if_then,
++        if_else: Some(if_else),
++    }) = higher::IfLet::hir(cx, expr)
++    {
++        if !is_else_clause(cx.tcx, expr) {
++            return try_get_option_occurence(cx, let_pat, let_expr, if_then, if_else);
++        }
++    }
++    None
++}
++
++fn detect_option_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionOccurence> {
++    if let ExprKind::Match(ex, arms, MatchSource::Normal) = expr.kind {
++        if let Some((let_pat, if_then, if_else)) = try_convert_match(cx, arms) {
++            return try_get_option_occurence(cx, let_pat, ex, if_then, if_else);
++        }
++    }
++    None
++}
++
++fn try_convert_match<'tcx>(
++    cx: &LateContext<'tcx>,
++    arms: &[Arm<'tcx>],
++) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> {
++    if arms.len() == 2 {
++        return if is_none_or_err_arm(cx, &arms[1]) {
++            Some((arms[0].pat, arms[0].body, arms[1].body))
++        } else if is_none_or_err_arm(cx, &arms[0]) {
++            Some((arms[1].pat, arms[1].body, arms[0].body))
 +        } else {
 +            None
-         if let Some(detection) = detect_option_if_let_else(cx, expr) {
++        };
++    }
++    None
++}
++
++fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
++    match arm.pat.kind {
++        PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
++        PatKind::TupleStruct(ref qpath, [first_pat], _) => {
++            is_lang_ctor(cx, qpath, ResultErr) && matches!(first_pat.kind, PatKind::Wild)
++        },
++        PatKind::Wild => true,
++        _ => false,
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
-                 format!("use Option::{} instead of an if let/else", detection.method_sugg).as_str(),
++        // Don't lint macros and constants
++        if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
++            return;
++        }
++
++        let detection = detect_option_if_let_else(cx, expr).or_else(|| detect_option_match(cx, expr));
++        if let Some(det) = detection {
 +            span_lint_and_sugg(
 +                cx,
 +                OPTION_IF_LET_ELSE,
 +                expr.span,
-                     detection.option, detection.method_sugg, detection.none_expr, detection.some_expr,
++                format!("use Option::{} instead of an if let/else", det.method_sugg).as_str(),
 +                "try",
 +                format!(
 +                    "{}.{}({}, {})",
++                    det.option, det.method_sugg, det.none_expr, det.some_expr
 +                ),
 +                Applicability::MaybeIncorrect,
 +            );
 +        }
 +    }
 +}
index eee7642068d6237bebe5fdcae8b91624fe40518b,0000000000000000000000000000000000000000..000b0ba7a148e75e503dd9f73ca2061ead430189
mode 100644,000000..100644
--- /dev/null
@@@ -1,104 -1,0 +1,105 @@@
-             matches!(&peel_hir_expr_refs(expr).0.kind,
 +use clippy_utils::{
 +    diagnostics::span_lint_and_sugg, is_lang_ctor, peel_hir_expr_refs, peel_ref_operators, sugg,
 +    ty::is_type_diagnostic_item,
 +};
 +use rustc_errors::Applicability;
 +use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Checks for binary comparisons to a literal `Option::None`.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// A programmer checking if some `foo` is `None` via a comparison `foo == None`
 +    /// is usually inspired from other programming languages (e.g. `foo is None`
 +    /// in Python).
 +    /// Checking if a value of type `Option<T>` is (not) equal to `None` in that
 +    /// way relies on `T: PartialEq` to do the comparison, which is unneeded.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(f: Option<u32>) -> &'static str {
 +    ///     if f != None { "yay" } else { "nay" }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo(f: Option<u32>) -> &'static str {
 +    ///     if f.is_some() { "yay" } else { "nay" }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.64.0"]
 +    pub PARTIALEQ_TO_NONE,
 +    style,
 +    "Binary comparison to `Option<T>::None` relies on `T: PartialEq`, which is unneeded"
 +}
 +declare_lint_pass!(PartialeqToNone => [PARTIALEQ_TO_NONE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for PartialeqToNone {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        // Skip expanded code, as we have no control over it anyway...
 +        if e.span.from_expansion() {
 +            return;
 +        }
 +
 +        // If the expression is of type `Option`
 +        let is_ty_option =
 +            |expr: &Expr<'_>| is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr).peel_refs(), sym::Option);
 +
 +        // If the expression is a literal `Option::None`
 +        let is_none_ctor = |expr: &Expr<'_>| {
++            !expr.span.from_expansion()
++                && matches!(&peel_hir_expr_refs(expr).0.kind,
 +            ExprKind::Path(p) if is_lang_ctor(cx, p, LangItem::OptionNone))
 +        };
 +
 +        let mut applicability = Applicability::MachineApplicable;
 +
 +        if let ExprKind::Binary(op, left_side, right_side) = e.kind {
 +            // All other comparisons (e.g. `>= None`) have special meaning wrt T
 +            let is_eq = match op.node {
 +                BinOpKind::Eq => true,
 +                BinOpKind::Ne => false,
 +                _ => return,
 +            };
 +
 +            // We are only interested in comparisons between `Option` and a literal `Option::None`
 +            let scrutinee = match (
 +                is_none_ctor(left_side) && is_ty_option(right_side),
 +                is_none_ctor(right_side) && is_ty_option(left_side),
 +            ) {
 +                (true, false) => right_side,
 +                (false, true) => left_side,
 +                _ => return,
 +            };
 +
 +            // Peel away refs/derefs (as long as we don't cross manual deref impls), as
 +            // autoref/autoderef will take care of those
 +            let sugg = format!(
 +                "{}.{}",
 +                sugg::Sugg::hir_with_applicability(cx, peel_ref_operators(cx, scrutinee), "..", &mut applicability)
 +                    .maybe_par(),
 +                if is_eq { "is_none()" } else { "is_some()" }
 +            );
 +
 +            span_lint_and_sugg(
 +                cx,
 +                PARTIALEQ_TO_NONE,
 +                e.span,
 +                "binary comparison to literal `Option::None`",
 +                if is_eq {
 +                    "use `Option::is_none()` instead"
 +                } else {
 +                    "use `Option::is_some()` instead"
 +                },
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
index 964a057f00d32e5c1da41c7a4d1b7a3525710f0c,0000000000000000000000000000000000000000..b432ccb1ee32dd675d054de109fd3ec690fe80f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,230 -1,0 +1,230 @@@
-         if let PatKind::Binding(annot, bind_id, ident, _) = field.kind;
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::higher;
 +use clippy_utils::source::snippet_with_applicability;
 +use clippy_utils::ty::is_type_diagnostic_item;
 +use clippy_utils::{
 +    eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks,
 +    peel_blocks_with_stmt,
 +};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 +use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty::Ty;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, symbol::Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions that could be replaced by the question mark operator.
 +    ///
 +    /// ### Why is this bad?
 +    /// Question mark usage is more idiomatic.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// if option.is_none() {
 +    ///     return None;
 +    /// }
 +    /// ```
 +    ///
 +    /// Could be written:
 +    ///
 +    /// ```ignore
 +    /// option?;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub QUESTION_MARK,
 +    style,
 +    "checks for expressions that could be replaced by the question mark operator"
 +}
 +
 +declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
 +
 +enum IfBlockType<'hir> {
 +    /// An `if x.is_xxx() { a } else { b } ` expression.
 +    ///
 +    /// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
 +    IfIs(
 +        &'hir Expr<'hir>,
 +        Ty<'hir>,
 +        Symbol,
 +        &'hir Expr<'hir>,
 +        Option<&'hir Expr<'hir>>,
 +    ),
 +    /// An `if let Xxx(a) = b { c } else { d }` expression.
 +    ///
 +    /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
 +    /// if_else (d)
 +    IfLet(
 +        &'hir QPath<'hir>,
 +        Ty<'hir>,
 +        Symbol,
 +        &'hir Expr<'hir>,
 +        &'hir Expr<'hir>,
 +        Option<&'hir Expr<'hir>>,
 +    ),
 +}
 +
 +/// Checks if the given expression on the given context matches the following structure:
 +///
 +/// ```ignore
 +/// if option.is_none() {
 +///    return None;
 +/// }
 +/// ```
 +///
 +/// ```ignore
 +/// if result.is_err() {
 +///     return result;
 +/// }
 +/// ```
 +///
 +/// If it matches, it will suggest to use the question mark operator instead
 +fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +    if_chain! {
 +        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
 +        if !is_else_clause(cx.tcx, expr);
 +        if let ExprKind::MethodCall(segment, [caller, ..], _) = &cond.kind;
 +        let caller_ty = cx.typeck_results().expr_ty(caller);
 +        let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
 +        if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
 +            let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx.at(caller.span), cx.param_env) &&
 +                !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
 +            let sugg = if let Some(else_inner) = r#else {
 +                if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
 +                    format!("Some({}?)", receiver_str)
 +                } else {
 +                    return;
 +                }
 +            } else {
 +                format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" })
 +            };
 +
 +            span_lint_and_sugg(
 +                cx,
 +                QUESTION_MARK,
 +                expr.span,
 +                "this block may be rewritten with the `?` operator",
 +                "replace it with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
 +    if_chain! {
 +        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
 +        if !is_else_clause(cx.tcx, expr);
 +        if let PatKind::TupleStruct(ref path1, [field], None) = let_pat.kind;
++        if let PatKind::Binding(annot, bind_id, ident, None) = field.kind;
 +        let caller_ty = cx.typeck_results().expr_ty(let_expr);
 +        let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
 +        if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
 +            || is_early_return(sym::Result, cx, &if_block);
 +        if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
 +        then {
 +            let mut applicability = Applicability::MachineApplicable;
 +            let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
 +            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
 +            let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
 +            let sugg = format!(
 +                "{}{}?{}",
 +                receiver_str,
 +                if by_ref { ".as_ref()" } else { "" },
 +                if requires_semi { ";" } else { "" }
 +            );
 +            span_lint_and_sugg(
 +                cx,
 +                QUESTION_MARK,
 +                expr.span,
 +                "this block may be rewritten with the `?` operator",
 +                "replace it with",
 +                sugg,
 +                applicability,
 +            );
 +        }
 +    }
 +}
 +
 +fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
 +    match *if_block {
 +        IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
 +            // If the block could be identified as `if x.is_none()/is_err()`,
 +            // we then only need to check the if_then return to see if it is none/err.
 +            is_type_diagnostic_item(cx, caller_ty, smbl)
 +                && expr_return_none_or_err(smbl, cx, if_then, caller, None)
 +                && match smbl {
 +                    sym::Option => call_sym == sym!(is_none),
 +                    sym::Result => call_sym == sym!(is_err),
 +                    _ => false,
 +                }
 +        },
 +        IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
 +            is_type_diagnostic_item(cx, let_expr_ty, smbl)
 +                && match smbl {
 +                    sym::Option => {
 +                        // We only need to check `if let Some(x) = option` not `if let None = option`,
 +                        // because the later one will be suggested as `if option.is_none()` thus causing conflict.
 +                        is_lang_ctor(cx, qpath, OptionSome)
 +                            && if_else.is_some()
 +                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None)
 +                    },
 +                    sym::Result => {
 +                        (is_lang_ctor(cx, qpath, ResultOk)
 +                            && if_else.is_some()
 +                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym)))
 +                            || is_lang_ctor(cx, qpath, ResultErr)
 +                                && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym))
 +                    },
 +                    _ => false,
 +                }
 +        },
 +    }
 +}
 +
 +fn expr_return_none_or_err(
 +    smbl: Symbol,
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    cond_expr: &Expr<'_>,
 +    err_sym: Option<Symbol>,
 +) -> bool {
 +    match peel_blocks_with_stmt(expr).kind {
 +        ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym),
 +        ExprKind::Path(ref qpath) => match smbl {
 +            sym::Option => is_lang_ctor(cx, qpath, OptionNone),
 +            sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
 +            _ => false,
 +        },
 +        ExprKind::Call(call_expr, args_expr) => {
 +            if_chain! {
 +                if smbl == sym::Result;
 +                if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind;
 +                if let Some(segment) = path.segments.first();
 +                if let Some(err_sym) = err_sym;
 +                if let Some(arg) = args_expr.first();
 +                if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind;
 +                if let Some(PathSegment { ident, .. }) = arg_path.segments.first();
 +                then {
 +                    return segment.ident.name == sym::Err && err_sym == ident.name;
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for QuestionMark {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        check_is_none_or_err_and_early_return(cx, expr);
 +        check_if_let_some_or_err_and_early_return(cx, expr);
 +    }
 +}
index fbf842c339e4906b3fff22a5e3a2d608bc40e047,0000000000000000000000000000000000000000..490f345d2970777634c6554ec54cbb5fd5c040cb
mode 100644,000000..100644
--- /dev/null
@@@ -1,598 -1,0 +1,537 @@@
- use clippy_utils::{higher, SpanlessEq};
 +use clippy_utils::consts::{constant, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
++use clippy_utils::higher;
 +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use clippy_utils::sugg::Sugg;
 +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
- use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
 +use if_chain::if_chain;
 +use rustc_ast::ast::RangeLimits;
 +use rustc_errors::Applicability;
- use rustc_span::sym;
++use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::{Span, Spanned};
- declare_clippy_lint! {
-     /// ### What it does
-     /// Checks for zipping a collection with the range of
-     /// `0.._.len()`.
-     ///
-     /// ### Why is this bad?
-     /// The code is better expressed with `.enumerate()`.
-     ///
-     /// ### Example
-     /// ```rust
-     /// # let x = vec![1];
-     /// let _ = x.iter().zip(0..x.len());
-     /// ```
-     ///
-     /// Use instead:
-     /// ```rust
-     /// # let x = vec![1];
-     /// let _ = x.iter().enumerate();
-     /// ```
-     #[clippy::version = "pre 1.29.0"]
-     pub RANGE_ZIP_WITH_LEN,
-     complexity,
-     "zipping iterator with a range when `enumerate()` would do"
- }
 +use std::cmp::Ordering;
 +
-     RANGE_ZIP_WITH_LEN,
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for exclusive ranges where 1 is added to the
 +    /// upper bound, e.g., `x..(y+1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an inclusive range
 +    /// like `x..=y`.
 +    ///
 +    /// ### Known problems
 +    /// Will add unnecessary pair of parentheses when the
 +    /// expression is not wrapped in a pair but starts with an opening parenthesis
 +    /// and ends with a closing one.
 +    /// I.e., `let _ = (f()+1)..(f()+1)` results in `let _ = ((f()+1)..=f())`.
 +    ///
 +    /// Also in many cases, inclusive ranges are still slower to run than
 +    /// exclusive ranges, because they essentially add an extra branch that
 +    /// LLVM may fail to hoist out of the loop.
 +    ///
 +    /// This will cause a warning that cannot be fixed if the consumer of the
 +    /// range only accepts a specific range type, instead of the generic
 +    /// `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..(y+1) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..=y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_PLUS_ONE,
 +    pedantic,
 +    "`x..(y+1)` reads better as `x..=y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for inclusive ranges where 1 is subtracted from
 +    /// the upper bound, e.g., `x..=(y-1)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The code is more readable with an exclusive range
 +    /// like `x..y`.
 +    ///
 +    /// ### Known problems
 +    /// This will cause a warning that cannot be fixed if
 +    /// the consumer of the range only accepts a specific range type, instead of
 +    /// the generic `RangeBounds` trait
 +    /// ([#3307](https://github.com/rust-lang/rust-clippy/issues/3307)).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..=(y-1) {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # let x = 0;
 +    /// # let y = 1;
 +    /// for i in x..y {
 +    ///     // ..
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub RANGE_MINUS_ONE,
 +    pedantic,
 +    "`x..=(y-1)` reads better as `x..y`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for range expressions `x..y` where both `x` and `y`
 +    /// are constant and `x` is greater or equal to `y`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Empty ranges yield no values so iterating them is a no-op.
 +    /// Moreover, trying to use a reversed range to index a slice will panic at run-time.
 +    ///
 +    /// ### Example
 +    /// ```rust,no_run
 +    /// fn main() {
 +    ///     (10..=0).for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[3..1];
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn main() {
 +    ///     (0..=10).rev().for_each(|x| println!("{}", x));
 +    ///
 +    ///     let arr = [1, 2, 3, 4, 5];
 +    ///     let sub = &arr[1..3];
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.45.0"]
 +    pub REVERSED_EMPTY_RANGES,
 +    correctness,
 +    "reversing the limits of range expressions, resulting in empty ranges"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for expressions like `x >= 3 && x < 8` that could
 +    /// be more readably expressed as `(3..8).contains(x)`.
 +    ///
 +    /// ### Why is this bad?
 +    /// `contains` expresses the intent better and has less
 +    /// failure modes (such as fencepost errors or using `||` instead of `&&`).
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // given
 +    /// let x = 6;
 +    ///
 +    /// assert!(x >= 3 && x < 8);
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    ///# let x = 6;
 +    /// assert!((3..8).contains(&x));
 +    /// ```
 +    #[clippy::version = "1.49.0"]
 +    pub MANUAL_RANGE_CONTAINS,
 +    style,
 +    "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
 +}
 +
 +pub struct Ranges {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl Ranges {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(Ranges => [
-         match expr.kind {
-             ExprKind::MethodCall(path, args, _) => {
-                 check_range_zip_with_len(cx, path, args, expr.span);
-             },
-             ExprKind::Binary(ref op, l, r) => {
-                 if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
-                     check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
-                 }
-             },
-             _ => {},
 +    RANGE_PLUS_ONE,
 +    RANGE_MINUS_ONE,
 +    REVERSED_EMPTY_RANGES,
 +    MANUAL_RANGE_CONTAINS,
 +]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Ranges {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
-     if_chain! {
-         if path.ident.as_str() == "zip";
-         if let [iter, zip_arg] = args;
-         // `.iter()` call
-         if let ExprKind::MethodCall(iter_path, [iter_caller, ..], _) = iter.kind;
-         if iter_path.ident.name == sym::iter;
-         // range expression in `.zip()` call: `0..x.len()`
-         if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
-         if is_integer_const(cx, start, 0);
-         // `.len()` call
-         if let ExprKind::MethodCall(len_path, [len_caller], _) = end.kind;
-         if len_path.ident.name == sym::len;
-         // `.iter()` and `.len()` called on same `Path`
-         if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_caller.kind;
-         if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_caller.kind;
-         if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
-         then {
-             span_lint(cx,
-                 RANGE_ZIP_WITH_LEN,
-                 span,
-                 &format!("it is more idiomatic to use `{}.iter().enumerate()`",
-                     snippet(cx, iter_caller.span, "_"))
-             );
-         }
-     }
- }
++        if let ExprKind::Binary(ref op, l, r) = expr.kind {
++            if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) {
++                check_possible_range_contains(cx, op.node, l, r, expr, expr.span);
++            }
 +        }
 +
 +        check_exclusive_range_plus_one(cx, expr);
 +        check_inclusive_range_minus_one(cx, expr);
 +        check_reversed_empty_range(cx, expr);
 +    }
 +    extract_msrv_attr!(LateContext);
 +}
 +
 +fn check_possible_range_contains(
 +    cx: &LateContext<'_>,
 +    op: BinOpKind,
 +    left: &Expr<'_>,
 +    right: &Expr<'_>,
 +    expr: &Expr<'_>,
 +    span: Span,
 +) {
 +    if in_constant(cx, expr.hir_id) {
 +        return;
 +    }
 +
 +    let combine_and = match op {
 +        BinOpKind::And | BinOpKind::BitAnd => true,
 +        BinOpKind::Or | BinOpKind::BitOr => false,
 +        _ => return,
 +    };
 +    // value, name, order (higher/lower), inclusiveness
 +    if let (Some(l), Some(r)) = (check_range_bounds(cx, left), check_range_bounds(cx, right)) {
 +        // we only lint comparisons on the same name and with different
 +        // direction
 +        if l.id != r.id || l.ord == r.ord {
 +            return;
 +        }
 +        let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l.expr), &l.val, &r.val);
 +        if combine_and && ord == Some(r.ord) {
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if r.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            // we only lint inclusive lower bounds
 +            if !l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("RangeInclusive", "..=")
 +            } else {
 +                ("Range", "..")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `{}::contains` implementation", range_type),
 +                "use",
 +                format!("({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        } else if !combine_and && ord == Some(l.ord) {
 +            // `!_.contains(_)`
 +            // order lower bound and upper bound
 +            let (l_span, u_span, l_inc, u_inc) = if l.ord == Ordering::Less {
 +                (l.val_span, r.val_span, l.inc, r.inc)
 +            } else {
 +                (r.val_span, l.val_span, r.inc, l.inc)
 +            };
 +            if l_inc {
 +                return;
 +            }
 +            let (range_type, range_op) = if u_inc {
 +                ("Range", "..")
 +            } else {
 +                ("RangeInclusive", "..=")
 +            };
 +            let mut applicability = Applicability::MachineApplicable;
 +            let name = snippet_with_applicability(cx, l.name_span, "_", &mut applicability);
 +            let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
 +            let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
 +            let space = if lo.ends_with('.') { " " } else { "" };
 +            span_lint_and_sugg(
 +                cx,
 +                MANUAL_RANGE_CONTAINS,
 +                span,
 +                &format!("manual `!{}::contains` implementation", range_type),
 +                "use",
 +                format!("!({}{}{}{}).contains(&{})", lo, space, range_op, hi, name),
 +                applicability,
 +            );
 +        }
 +    }
 +
 +    // If the LHS is the same operator, we have to recurse to get the "real" RHS, since they have
 +    // the same operator precedence
 +    if_chain! {
 +        if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind;
 +        if op == lhs_op.node;
 +        let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent());
 +        if let Some(snip) = &snippet_opt(cx, new_span);
 +        // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong
 +        if snip.matches('(').count() == snip.matches(')').count();
 +        then {
 +            check_possible_range_contains(cx, op, new_lhs, right, expr, new_span);
 +        }
 +    }
 +}
 +
 +struct RangeBounds<'a> {
 +    val: Constant,
 +    expr: &'a Expr<'a>,
 +    id: HirId,
 +    name_span: Span,
 +    val_span: Span,
 +    ord: Ordering,
 +    inc: bool,
 +}
 +
 +// Takes a binary expression such as x <= 2 as input
 +// Breaks apart into various pieces, such as the value of the number,
 +// hir id of the variable, and direction/inclusiveness of the operator
 +fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<RangeBounds<'a>> {
 +    if let ExprKind::Binary(ref op, l, r) = ex.kind {
 +        let (inclusive, ordering) = match op.node {
 +            BinOpKind::Gt => (false, Ordering::Greater),
 +            BinOpKind::Ge => (true, Ordering::Greater),
 +            BinOpKind::Lt => (false, Ordering::Less),
 +            BinOpKind::Le => (true, Ordering::Less),
 +            _ => return None,
 +        };
 +        if let Some(id) = path_to_local(l) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: r,
 +                    id,
 +                    name_span: l.span,
 +                    val_span: r.span,
 +                    ord: ordering,
 +                    inc: inclusive,
 +                });
 +            }
 +        } else if let Some(id) = path_to_local(r) {
 +            if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
 +                return Some(RangeBounds {
 +                    val: c,
 +                    expr: l,
 +                    id,
 +                    name_span: r.span,
 +                    val_span: l.span,
 +                    ord: ordering.reverse(),
 +                    inc: inclusive,
 +                });
 +            }
 +        }
 +    }
 +    None
 +}
 +
 +// exclusive range plus one: `x..(y+1)`
 +fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range {
 +            start,
 +            end: Some(end),
 +            limits: RangeLimits::HalfOpen
 +        }) = higher::Range::hir(expr);
 +        if let Some(y) = y_plus_one(cx, end);
 +        then {
 +            let span = if expr.span.from_expansion() {
 +                expr.span
 +                    .ctxt()
 +                    .outer_expn_data()
 +                    .call_site
 +            } else {
 +                expr.span
 +            };
 +            span_lint_and_then(
 +                cx,
 +                RANGE_PLUS_ONE,
 +                span,
 +                "an inclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    if let Some(is_wrapped) = &snippet_opt(cx, span) {
 +                        if is_wrapped.starts_with('(') && is_wrapped.ends_with(')') {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("({}..={})", start, end),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        } else {
 +                            diag.span_suggestion(
 +                                span,
 +                                "use",
 +                                format!("{}..={}", start, end),
 +                                Applicability::MachineApplicable, // snippet
 +                            );
 +                        }
 +                    }
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +// inclusive range minus one: `x..=(y-1)`
 +fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    if_chain! {
 +        if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
 +        if let Some(y) = y_minus_one(cx, end);
 +        then {
 +            span_lint_and_then(
 +                cx,
 +                RANGE_MINUS_ONE,
 +                expr.span,
 +                "an exclusive range would be more readable",
 +                |diag| {
 +                    let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_par().to_string());
 +                    let end = Sugg::hir(cx, y, "y").maybe_par();
 +                    diag.span_suggestion(
 +                        expr.span,
 +                        "use",
 +                        format!("{}..{}", start, end),
 +                        Applicability::MachineApplicable, // snippet
 +                    );
 +                },
 +            );
 +        }
 +    }
 +}
 +
 +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
 +    fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        matches!(
 +            get_parent_expr(cx, expr),
 +            Some(Expr {
 +                kind: ExprKind::Index(..),
 +                ..
 +            })
 +        )
 +    }
 +
 +    fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +        let mut cur_expr = expr;
 +        while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
 +            match higher::ForLoop::hir(parent_expr) {
 +                Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true,
 +                _ => cur_expr = parent_expr,
 +            }
 +        }
 +
 +        false
 +    }
 +
 +    fn is_empty_range(limits: RangeLimits, ordering: Ordering) -> bool {
 +        match limits {
 +            RangeLimits::HalfOpen => ordering != Ordering::Less,
 +            RangeLimits::Closed => ordering == Ordering::Greater,
 +        }
 +    }
 +
 +    if_chain! {
 +        if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
 +        let ty = cx.typeck_results().expr_ty(start);
 +        if let ty::Int(_) | ty::Uint(_) = ty.kind();
 +        if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
 +        if let Some((end_idx, _)) = constant(cx, cx.typeck_results(), end);
 +        if let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx);
 +        if is_empty_range(limits, ordering);
 +        then {
 +            if inside_indexing_expr(cx, expr) {
 +                // Avoid linting `N..N` as it has proven to be useful, see #5689 and #5628 ...
 +                if ordering != Ordering::Equal {
 +                    span_lint(
 +                        cx,
 +                        REVERSED_EMPTY_RANGES,
 +                        expr.span,
 +                        "this range is reversed and using it to index a slice will panic at run-time",
 +                    );
 +                }
 +            // ... except in for loop arguments for backwards compatibility with `reverse_range_loop`
 +            } else if ordering != Ordering::Equal || is_for_loop_arg(cx, expr) {
 +                span_lint_and_then(
 +                    cx,
 +                    REVERSED_EMPTY_RANGES,
 +                    expr.span,
 +                    "this range is empty so it will yield no values",
 +                    |diag| {
 +                        if ordering != Ordering::Equal {
 +                            let start_snippet = snippet(cx, start.span, "_");
 +                            let end_snippet = snippet(cx, end.span, "_");
 +                            let dots = match limits {
 +                                RangeLimits::HalfOpen => "..",
 +                                RangeLimits::Closed => "..="
 +                            };
 +
 +                            diag.span_suggestion(
 +                                expr.span,
 +                                "consider using the following if you are attempting to iterate over this \
 +                                 range in reverse",
 +                                format!("({}{}{}).rev()", end_snippet, dots, start_snippet),
 +                                Applicability::MaybeIncorrect,
 +                            );
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Add, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) => {
 +            if is_integer_const(cx, lhs, 1) {
 +                Some(rhs)
 +            } else if is_integer_const(cx, rhs, 1) {
 +                Some(lhs)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
 +    match expr.kind {
 +        ExprKind::Binary(
 +            Spanned {
 +                node: BinOpKind::Sub, ..
 +            },
 +            lhs,
 +            rhs,
 +        ) if is_integer_const(cx, rhs, 1) => Some(lhs),
 +        _ => None,
 +    }
 +}
index 8db8c4e9b7870117d71022f7baa10809ab1c6bbf,0000000000000000000000000000000000000000..e82aa3a7b9897dd751a40aa5c8a4f59e4de247b6
mode 100644,000000..100644
--- /dev/null
@@@ -1,139 -1,0 +1,139 @@@
-     #[clippy::version = "1.62.0"]
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::higher::VecArgs;
 +use clippy_utils::last_path_segment;
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::paths;
 +use clippy_utils::source::{indent_of, snippet};
 +use clippy_utils::ty::match_type;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, QPath, TyKind};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::{sym, Span, Symbol};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for reference-counted pointers (`Arc`, `Rc`, `rc::Weak`, and `sync::Weak`)
 +    /// in `vec![elem; len]`
 +    ///
 +    /// ### Why is this bad?
 +    /// This will create `elem` once and clone it `len` times - doing so with `Arc`/`Rc`/`Weak`
 +    /// is a bit misleading, as it will create references to the same pointer, rather
 +    /// than different instances.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let v = vec![std::sync::Arc::new("some data".to_string()); 100];
 +    /// // or
 +    /// let v = vec![std::rc::Rc::new("some data".to_string()); 100];
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// // Initialize each value separately:
 +    /// let mut data = Vec::with_capacity(100);
 +    /// for _ in 0..100 {
 +    ///     data.push(std::rc::Rc::new("some data".to_string()));
 +    /// }
 +    ///
 +    /// // Or if you want clones of the same reference,
 +    /// // Create the reference beforehand to clarify that
 +    /// // it should be cloned for each value
 +    /// let data = std::rc::Rc::new("some data".to_string());
 +    /// let v = vec![data; 100];
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub RC_CLONE_IN_VEC_INIT,
 +    suspicious,
 +    "initializing reference-counted pointer in `vec![elem; len]`"
 +}
 +declare_lint_pass!(RcCloneInVecInit => [RC_CLONE_IN_VEC_INIT]);
 +
 +impl LateLintPass<'_> for RcCloneInVecInit {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
 +        let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return; };
 +        let Some(VecArgs::Repeat(elem, len)) = VecArgs::hir(cx, expr) else { return; };
 +        let Some((symbol, func_span)) = ref_init(cx, elem) else { return; };
 +
 +        emit_lint(cx, symbol, macro_call.span, elem, len, func_span);
 +    }
 +}
 +
 +fn loop_init_suggestion(elem: &str, len: &str, indent: &str) -> String {
 +    format!(
 +        r#"{{
 +{indent}    let mut v = Vec::with_capacity({len});
 +{indent}    (0..{len}).for_each(|_| v.push({elem}));
 +{indent}    v
 +{indent}}}"#
 +    )
 +}
 +
 +fn extract_suggestion(elem: &str, len: &str, indent: &str) -> String {
 +    format!(
 +        "{{
 +{indent}    let data = {elem};
 +{indent}    vec![data; {len}]
 +{indent}}}"
 +    )
 +}
 +
 +fn emit_lint(cx: &LateContext<'_>, symbol: Symbol, lint_span: Span, elem: &Expr<'_>, len: &Expr<'_>, func_span: Span) {
 +    let symbol_name = symbol.as_str();
 +
 +    span_lint_and_then(
 +        cx,
 +        RC_CLONE_IN_VEC_INIT,
 +        lint_span,
 +        "initializing a reference-counted pointer in `vec![elem; len]`",
 +        |diag| {
 +            let len_snippet = snippet(cx, len.span, "..");
 +            let elem_snippet = format!("{}(..)", snippet(cx, elem.span.with_hi(func_span.hi()), ".."));
 +            let indentation = " ".repeat(indent_of(cx, lint_span).unwrap_or(0));
 +            let loop_init_suggestion = loop_init_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
 +            let extract_suggestion = extract_suggestion(&elem_snippet, len_snippet.as_ref(), &indentation);
 +
 +            diag.note(format!("each element will point to the same `{symbol_name}` instance"));
 +            diag.span_suggestion(
 +                lint_span,
 +                format!("consider initializing each `{symbol_name}` element individually"),
 +                loop_init_suggestion,
 +                Applicability::HasPlaceholders,
 +            );
 +            diag.span_suggestion(
 +                lint_span,
 +                format!(
 +                    "or if this is intentional, consider extracting the `{symbol_name}` initialization to a variable"
 +                ),
 +                extract_suggestion,
 +                Applicability::HasPlaceholders,
 +            );
 +        },
 +    );
 +}
 +
 +/// Checks whether the given `expr` is a call to `Arc::new`, `Rc::new`, or evaluates to a `Weak`
 +fn ref_init(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(Symbol, Span)> {
 +    if_chain! {
 +        if let ExprKind::Call(func, _args) = expr.kind;
 +        if let ExprKind::Path(ref func_path @ QPath::TypeRelative(ty, _)) = func.kind;
 +        if let TyKind::Path(ref ty_path) = ty.kind;
 +        if let Some(def_id) = cx.qpath_res(ty_path, ty.hir_id).opt_def_id();
 +
 +        then {
 +            if last_path_segment(func_path).ident.name == sym::new
 +                && let Some(symbol) = cx
 +                    .tcx
 +                    .get_diagnostic_name(def_id)
 +                    .filter(|symbol| symbol == &sym::Arc || symbol == &sym::Rc) {
 +                return Some((symbol, func.span));
 +            }
 +
 +            let ty_path = cx.typeck_results().expr_ty(expr);
 +            if match_type(cx, ty_path, &paths::WEAK_RC) || match_type(cx, ty_path, &paths::WEAK_ARC) {
 +                return Some((Symbol::intern("Weak"), func.span));
 +            }
 +        }
 +    }
 +
 +    None
 +}
index db6c97f3739c74068a74e4b26ca4e9f80aae964c,0000000000000000000000000000000000000000..8693ca9af83003f2492158e481fae972649e5276
mode 100644,000000..100644
--- /dev/null
@@@ -1,169 -1,0 +1,171 @@@
-                         cx.tcx.mk_projection(target_id, cx.tcx.mk_substs([GenericArg::from(indexed_ty)].into_iter())),
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::get_parent_expr;
 +use clippy_utils::source::snippet_with_context;
 +use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs};
 +use if_chain::if_chain;
 +use rustc_ast::util::parser::PREC_PREFIX;
 +use rustc_errors::Applicability;
 +use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
 +use rustc_lint::{LateContext, LateLintPass, Lint};
 +use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability};
 +use rustc_middle::ty::subst::GenericArg;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
++use std::iter;
++
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for redundant slicing expressions which use the full range, and
 +    /// do not change the type.
 +    ///
 +    /// ### Why is this bad?
 +    /// It unnecessarily adds complexity to the expression.
 +    ///
 +    /// ### Known problems
 +    /// If the type being sliced has an implementation of `Index<RangeFull>`
 +    /// that actually changes anything then it can't be removed. However, this would be surprising
 +    /// to people reading the code and should have a note with it.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// fn get_slice(x: &[u32]) -> &[u32] {
 +    ///     &x[..]
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```ignore
 +    /// fn get_slice(x: &[u32]) -> &[u32] {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.51.0"]
 +    pub REDUNDANT_SLICING,
 +    complexity,
 +    "redundant slicing of the whole range of a type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for slicing expressions which are equivalent to dereferencing the
 +    /// value.
 +    ///
 +    /// ### Why is this bad?
 +    /// Some people may prefer to dereference rather than slice.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let vec = vec![1, 2, 3];
 +    /// let slice = &vec[..];
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let vec = vec![1, 2, 3];
 +    /// let slice = &*vec;
 +    /// ```
 +    #[clippy::version = "1.61.0"]
 +    pub DEREF_BY_SLICING,
 +    restriction,
 +    "slicing instead of dereferencing"
 +}
 +
 +declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING, DEREF_BY_SLICING]);
 +
 +static REDUNDANT_SLICING_LINT: (&Lint, &str) = (REDUNDANT_SLICING, "redundant slicing of the whole range");
 +static DEREF_BY_SLICING_LINT: (&Lint, &str) = (DEREF_BY_SLICING, "slicing when dereferencing would work");
 +
 +impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        let ctxt = expr.span.ctxt();
 +        if_chain! {
 +            if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
 +            if addressee.span.ctxt() == ctxt;
 +            if let ExprKind::Index(indexed, range) = addressee.kind;
 +            if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull);
 +            then {
 +                let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr));
 +                let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed));
 +                let parent_expr = get_parent_expr(cx, expr);
 +                let needs_parens_for_prefix = parent_expr.map_or(false, |parent| {
 +                    parent.precedence().order() > PREC_PREFIX
 +                });
 +                let mut app = Applicability::MachineApplicable;
 +
 +                let ((lint, msg), help, sugg) = if expr_ty == indexed_ty {
 +                    if expr_ref_count > indexed_ref_count {
 +                        // Indexing takes self by reference and can't return a reference to that
 +                        // reference as it's a local variable. The only way this could happen is if
 +                        // `self` contains a reference to the `Self` type. If this occurs then the
 +                        // lint no longer applies as it's essentially a field access, which is not
 +                        // redundant.
 +                        return;
 +                    }
 +                    let deref_count = indexed_ref_count - expr_ref_count;
 +
 +                    let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut {
 +                        // The slice was used to reborrow the mutable reference.
 +                        (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead")
 +                    } else if matches!(
 +                        parent_expr,
 +                        Some(Expr {
 +                            kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
 +                            ..
 +                        })
 +                    ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
 +                        matches!(a.kind, Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })))
 +                    }) {
 +                        // The slice was used to make a temporary reference.
 +                        (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead")
 +                    } else if deref_count != 0 {
 +                        (DEREF_BY_SLICING_LINT, "", "dereference the original value instead")
 +                    } else {
 +                        (REDUNDANT_SLICING_LINT, "", "use the original value instead")
 +                    };
 +
 +                    let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
 +                    let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix {
 +                        format!("({}{}{})", reborrow_str, "*".repeat(deref_count), snip)
 +                    } else {
 +                        format!("{}{}{}", reborrow_str, "*".repeat(deref_count), snip)
 +                    };
 +
 +                    (lint, help_str, sugg)
 +                } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
 +                    if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
 +                        cx.param_env,
++                        cx.tcx.mk_projection(target_id, cx.tcx.mk_substs(iter::once(GenericArg::from(indexed_ty)))),
 +                    ) {
 +                        if deref_ty == expr_ty {
 +                            let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
 +                            let sugg = if needs_parens_for_prefix {
 +                                format!("(&{}{}*{})", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip)
 +                            } else {
 +                                format!("&{}{}*{}", mutability.prefix_str(), "*".repeat(indexed_ref_count), snip)
 +                            };
 +                            (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg)
 +                        } else {
 +                            return;
 +                        }
 +                    } else {
 +                        return;
 +                    }
 +                } else {
 +                    return;
 +                };
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    lint,
 +                    expr.span,
 +                    msg,
 +                    help,
 +                    sugg,
 +                    app,
 +                );
 +            }
 +        }
 +    }
 +}
index f8801f769e83d6cc2dddd94abac1f4bbc2f10da8,0000000000000000000000000000000000000000..2d751c274679f9b64eef70d1b8af54d45bd7aad6
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,117 @@@
-     fn visit_type(&mut self, ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{meets_msrv, msrvs};
 +use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for constants and statics with an explicit `'static` lifetime.
 +    ///
 +    /// ### Why is this bad?
 +    /// Adding `'static` to every reference can create very
 +    /// complicated types.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// const FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
 +    /// &[...]
 +    /// static FOO: &'static [(&'static str, &'static str, fn(&Bar) -> bool)] =
 +    /// &[...]
 +    /// ```
 +    /// This code can be rewritten as
 +    /// ```ignore
 +    ///  const FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
 +    ///  static FOO: &[(&str, &str, fn(&Bar) -> bool)] = &[...]
 +    /// ```
 +    #[clippy::version = "1.37.0"]
 +    pub REDUNDANT_STATIC_LIFETIMES,
 +    style,
 +    "Using explicit `'static` lifetime for constants or statics when elision rules would allow omitting them."
 +}
 +
 +pub struct RedundantStaticLifetimes {
 +    msrv: Option<RustcVersion>,
 +}
 +
 +impl RedundantStaticLifetimes {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +
 +impl_lint_pass!(RedundantStaticLifetimes => [REDUNDANT_STATIC_LIFETIMES]);
 +
 +impl RedundantStaticLifetimes {
 +    // Recursively visit types
-                 self.visit_type(ty, cx, reason);
++    fn visit_type(ty: &Ty, cx: &EarlyContext<'_>, reason: &str) {
 +        match ty.kind {
 +            // Be careful of nested structures (arrays and tuples)
 +            TyKind::Array(ref ty, _) | TyKind::Slice(ref ty) => {
-                     self.visit_type(tup_ty, cx, reason);
++                Self::visit_type(ty, cx, reason);
 +            },
 +            TyKind::Tup(ref tup) => {
 +                for tup_ty in tup {
-                 self.visit_type(&borrow_type.ty, cx, reason);
++                    Self::visit_type(tup_ty, cx, reason);
 +                }
 +            },
 +            // This is what we are looking for !
 +            TyKind::Rptr(ref optional_lifetime, ref borrow_type) => {
 +                // Match the 'static lifetime
 +                if let Some(lifetime) = *optional_lifetime {
 +                    match borrow_type.ty.kind {
 +                        TyKind::Path(..) | TyKind::Slice(..) | TyKind::Array(..) | TyKind::Tup(..) => {
 +                            if lifetime.ident.name == rustc_span::symbol::kw::StaticLifetime {
 +                                let snip = snippet(cx, borrow_type.ty.span, "<type>");
 +                                let sugg = format!("&{}", snip);
 +                                span_lint_and_then(
 +                                    cx,
 +                                    REDUNDANT_STATIC_LIFETIMES,
 +                                    lifetime.ident.span,
 +                                    reason,
 +                                    |diag| {
 +                                        diag.span_suggestion(
 +                                            ty.span,
 +                                            "consider removing `'static`",
 +                                            sugg,
 +                                            Applicability::MachineApplicable, //snippet
 +                                        );
 +                                    },
 +                                );
 +                            }
 +                        },
 +                        _ => {},
 +                    }
 +                }
-                 self.visit_type(var_type, cx, "constants have by default a `'static` lifetime");
++                Self::visit_type(&borrow_type.ty, cx, reason);
 +            },
 +            _ => {},
 +        }
 +    }
 +}
 +
 +impl EarlyLintPass for RedundantStaticLifetimes {
 +    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
 +        if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) {
 +            return;
 +        }
 +
 +        if !item.span.from_expansion() {
 +            if let ItemKind::Const(_, ref var_type, _) = item.kind {
-                 self.visit_type(var_type, cx, "statics have by default a `'static` lifetime");
++                Self::visit_type(var_type, cx, "constants have by default a `'static` lifetime");
 +                // Don't check associated consts because `'static` cannot be elided on those (issue
 +                // #2438)
 +            }
 +
 +            if let ItemKind::Static(ref var_type, _, _) = item.kind {
++                Self::visit_type(var_type, cx, "statics have by default a `'static` lifetime");
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(EarlyContext);
 +}
index 1d9a2abf7066c8819b61a42faacde644a72eb94a,0000000000000000000000000000000000000000..1926661c59603a886b5bfe369532d42df86a497e
mode 100644,000000..100644
--- /dev/null
@@@ -1,333 -1,0 +1,325 @@@
- use rustc_ast::ast::Attribute;
 +use clippy_utils::diagnostics::span_lint_hir_and_then;
 +use clippy_utils::source::{snippet_opt, snippet_with_context};
 +use clippy_utils::{fn_def_id, path_to_local_id};
 +use if_chain::if_chain;
- use rustc_span::sym;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, HirId, MatchSource, PatKind, StmtKind};
 +use rustc_lint::{LateContext, LateLintPass, LintContext};
 +use rustc_middle::lint::in_external_macro;
 +use rustc_middle::ty::subst::GenericArgKind;
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
- fn attr_is_cfg(attr: &Attribute) -> bool {
-     attr.meta_item_list().is_some() && attr.has_name(sym::cfg)
- }
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for `let`-bindings, which are subsequently
 +    /// returned.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is just extraneous code. Remove it to make your code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo() -> String {
 +    ///     let x = String::new();
 +    ///     x
 +    /// }
 +    /// ```
 +    /// instead, use
 +    /// ```
 +    /// fn foo() -> String {
 +    ///     String::new()
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub LET_AND_RETURN,
 +    style,
 +    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for return statements at the end of a block.
 +    ///
 +    /// ### Why is this bad?
 +    /// Removing the `return` and semicolon will make the code
 +    /// more rusty.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     return x;
 +    /// }
 +    /// ```
 +    /// simplify to
 +    /// ```rust
 +    /// fn foo(x: usize) -> usize {
 +    ///     x
 +    /// }
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NEEDLESS_RETURN,
 +    style,
 +    "using a return statement like `return expr;` where an expression would suffice"
 +}
 +
 +#[derive(PartialEq, Eq, Copy, Clone)]
 +enum RetReplacement {
 +    Empty,
 +    Block,
 +    Unit,
 +}
 +
 +declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN]);
 +
 +impl<'tcx> LateLintPass<'tcx> for Return {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
 +        // we need both a let-binding stmt and an expr
 +        if_chain! {
 +            if let Some(retexpr) = block.expr;
 +            if let Some(stmt) = block.stmts.iter().last();
 +            if let StmtKind::Local(local) = &stmt.kind;
 +            if local.ty.is_none();
 +            if cx.tcx.hir().attrs(local.hir_id).is_empty();
 +            if let Some(initexpr) = &local.init;
 +            if let PatKind::Binding(_, local_id, _, _) = local.pat.kind;
 +            if path_to_local_id(retexpr, local_id);
 +            if !last_statement_borrows(cx, initexpr);
 +            if !in_external_macro(cx.sess(), initexpr.span);
 +            if !in_external_macro(cx.sess(), retexpr.span);
 +            if !local.span.from_expansion();
 +            then {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    LET_AND_RETURN,
 +                    retexpr.hir_id,
 +                    retexpr.span,
 +                    "returning the result of a `let` binding from a block",
 +                    |err| {
 +                        err.span_label(local.span, "unnecessary `let` binding");
 +
 +                        if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
 +                            if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
 +                                snippet.push_str(" as _");
 +                            }
 +                            err.multipart_suggestion(
 +                                "return the expression directly",
 +                                vec![
 +                                    (local.span, String::new()),
 +                                    (retexpr.span, snippet),
 +                                ],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        } else {
 +                            err.span_help(initexpr.span, "this expression can be directly returned");
 +                        }
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        kind: FnKind<'tcx>,
 +        _: &'tcx FnDecl<'tcx>,
 +        body: &'tcx Body<'tcx>,
 +        _: Span,
 +        _: HirId,
 +    ) {
 +        match kind {
 +            FnKind::Closure => {
 +                // when returning without value in closure, replace this `return`
 +                // with an empty block to prevent invalid suggestion (see #6501)
 +                let replacement = if let ExprKind::Ret(None) = &body.value.kind {
 +                    RetReplacement::Block
 +                } else {
 +                    RetReplacement::Empty
 +                };
 +                check_final_expr(cx, &body.value, Some(body.value.span), replacement);
 +            },
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                if let ExprKind::Block(block, _) = body.value.kind {
 +                    check_block_return(cx, block);
 +                }
 +            },
 +        }
 +    }
 +}
 +
-             // allow `#[cfg(a)] return a; #[cfg(b)] return b;`
-             let attrs = cx.tcx.hir().attrs(expr.hir_id);
-             if !attrs.iter().any(attr_is_cfg) {
 +fn check_block_return<'tcx>(cx: &LateContext<'tcx>, block: &Block<'tcx>) {
 +    if let Some(expr) = block.expr {
 +        check_final_expr(cx, expr, Some(expr.span), RetReplacement::Empty);
 +    } else if let Some(stmt) = block.stmts.iter().last() {
 +        match stmt.kind {
 +            StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
 +                check_final_expr(cx, expr, Some(stmt.span), RetReplacement::Empty);
 +            },
 +            _ => (),
 +        }
 +    }
 +}
 +
 +fn check_final_expr<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'tcx>,
 +    span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    match expr.kind {
 +        // simple return is always "bad"
 +        ExprKind::Ret(ref inner) => {
++            if cx.tcx.hir().attrs(expr.hir_id).is_empty() {
 +                let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
 +                if !borrows {
 +                    emit_return_lint(
 +                        cx,
 +                        inner.map_or(expr.hir_id, |inner| inner.hir_id),
 +                        span.expect("`else return` is not possible"),
 +                        inner.as_ref().map(|i| i.span),
 +                        replacement,
 +                    );
 +                }
 +            }
 +        },
 +        // a whole block? check it!
 +        ExprKind::Block(block, _) => {
 +            check_block_return(cx, block);
 +        },
 +        ExprKind::If(_, then, else_clause_opt) => {
 +            if let ExprKind::Block(ifblock, _) = then.kind {
 +                check_block_return(cx, ifblock);
 +            }
 +            if let Some(else_clause) = else_clause_opt {
 +                check_final_expr(cx, else_clause, None, RetReplacement::Empty);
 +            }
 +        },
 +        // a match expr, check all arms
 +        // an if/if let expr, check both exprs
 +        // note, if without else is going to be a type checking error anyways
 +        // (except for unit type functions) so we don't match it
 +        ExprKind::Match(_, arms, MatchSource::Normal) => {
 +            for arm in arms.iter() {
 +                check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Unit);
 +            }
 +        },
 +        ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
 +        _ => (),
 +    }
 +}
 +
 +fn emit_return_lint(
 +    cx: &LateContext<'_>,
 +    emission_place: HirId,
 +    ret_span: Span,
 +    inner_span: Option<Span>,
 +    replacement: RetReplacement,
 +) {
 +    if ret_span.from_expansion() {
 +        return;
 +    }
 +    match inner_span {
 +        Some(inner_span) => {
 +            let mut applicability = Applicability::MachineApplicable;
 +            span_lint_hir_and_then(
 +                cx,
 +                NEEDLESS_RETURN,
 +                emission_place,
 +                ret_span,
 +                "unneeded `return` statement",
 +                |diag| {
 +                    let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
 +                    diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
 +                },
 +            );
 +        },
 +        None => match replacement {
 +            RetReplacement::Empty => {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    emission_place,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            ret_span,
 +                            "remove `return`",
 +                            String::new(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +            RetReplacement::Block => {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    emission_place,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            ret_span,
 +                            "replace `return` with an empty block",
 +                            "{}".to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +            RetReplacement::Unit => {
 +                span_lint_hir_and_then(
 +                    cx,
 +                    NEEDLESS_RETURN,
 +                    emission_place,
 +                    ret_span,
 +                    "unneeded `return` statement",
 +                    |diag| {
 +                        diag.span_suggestion(
 +                            ret_span,
 +                            "replace `return` with a unit value",
 +                            "()".to_string(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            },
 +        },
 +    }
 +}
 +
 +fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
 +    let mut visitor = BorrowVisitor { cx, borrows: false };
 +    walk_expr(&mut visitor, expr);
 +    visitor.borrows
 +}
 +
 +struct BorrowVisitor<'a, 'tcx> {
 +    cx: &'a LateContext<'tcx>,
 +    borrows: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for BorrowVisitor<'_, 'tcx> {
 +    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
 +        if self.borrows || expr.span.from_expansion() {
 +            return;
 +        }
 +
 +        if let Some(def_id) = fn_def_id(self.cx, expr) {
 +            self.borrows = self
 +                .cx
 +                .tcx
 +                .fn_sig(def_id)
 +                .output()
 +                .skip_binder()
 +                .walk()
 +                .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
 +        }
 +
 +        walk_expr(self, expr);
 +    }
 +}
index d07c26d7c8975da63108f5ec5e9af7efd2ab8ebd,0000000000000000000000000000000000000000..9cea4d8806710a84b9f1980361f0c2e407a64321
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,91 @@@
- use clippy_utils::ty::{contains_adt_constructor, contains_ty};
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::return_ty;
-         } else if !contains_ty(ret_ty, self_ty) {
++use clippy_utils::ty::contains_adt_constructor;
 +use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Warns when constructors have the same name as their types.
 +    ///
 +    /// ### Why is this bad?
 +    /// Repeating the name of the type is redundant.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// struct Foo {}
 +    ///
 +    /// impl Foo {
 +    ///     pub fn foo() -> Foo {
 +    ///         Foo {}
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// struct Foo {}
 +    ///
 +    /// impl Foo {
 +    ///     pub fn new() -> Foo {
 +    ///         Foo {}
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.55.0"]
 +    pub SELF_NAMED_CONSTRUCTORS,
 +    style,
 +    "method should not have the same name as the type it is implemented for"
 +}
 +
 +declare_lint_pass!(SelfNamedConstructors => [SELF_NAMED_CONSTRUCTORS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for SelfNamedConstructors {
 +    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
 +        match impl_item.kind {
 +            ImplItemKind::Fn(ref sig, _) => {
 +                if sig.decl.implicit_self.has_implicit_self() {
 +                    return;
 +                }
 +            },
 +            _ => return,
 +        }
 +
 +        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 ret_ty = return_ty(cx, impl_item.hir_id());
 +
 +        // Do not check trait impls
 +        if matches!(item.kind, ItemKind::Impl(Impl { of_trait: Some(_), .. })) {
 +            return;
 +        }
 +
 +        // Ensure method is constructor-like
 +        if let Some(self_adt) = self_ty.ty_adt_def() {
 +            if !contains_adt_constructor(ret_ty, self_adt) {
 +                return;
 +            }
++        } else if !ret_ty.contains(self_ty) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let Some(self_def) = self_ty.ty_adt_def();
 +            if let Some(self_local_did) = self_def.did().as_local();
 +            let self_id = cx.tcx.hir().local_def_id_to_hir_id(self_local_did);
 +            if let Some(Node::Item(x)) = cx.tcx.hir().find(self_id);
 +            let type_name = x.ident.name.as_str().to_lowercase();
 +            if impl_item.ident.name.as_str() == type_name || impl_item.ident.name.as_str().replace('_', "") == type_name;
 +
 +            then {
 +                span_lint(
 +                    cx,
 +                    SELF_NAMED_CONSTRUCTORS,
 +                    impl_item.span,
 +                    &format!("constructor `{}` has the same name as the type", impl_item.ident.name),
 +                );
 +            }
 +        }
 +    }
 +}
index 0a42a31fb8cf9e0a96dac08ede5a215e3e736880,0000000000000000000000000000000000000000..2ffa022b04f7a4b870e32717c5cd45aa189d23cf
mode 100644,000000..100644
--- /dev/null
@@@ -1,376 -1,0 +1,402 @@@
- use rustc_data_structures::fx::FxHashMap;
 +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 +use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use clippy_utils::{SpanlessEq, SpanlessHash};
 +use core::hash::{Hash, Hasher};
 +use if_chain::if_chain;
 +use itertools::Itertools;
-         check_bounds_or_where_duplication(cx, gen);
++use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_errors::Applicability;
 +use rustc_hir::def::Res;
 +use rustc_hir::{
 +    GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
 +    TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
 +};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::{BytePos, Span};
++use std::collections::hash_map::Entry;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about unnecessary type repetitions in trait bounds
 +    ///
 +    /// ### Why is this bad?
 +    /// Repeating the type for every bound makes the code
 +    /// less readable than combining the bounds
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy, T: Clone {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// pub fn foo<T>(t: T) where T: Copy + Clone {}
 +    /// ```
 +    #[clippy::version = "1.38.0"]
 +    pub TYPE_REPETITION_IN_BOUNDS,
 +    nursery,
 +    "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases where generics are being used and multiple
 +    /// syntax specifications for trait bounds are used simultaneously.
 +    ///
 +    /// ### Why is this bad?
 +    /// Duplicate bounds makes the code
 +    /// less readable than specifying them only once.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn func<T: Clone + Default>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # mod hidden {
 +    /// fn func<T: Clone + Default>(arg: T) {}
 +    /// # }
 +    ///
 +    /// // or
 +    ///
 +    /// fn func<T>(arg: T) where T: Clone + Default {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// fn foo<T: Default + Default>(bar: T) {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo<T: Default>(bar: T) {}
 +    /// ```
 +    ///
 +    /// ```rust
 +    /// fn foo<T>(bar: T) where T: Default + Default {}
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn foo<T>(bar: T) where T: Default {}
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRAIT_DUPLICATION_IN_BOUNDS,
 +    nursery,
 +    "check if the same trait bounds are specified more than once during a generic declaration"
 +}
 +
 +#[derive(Copy, Clone)]
 +pub struct TraitBounds {
 +    max_trait_bounds: u64,
 +}
 +
 +impl TraitBounds {
 +    #[must_use]
 +    pub fn new(max_trait_bounds: u64) -> Self {
 +        Self { max_trait_bounds }
 +    }
 +}
 +
 +impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 +    fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        self.check_type_repetition(cx, gen);
 +        check_trait_bound_duplication(cx, gen);
-     if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
 +    }
 +
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
 +        // special handling for self trait bounds as these are not considered generics
 +        // ie. trait Foo: Display {}
 +        if let Item {
 +            kind: ItemKind::Trait(_, _, _, bounds, ..),
 +            ..
 +        } = item
 +        {
 +            rollup_traits(cx, bounds, "these bounds contain repeated elements");
 +        }
 +    }
 +
 +    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
 +        let mut self_bounds_map = FxHashMap::default();
 +
 +        for predicate in item.generics.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
 +                if bound_predicate.origin != PredicateOrigin::ImplTrait;
 +                if !bound_predicate.span.from_expansion();
 +                if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
 +                if let Some(PathSegment {
 +                    res: Some(Res::SelfTy{ trait_: Some(def_id), alias_to: _ }), ..
 +                }) = segments.first();
 +                if let Some(
 +                    Node::Item(
 +                        Item {
 +                            kind: ItemKind::Trait(_, _, _, self_bounds, _),
 +                            .. }
 +                        )
 +                    ) = cx.tcx.hir().get_if_local(*def_id);
 +                then {
 +                    if self_bounds_map.is_empty() {
 +                        for bound in self_bounds.iter() {
 +                            let Some((self_res, self_segments, _)) = get_trait_info_from_bound(bound) else { continue };
 +                            self_bounds_map.insert(self_res, self_segments);
 +                        }
 +                    }
 +
 +                    bound_predicate
 +                        .bounds
 +                        .iter()
 +                        .filter_map(get_trait_info_from_bound)
 +                        .for_each(|(trait_item_res, trait_item_segments, span)| {
 +                            if let Some(self_segments) = self_bounds_map.get(&trait_item_res) {
 +                                if SpanlessEq::new(cx).eq_path_segments(self_segments, trait_item_segments) {
 +                                    span_lint_and_help(
 +                                        cx,
 +                                        TRAIT_DUPLICATION_IN_BOUNDS,
 +                                        span,
 +                                        "this trait bound is already specified in trait declaration",
 +                                        None,
 +                                        "consider removing this trait bound",
 +                                    );
 +                                }
 +                            }
 +                        });
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl TraitBounds {
 +    fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
 +        struct SpanlessTy<'cx, 'tcx> {
 +            ty: &'tcx Ty<'tcx>,
 +            cx: &'cx LateContext<'tcx>,
 +        }
 +        impl PartialEq for SpanlessTy<'_, '_> {
 +            fn eq(&self, other: &Self) -> bool {
 +                let mut eq = SpanlessEq::new(self.cx);
 +                eq.inter_expr().eq_ty(self.ty, other.ty)
 +            }
 +        }
 +        impl Hash for SpanlessTy<'_, '_> {
 +            fn hash<H: Hasher>(&self, h: &mut H) {
 +                let mut t = SpanlessHash::new(self.cx);
 +                t.hash_ty(self.ty);
 +                h.write_u64(t.finish());
 +            }
 +        }
 +        impl Eq for SpanlessTy<'_, '_> {}
 +
 +        if gen.span.from_expansion() {
 +            return;
 +        }
 +        let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
 +        let mut applicability = Applicability::MaybeIncorrect;
 +        for bound in gen.predicates {
 +            if_chain! {
 +                if let WherePredicate::BoundPredicate(ref p) = bound;
 +                if p.origin != PredicateOrigin::ImplTrait;
 +                if p.bounds.len() as u64 <= self.max_trait_bounds;
 +                if !p.span.from_expansion();
 +                if let Some(ref v) = map.insert(
 +                    SpanlessTy { ty: p.bounded_ty, cx },
 +                    p.bounds.iter().collect::<Vec<_>>()
 +                );
 +
 +                then {
 +                    let trait_bounds = v
 +                        .iter()
 +                        .copied()
 +                        .chain(p.bounds.iter())
 +                        .filter_map(get_trait_info_from_bound)
 +                        .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability))
 +                        .join(" + ");
 +                    let hint_string = format!(
 +                        "consider combining the bounds: `{}: {}`",
 +                        snippet(cx, p.bounded_ty.span, "_"),
 +                        trait_bounds,
 +                    );
 +                    span_lint_and_help(
 +                        cx,
 +                        TYPE_REPETITION_IN_BOUNDS,
 +                        p.span,
 +                        "this type has already been used as a bound predicate",
 +                        None,
 +                        &hint_string,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-     let mut map = FxHashMap::<_, Vec<_>>::default();
-     for predicate in gen.predicates {
++    if gen.span.from_expansion() {
 +        return;
 +    }
 +
-             if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
++    // Explanation:
++    // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
++    // where T: Clone + Default, { unimplemented!(); }
++    //       ^^^^^^^^^^^^^^^^^^
++    //       |
++    // collects each of these where clauses into a set keyed by generic name and comparable trait
++    // eg. (T, Clone)
++    let where_predicates = gen
++        .predicates
++        .iter()
++        .filter_map(|pred| {
++            if_chain! {
++                if pred.in_where_clause();
++                if let WherePredicate::BoundPredicate(bound_predicate) = pred;
++                if let TyKind::Path(QPath::Resolved(_, path)) =  bound_predicate.bounded_ty.kind;
++                then {
++                    return Some(
++                        rollup_traits(cx, bound_predicate.bounds, "these where clauses contain repeated elements")
++                        .into_iter().map(|(trait_ref, _)| (path.res, trait_ref)))
++                }
++            }
++            None
++        })
++        .flatten()
++        .collect::<FxHashSet<_>>();
++
++    // Explanation:
++    // fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z) ...
++    //            ^^^^^^^^^^^^^^^^^^  ^^^^^^^
++    //            |
++    // compare trait bounds keyed by generic name and comparable trait to collected where
++    // predicates eg. (T, Clone)
++    for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
 +        if_chain! {
-             if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
-             if let Some(segment) = segments.first();
++            if let WherePredicate::BoundPredicate(bound_predicate) = predicate;
 +            if bound_predicate.origin != PredicateOrigin::ImplTrait;
 +            if !bound_predicate.span.from_expansion();
-                 for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
-                     let trait_resolutions_direct = map.entry(segment.ident).or_default();
-                     if let Some((_, span_direct)) = trait_resolutions_direct
-                                                 .iter()
-                                                 .find(|(res_direct, _)| *res_direct == res_where) {
++            if let TyKind::Path(QPath::Resolved(_, path)) =  bound_predicate.bounded_ty.kind;
 +            then {
-                             *span_direct,
++                let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
++                for (trait_ref, span) in traits {
++                    let key = (path.res, trait_ref);
++                    if where_predicates.contains(&key) {
 +                        span_lint_and_help(
 +                            cx,
 +                            TRAIT_DUPLICATION_IN_BOUNDS,
-                         );
-                     }
-                     else {
-                         trait_resolutions_direct.push((res_where, span_where));
++                            span,
 +                            "this trait bound is already specified in the where clause",
 +                            None,
 +                            "consider removing this trait bound",
- #[derive(PartialEq, Eq, Hash, Debug)]
++                            );
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
- fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-     if gen.span.from_expansion() {
-         return;
-     }
-     for predicate in gen.predicates {
-         if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
-             let msg = if predicate.in_where_clause() {
-                 "these where clauses contain repeated elements"
-             } else {
-                 "these bounds contain repeated elements"
-             };
-             rollup_traits(cx, bound_predicate.bounds, msg);
-         }
++#[derive(Clone, PartialEq, Eq, Hash, Debug)]
 +struct ComparableTraitRef(Res, Vec<Res>);
- fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
++impl Default for ComparableTraitRef {
++    fn default() -> Self {
++        Self(Res::Err, Vec::new())
 +    }
 +}
 +
 +fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
 +    if let GenericBound::Trait(t, tbm) = bound {
 +        let trait_path = t.trait_ref.path;
 +        let trait_span = {
 +            let path_span = trait_path.span;
 +            if let TraitBoundModifier::Maybe = tbm {
 +                path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
 +            } else {
 +                path_span
 +            }
 +        };
 +        Some((trait_path.res, trait_path.segments, trait_span))
 +    } else {
 +        None
 +    }
 +}
 +
 +// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
 +fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
 +    ComparableTraitRef(
 +        trait_ref.path.res,
 +        trait_ref
 +            .path
 +            .segments
 +            .iter()
 +            .filter_map(|segment| {
 +                // get trait bound type arguments
 +                Some(segment.args?.args.iter().filter_map(|arg| {
 +                    if_chain! {
 +                        if let GenericArg::Type(ty) = arg;
 +                        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
 +                        then { return Some(path.res) }
 +                    }
 +                    None
 +                }))
 +            })
 +            .flatten()
 +            .collect(),
 +    )
 +}
 +
-         if map.insert(comparable_bound, span_direct).is_some() {
-             repeated_res = true;
++fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) -> Vec<(ComparableTraitRef, Span)> {
 +    let mut map = FxHashMap::default();
 +    let mut repeated_res = false;
 +
 +    let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
 +        if let GenericBound::Trait(t, _) = bound {
 +            Some((into_comparable_trait_ref(&t.trait_ref), t.span))
 +        } else {
 +            None
 +        }
 +    };
 +
++    let mut i = 0usize;
 +    for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
 +        let (comparable_bound, span_direct) = bound;
-             let mut traits = map.values()
-                 .filter_map(|span| snippet_opt(cx, *span))
++        match map.entry(comparable_bound) {
++            Entry::Occupied(_) => repeated_res = true,
++            Entry::Vacant(e) => {
++                e.insert((span_direct, i));
++                i += 1;
++            },
 +        }
 +    }
 +
++    // Put bounds in source order
++    let mut comparable_bounds = vec![Default::default(); map.len()];
++    for (k, (v, i)) in map {
++        comparable_bounds[i] = (k, v);
++    }
++
 +    if_chain! {
 +        if repeated_res;
 +        if let [first_trait, .., last_trait] = bounds;
 +        then {
 +            let all_trait_span = first_trait.span().to(last_trait.span());
 +
-             traits.sort_unstable();
++            let traits = comparable_bounds.iter()
++                .filter_map(|&(_, span)| snippet_opt(cx, span))
 +                .collect::<Vec<_>>();
 +            let traits = traits.join(" + ");
 +
 +            span_lint_and_sugg(
 +                cx,
 +                TRAIT_DUPLICATION_IN_BOUNDS,
 +                all_trait_span,
 +                msg,
 +                "try",
 +                traits,
 +                Applicability::MachineApplicable
 +            );
 +        }
 +    }
++
++    comparable_bounds
 +}
index 5f3e98144f42ddfb5ccf9ce5021e652e0369a08d,0000000000000000000000000000000000000000..424a6e9264e4b96680b6e4a09cfab81e2a36083f
mode 100644,000000..100644
--- /dev/null
@@@ -1,460 -1,0 +1,485 @@@
 +mod crosspointer_transmute;
 +mod transmute_float_to_int;
 +mod transmute_int_to_bool;
 +mod transmute_int_to_char;
 +mod transmute_int_to_float;
 +mod transmute_num_to_bytes;
 +mod transmute_ptr_to_ptr;
 +mod transmute_ptr_to_ref;
 +mod transmute_ref_to_ref;
 +mod transmute_undefined_repr;
 +mod transmutes_expressible_as_ptr_casts;
++mod transmuting_null;
 +mod unsound_collection_transmute;
 +mod useless_transmute;
 +mod utils;
 +mod wrong_transmute;
 +
 +use clippy_utils::in_constant;
 +use if_chain::if_chain;
 +use rustc_hir::{Expr, ExprKind, QPath};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes that can't ever be correct on any
 +    /// architecture.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's basically guaranteed to be undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// When accessing C, users might want to store pointer
 +    /// sized objects in `extradata` arguments to save an allocation.
 +    ///
 +    /// ### Example
 +    /// ```ignore
 +    /// let ptr: *const T = core::intrinsics::transmute('x')
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRONG_TRANSMUTE,
 +    correctness,
 +    "transmutes that are confusing at best, undefined behavior at worst and always useless"
 +}
 +
 +// FIXME: Move this to `complexity` again, after #5343 is fixed
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes to the original type of the object
 +    /// and transmutes that could be a cast.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. The code tricks people into thinking that
 +    /// something complex is going on.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USELESS_TRANSMUTE,
 +    complexity,
 +    "transmutes that have the same to and from types or could be a cast/coercion"
 +}
 +
 +// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///Checks for transmutes that could be a pointer cast.
 +    ///
 +    /// ### Why is this bad?
 +    /// Readability. The code tricks people into thinking that
 +    /// something complex is going on.
 +    ///
 +    /// ### Example
 +    ///
 +    /// ```rust
 +    /// # let p: *const [i32] = &[];
 +    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// # let p: *const [i32] = &[];
 +    /// p as *const [u16];
 +    /// ```
 +    #[clippy::version = "1.47.0"]
 +    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    complexity,
 +    "transmutes that could be a pointer cast"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between a type `T` and `*T`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's easy to mistakenly transmute between a type and a
 +    /// pointer to that type.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// core::intrinsics::transmute(t) // where the result type is the same as
 +    ///                                // `*t` or `&t`'s
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub CROSSPOINTER_TRANSMUTE,
 +    complexity,
 +    "transmutes that have to or from types that are a pointer to the other"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a pointer to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// This can always be rewritten with `&` and `*`.
 +    ///
 +    /// ### Known problems
 +    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
 +    /// while dereferencing raw pointer is not stable yet.
 +    /// If you need to do this in those places,
 +    /// you would have to use `transmute` instead.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// unsafe {
 +    ///     let _: &T = std::mem::transmute(p); // where p: *const T
 +    /// }
 +    ///
 +    /// // can be written:
 +    /// let _: &T = &*p;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_PTR_TO_REF,
 +    complexity,
 +    "transmutes from a pointer to a reference type"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a `char`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not every integer is a Unicode scalar value.
 +    ///
 +    /// ### Known problems
 +    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
 +    /// as it needs to validate the input.
 +    /// If you are certain that the input is always a valid Unicode scalar value,
 +    /// use [`from_u32_unchecked`] which is as fast as `transmute`
 +    /// but has a semantically meaningful name.
 +    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
 +    ///
 +    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
 +    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1_u32;
 +    /// unsafe {
 +    ///     let _: char = std::mem::transmute(x); // where x: u32
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _ = std::char::from_u32(x).unwrap();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_CHAR,
 +    complexity,
 +    "transmutes from an integer to a `char`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a `&[u8]` to a `&str`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Not every byte slice is a valid UTF-8 string.
 +    ///
 +    /// ### Known problems
 +    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
 +    /// as it needs to validate the input.
 +    /// If you are certain that the input is always a valid UTF-8,
 +    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
 +    /// but has a semantically meaningful name.
 +    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
 +    ///
 +    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
 +    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let b: &[u8] = &[1_u8, 2_u8];
 +    /// unsafe {
 +    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _ = std::str::from_utf8(b).unwrap();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_BYTES_TO_STR,
 +    complexity,
 +    "transmutes from a `&[u8]` to a `&str`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a `bool`.
 +    ///
 +    /// ### Why is this bad?
 +    /// This might result in an invalid in-memory representation of a `bool`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1_u8;
 +    /// unsafe {
 +    ///     let _: bool = std::mem::transmute(x); // where x: u8
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: bool = x != 0;
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_BOOL,
 +    complexity,
 +    "transmutes from an integer to a `bool`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from an integer to a float.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
 +    /// and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: f32 = f32::from_bits(1_u32);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_INT_TO_FLOAT,
 +    complexity,
 +    "transmutes from an integer to a float"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a float to an integer.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
 +    /// and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let _: u32 = std::mem::transmute(1f32);
 +    /// }
 +    ///
 +    /// // should be:
 +    /// let _: u32 = 1f32.to_bits();
 +    /// ```
 +    #[clippy::version = "1.41.0"]
 +    pub TRANSMUTE_FLOAT_TO_INT,
 +    complexity,
 +    "transmutes from a float to an integer"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a number to an array of `u8`
 +    ///
 +    /// ### Why this is bad?
 +    /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
 +    /// is intuitive and safe.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// unsafe {
 +    ///     let x: [u8; 8] = std::mem::transmute(1i64);
 +    /// }
 +    ///
 +    /// // should be
 +    /// let x: [u8; 8] = 0i64.to_ne_bytes();
 +    /// ```
 +    #[clippy::version = "1.58.0"]
 +    pub TRANSMUTE_NUM_TO_BYTES,
 +    complexity,
 +    "transmutes from a number to an array of `u8`"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes from a pointer to a pointer, or
 +    /// from a reference to a reference.
 +    ///
 +    /// ### Why is this bad?
 +    /// Transmutes are dangerous, and these can instead be
 +    /// written as casts.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let ptr = &1u32 as *const u32;
 +    /// unsafe {
 +    ///     // pointer-to-pointer transmute
 +    ///     let _: *const f32 = std::mem::transmute(ptr);
 +    ///     // ref-ref transmute
 +    ///     let _: &f32 = std::mem::transmute(&1u32);
 +    /// }
 +    /// // These can be respectively written:
 +    /// let _ = ptr as *const f32;
 +    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub TRANSMUTE_PTR_TO_PTR,
 +    pedantic,
 +    "transmutes from a pointer to a pointer / a reference to a reference"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between collections whose
 +    /// types have different ABI, size or alignment.
 +    ///
 +    /// ### Why is this bad?
 +    /// This is undefined behavior.
 +    ///
 +    /// ### Known problems
 +    /// Currently, we cannot know whether a type is a
 +    /// collection, so we just lint the ones that come with `std`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// // different size, therefore likely out-of-bounds memory access
 +    /// // You absolutely do not want this in your code!
 +    /// unsafe {
 +    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
 +    /// };
 +    /// ```
 +    ///
 +    /// You must always iterate, map and collect the values:
 +    ///
 +    /// ```rust
 +    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
 +    /// ```
 +    #[clippy::version = "1.40.0"]
 +    pub UNSOUND_COLLECTION_TRANSMUTE,
 +    correctness,
 +    "transmute between collections of layout-incompatible types"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for transmutes between types which do not have a representation defined relative to
 +    /// each other.
 +    ///
 +    /// ### Why is this bad?
 +    /// The results of such a transmute are not defined.
 +    ///
 +    /// ### Known problems
 +    /// This lint has had multiple problems in the past and was moved to `nursery`. See issue
 +    /// [#8496](https://github.com/rust-lang/rust-clippy/issues/8496) for more details.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// struct Foo<T>(u32, T);
 +    /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// #[repr(C)]
 +    /// struct Foo<T>(u32, T);
 +    /// let _ = unsafe { core::mem::transmute::<Foo<u32>, Foo<i32>>(Foo(0u32, 0u32)) };
 +    /// ```
 +    #[clippy::version = "1.60.0"]
 +    pub TRANSMUTE_UNDEFINED_REPR,
 +    nursery,
 +    "transmute to or from a type with an undefined representation"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// Checks for transmute calls which would receive a null pointer.
++    ///
++    /// ### Why is this bad?
++    /// Transmuting a null pointer is undefined behavior.
++    ///
++    /// ### Known problems
++    /// Not all cases can be detected at the moment of this writing.
++    /// For example, variables which hold a null pointer and are then fed to a `transmute`
++    /// call, aren't detectable yet.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let null_ref: &u64 = unsafe { std::mem::transmute(0 as *const u64) };
++    /// ```
++    #[clippy::version = "1.35.0"]
++    pub TRANSMUTING_NULL,
++    correctness,
++    "transmutes from a null pointer to a reference, which is undefined behavior"
++}
++
 +pub struct Transmute {
 +    msrv: Option<RustcVersion>,
 +}
 +impl_lint_pass!(Transmute => [
 +    CROSSPOINTER_TRANSMUTE,
 +    TRANSMUTE_PTR_TO_REF,
 +    TRANSMUTE_PTR_TO_PTR,
 +    USELESS_TRANSMUTE,
 +    WRONG_TRANSMUTE,
 +    TRANSMUTE_INT_TO_CHAR,
 +    TRANSMUTE_BYTES_TO_STR,
 +    TRANSMUTE_INT_TO_BOOL,
 +    TRANSMUTE_INT_TO_FLOAT,
 +    TRANSMUTE_FLOAT_TO_INT,
 +    TRANSMUTE_NUM_TO_BYTES,
 +    UNSOUND_COLLECTION_TRANSMUTE,
 +    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
 +    TRANSMUTE_UNDEFINED_REPR,
++    TRANSMUTING_NULL,
 +]);
 +impl Transmute {
 +    #[must_use]
 +    pub fn new(msrv: Option<RustcVersion>) -> Self {
 +        Self { msrv }
 +    }
 +}
 +impl<'tcx> LateLintPass<'tcx> for Transmute {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(path_expr, [arg]) = e.kind;
 +            if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
 +            if let Some(def_id) = path.res.opt_def_id();
 +            if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
 +            then {
 +                // Avoid suggesting non-const operations in const contexts:
 +                // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
 +                // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
 +                // - char conversions (https://github.com/rust-lang/rust/issues/89259)
 +                let const_context = in_constant(cx, e.hir_id);
 +
 +                let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
 +                // Adjustments for `to_ty` happen after the call to `transmute`, so don't use them.
 +                let to_ty = cx.typeck_results().expr_ty(e);
 +
 +                // If useless_transmute is triggered, the other lints can be skipped.
 +                if useless_transmute::check(cx, e, from_ty, to_ty, arg) {
 +                    return;
 +                }
 +
 +                let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
 +                    | crosspointer_transmute::check(cx, e, from_ty, to_ty)
++                    | transmuting_null::check(cx, e, arg, to_ty)
 +                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
 +                    | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
 +                    | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
 +                    | transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context)
 +                    | (
 +                        unsound_collection_transmute::check(cx, e, from_ty, to_ty)
 +                        || transmute_undefined_repr::check(cx, e, from_ty, to_ty)
 +                    );
 +
 +                if !linted {
 +                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, arg);
 +                }
 +            }
 +        }
 +    }
 +
 +    extract_msrv_attr!(LateContext);
 +}
index 20b348fc14f7b12f71823d410cd9f5ae2913c250,0000000000000000000000000000000000000000..b6d7d9f5b42ec2a7c5138163a738ced1b4cb8271
mode 100644,000000..100644
--- /dev/null
@@@ -1,372 -1,0 +1,338 @@@
- use rustc_span::Span;
 +use super::TRANSMUTE_UNDEFINED_REPR;
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::ty::is_c_void;
 +use rustc_hir::Expr;
 +use rustc_lint::LateContext;
 +use rustc_middle::ty::subst::{Subst, SubstsRef};
 +use rustc_middle::ty::{self, IntTy, Ty, TypeAndMut, UintTy};
- #[allow(clippy::too_many_lines)]
++use rustc_span::DUMMY_SP;
 +
-         match reduce_refs(cx, e.span, from_ty, to_ty) {
-             ReducedTys::FromFatPtr {
-                 unsized_ty,
-                 to_ty: to_sub_ty,
-             } => match reduce_ty(cx, to_sub_ty) {
-                 ReducedTy::TypeErasure => break,
-                 ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
-                 ReducedTy::Ref(to_sub_ty) => {
-                     from_ty = unsized_ty;
-                     to_ty = to_sub_ty;
-                     continue;
-                 },
-                 _ => {
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                         |diag| {
-                             if from_ty_orig.peel_refs() != unsized_ty {
-                                 diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                             }
-                         },
-                     );
-                     return true;
-                 },
++#[expect(clippy::too_many_lines)]
 +pub(super) fn check<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    e: &'tcx Expr<'_>,
 +    from_ty_orig: Ty<'tcx>,
 +    to_ty_orig: Ty<'tcx>,
 +) -> bool {
 +    let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
 +    let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
 +
 +    while from_ty != to_ty {
-             ReducedTys::ToFatPtr {
-                 unsized_ty,
-                 from_ty: from_sub_ty,
-             } => match reduce_ty(cx, from_sub_ty) {
-                 ReducedTy::TypeErasure => break,
-                 ReducedTy::UnorderedFields(ty) if is_size_pair(ty) => break,
-                 ReducedTy::Ref(from_sub_ty) => {
-                     from_ty = from_sub_ty;
-                     to_ty = unsized_ty;
-                     continue;
-                 },
-                 _ => {
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                         |diag| {
-                             if to_ty_orig.peel_refs() != unsized_ty {
-                                 diag.note(&format!("the contained type `&{}` has an undefined layout", unsized_ty));
-                             }
-                         },
-                     );
-                     return true;
-                 },
++        let reduced_tys = reduce_refs(cx, from_ty, to_ty);
++        match (reduce_ty(cx, reduced_tys.from_ty), reduce_ty(cx, reduced_tys.to_ty)) {
++            // Various forms of type erasure.
++            (ReducedTy::TypeErasure { raw_ptr_only: false }, _)
++            | (_, ReducedTy::TypeErasure { raw_ptr_only: false }) => return false,
++            (ReducedTy::TypeErasure { .. }, _) if reduced_tys.from_raw_ptr => return false,
++            (_, ReducedTy::TypeErasure { .. }) if reduced_tys.to_raw_ptr => return false,
++
++            // `Repr(C)` <-> unordered type.
++            // If the first field of the `Repr(C)` type matches then the transmute is ok
++            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::UnorderedFields(to_sub_ty))
++            | (ReducedTy::UnorderedFields(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty))) => {
++                from_ty = from_sub_ty;
++                to_ty = to_sub_ty;
++                continue;
 +            },
-             ReducedTys::ToPtr {
-                 from_ty: from_sub_ty,
-                 to_ty: to_sub_ty,
-             } => match reduce_ty(cx, from_sub_ty) {
-                 ReducedTy::UnorderedFields(from_ty) => {
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                         |diag| {
-                             if from_ty_orig.peel_refs() != from_ty {
-                                 diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                             }
-                         },
-                     );
-                     return true;
-                 },
-                 ReducedTy::Ref(from_sub_ty) => {
-                     from_ty = from_sub_ty;
-                     to_ty = to_sub_ty;
-                     continue;
-                 },
-                 _ => break,
++            (ReducedTy::OrderedFields(_, Some(from_sub_ty)), ReducedTy::Other(to_sub_ty)) if reduced_tys.to_fat_ptr => {
++                from_ty = from_sub_ty;
++                to_ty = to_sub_ty;
++                continue;
 +            },
-             ReducedTys::FromPtr {
-                 from_ty: from_sub_ty,
-                 to_ty: to_sub_ty,
-             } => match reduce_ty(cx, to_sub_ty) {
-                 ReducedTy::UnorderedFields(to_ty) => {
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
-                         |diag| {
-                             if to_ty_orig.peel_refs() != to_ty {
-                                 diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                             }
-                         },
-                     );
-                     return true;
-                 },
-                 ReducedTy::Ref(to_sub_ty) => {
-                     from_ty = from_sub_ty;
-                     to_ty = to_sub_ty;
-                     continue;
-                 },
-                 _ => break,
++            (ReducedTy::Other(from_sub_ty), ReducedTy::OrderedFields(_, Some(to_sub_ty)))
++                if reduced_tys.from_fat_ptr =>
++            {
++                from_ty = from_sub_ty;
++                to_ty = to_sub_ty;
++                continue;
 +            },
-             ReducedTys::Other {
-                 from_ty: from_sub_ty,
-                 to_ty: to_sub_ty,
-             } => match (reduce_ty(cx, from_sub_ty), reduce_ty(cx, to_sub_ty)) {
-                 (ReducedTy::TypeErasure, _) | (_, ReducedTy::TypeErasure) => return false,
-                 (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
-                     let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
++
++            // ptr <-> ptr
++            (ReducedTy::Other(from_sub_ty), ReducedTy::Other(to_sub_ty))
++                if matches!(from_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_))
++                    && matches!(to_sub_ty.kind(), ty::Ref(..) | ty::RawPtr(_)) =>
++            {
++                from_ty = from_sub_ty;
++                to_ty = to_sub_ty;
++                continue;
 +            },
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!(
-                             "transmute from `{}` to `{}`, both of which have an undefined layout",
-                             from_ty_orig, to_ty_orig
-                         ),
-                         |diag| {
-                             if let Some(same_adt_did) = same_adt_did {
-                                 diag.note(&format!(
-                                     "two instances of the same generic type (`{}`) may have different layouts",
-                                     cx.tcx.item_name(same_adt_did)
-                                 ));
-                             } else {
-                                 if from_ty_orig.peel_refs() != from_ty {
-                                     diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
-                                 }
-                                 if to_ty_orig.peel_refs() != to_ty {
-                                     diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
-                                 }
-                             }
-                         },
-                     );
-                     return true;
-                 },
-                 (
-                     ReducedTy::UnorderedFields(from_ty),
-                     ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
-                 ) => {
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
-                         |diag| {
++
++            // fat ptr <-> (*size, *size)
++            (ReducedTy::Other(_), ReducedTy::UnorderedFields(to_ty))
++                if reduced_tys.from_fat_ptr && is_size_pair(to_ty) =>
++            {
++                return false;
++            },
++            (ReducedTy::UnorderedFields(from_ty), ReducedTy::Other(_))
++                if reduced_tys.to_fat_ptr && is_size_pair(from_ty) =>
++            {
++                return false;
++            },
++
++            // fat ptr -> some struct | some struct -> fat ptr
++            (ReducedTy::Other(_), _) if reduced_tys.from_fat_ptr => {
++                span_lint_and_then(
++                    cx,
++                    TRANSMUTE_UNDEFINED_REPR,
++                    e.span,
++                    &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
++                    |diag| {
++                        if from_ty_orig.peel_refs() != from_ty.peel_refs() {
++                            diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
++                        }
++                    },
++                );
++                return true;
++            },
++            (_, ReducedTy::Other(_)) if reduced_tys.to_fat_ptr => {
++                span_lint_and_then(
++                    cx,
++                    TRANSMUTE_UNDEFINED_REPR,
++                    e.span,
++                    &format!("transmute to `{}` which has an undefined layout", to_ty_orig),
++                    |diag| {
++                        if to_ty_orig.peel_refs() != to_ty.peel_refs() {
++                            diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
++                        }
++                    },
++                );
++                return true;
++            },
++
++            (ReducedTy::UnorderedFields(from_ty), ReducedTy::UnorderedFields(to_ty)) if from_ty != to_ty => {
++                let same_adt_did = if let (ty::Adt(from_def, from_subs), ty::Adt(to_def, to_subs))
 +                        = (from_ty.kind(), to_ty.kind())
 +                        && from_def == to_def
 +                    {
 +                        if same_except_params(from_subs, to_subs) {
 +                            return false;
 +                        }
 +                        Some(from_def.did())
 +                    } else {
 +                        None
 +                    };
-                         },
-                     );
-                     return true;
-                 },
-                 (
-                     ReducedTy::Other(_) | ReducedTy::OrderedFields(_) | ReducedTy::Ref(_),
-                     ReducedTy::UnorderedFields(to_ty),
-                 ) => {
-                     span_lint_and_then(
-                         cx,
-                         TRANSMUTE_UNDEFINED_REPR,
-                         e.span,
-                         &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
-                         |diag| {
++                span_lint_and_then(
++                    cx,
++                    TRANSMUTE_UNDEFINED_REPR,
++                    e.span,
++                    &format!(
++                        "transmute from `{}` to `{}`, both of which have an undefined layout",
++                        from_ty_orig, to_ty_orig
++                    ),
++                    |diag| {
++                        if let Some(same_adt_did) = same_adt_did {
++                            diag.note(&format!(
++                                "two instances of the same generic type (`{}`) may have different layouts",
++                                cx.tcx.item_name(same_adt_did)
++                            ));
++                        } else {
 +                            if from_ty_orig.peel_refs() != from_ty {
 +                                diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
 +                            }
-                         },
-                     );
-                     return true;
-                 },
-                 (ReducedTy::Ref(from_sub_ty), ReducedTy::Ref(to_sub_ty)) => {
-                     from_ty = from_sub_ty;
-                     to_ty = to_sub_ty;
-                     continue;
-                 },
-                 (
-                     ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
-                     ReducedTy::OrderedFields(_) | ReducedTy::Ref(_) | ReducedTy::Other(_) | ReducedTy::Param,
-                 )
-                 | (
-                     ReducedTy::UnorderedFields(_) | ReducedTy::Param,
-                     ReducedTy::UnorderedFields(_) | ReducedTy::Param,
-                 ) => break,
 +                            if to_ty_orig.peel_refs() != to_ty {
 +                                diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
 +                            }
- enum ReducedTys<'tcx> {
-     FromFatPtr { unsized_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-     ToFatPtr { unsized_ty: Ty<'tcx>, from_ty: Ty<'tcx> },
-     ToPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-     FromPtr { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
-     Other { from_ty: Ty<'tcx>, to_ty: Ty<'tcx> },
++                        }
++                    },
++                );
++                return true;
++            },
++            (
++                ReducedTy::UnorderedFields(from_ty),
++                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
++            ) => {
++                span_lint_and_then(
++                    cx,
++                    TRANSMUTE_UNDEFINED_REPR,
++                    e.span,
++                    &format!("transmute from `{}` which has an undefined layout", from_ty_orig),
++                    |diag| {
++                        if from_ty_orig.peel_refs() != from_ty {
++                            diag.note(&format!("the contained type `{}` has an undefined layout", from_ty));
++                        }
++                    },
++                );
++                return true;
++            },
++            (
++                ReducedTy::Other(_) | ReducedTy::OrderedFields(..) | ReducedTy::TypeErasure { raw_ptr_only: true },
++                ReducedTy::UnorderedFields(to_ty),
++            ) => {
++                span_lint_and_then(
++                    cx,
++                    TRANSMUTE_UNDEFINED_REPR,
++                    e.span,
++                    &format!("transmute into `{}` which has an undefined layout", to_ty_orig),
++                    |diag| {
++                        if to_ty_orig.peel_refs() != to_ty {
++                            diag.note(&format!("the contained type `{}` has an undefined layout", to_ty));
++                        }
++                    },
++                );
++                return true;
++            },
++            (
++                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
++                ReducedTy::OrderedFields(..) | ReducedTy::Other(_) | ReducedTy::TypeErasure { raw_ptr_only: true },
++            )
++            | (ReducedTy::UnorderedFields(_), ReducedTy::UnorderedFields(_)) => {
++                break;
 +            },
 +        }
 +    }
 +
 +    false
 +}
 +
- fn reduce_refs<'tcx>(
-     cx: &LateContext<'tcx>,
-     span: Span,
-     mut from_ty: Ty<'tcx>,
-     mut to_ty: Ty<'tcx>,
- ) -> ReducedTys<'tcx> {
-     loop {
-         return match (from_ty.kind(), to_ty.kind()) {
++#[expect(clippy::struct_excessive_bools)]
++struct ReducedTys<'tcx> {
++    from_ty: Ty<'tcx>,
++    to_ty: Ty<'tcx>,
++    from_raw_ptr: bool,
++    to_raw_ptr: bool,
++    from_fat_ptr: bool,
++    to_fat_ptr: bool,
 +}
 +
 +/// Remove references so long as both types are references.
-                 if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
++fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: Ty<'tcx>) -> ReducedTys<'tcx> {
++    let mut from_raw_ptr = false;
++    let mut to_raw_ptr = false;
++    let (from_fat_ptr, to_fat_ptr) = loop {
++        break match (from_ty.kind(), to_ty.kind()) {
 +            (
 +                &(ty::Ref(_, from_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: from_sub_ty, .. })),
 +                &(ty::Ref(_, to_sub_ty, _) | ty::RawPtr(TypeAndMut { ty: to_sub_ty, .. })),
 +            ) => {
++                from_raw_ptr = matches!(*from_ty.kind(), ty::RawPtr(_));
 +                from_ty = from_sub_ty;
++                to_raw_ptr = matches!(*to_ty.kind(), ty::RawPtr(_));
 +                to_ty = to_sub_ty;
 +                continue;
 +            },
 +            (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })), _)
-                 ReducedTys::FromFatPtr { unsized_ty, to_ty }
++                if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
 +            {
-                 if !unsized_ty.is_sized(cx.tcx.at(span), cx.param_env) =>
++                (true, false)
 +            },
 +            (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(TypeAndMut { ty: unsized_ty, .. })))
-                 ReducedTys::ToFatPtr { unsized_ty, from_ty }
++                if !unsized_ty.is_sized(cx.tcx.at(DUMMY_SP), cx.param_env) =>
 +            {
-             (&(ty::Ref(_, from_ty, _) | ty::RawPtr(TypeAndMut { ty: from_ty, .. })), _) => {
-                 ReducedTys::FromPtr { from_ty, to_ty }
-             },
-             (_, &(ty::Ref(_, to_ty, _) | ty::RawPtr(TypeAndMut { ty: to_ty, .. }))) => {
-                 ReducedTys::ToPtr { from_ty, to_ty }
-             },
-             _ => ReducedTys::Other { from_ty, to_ty },
++                (false, true)
 +            },
-     TypeErasure,
++            _ => (false, false),
 +        };
++    };
++    ReducedTys {
++        from_ty,
++        to_ty,
++        from_raw_ptr,
++        to_raw_ptr,
++        from_fat_ptr,
++        to_fat_ptr,
 +    }
 +}
 +
 +enum ReducedTy<'tcx> {
 +    /// The type can be used for type erasure.
-     OrderedFields(Ty<'tcx>),
++    TypeErasure { raw_ptr_only: bool },
 +    /// The type is a struct containing either zero non-zero sized fields, or multiple non-zero
 +    /// sized fields with a defined order.
-     /// The type is a reference to the contained type.
-     Ref(Ty<'tcx>),
-     /// The type is a generic parameter.
-     Param,
++    /// The second value is the first non-zero sized type.
++    OrderedFields(Ty<'tcx>, Option<Ty<'tcx>>),
 +    /// The type is a struct containing multiple non-zero sized fields with no defined order.
 +    UnorderedFields(Ty<'tcx>),
-             ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => ReducedTy::TypeErasure,
 +    /// Any other type.
 +    Other(Ty<'tcx>),
 +}
 +
 +/// Reduce structs containing a single non-zero sized field to it's contained type.
 +fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
 +    loop {
 +        ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
 +        return match *ty.kind() {
-             ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure,
++            ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
++                ReducedTy::TypeErasure { raw_ptr_only: false }
++            },
 +            ty::Array(sub_ty, _) | ty::Slice(sub_ty) => {
 +                ty = sub_ty;
 +                continue;
 +            },
-                     return ReducedTy::OrderedFields(ty);
++            ty::Tuple(args) if args.is_empty() => ReducedTy::TypeErasure { raw_ptr_only: false },
 +            ty::Tuple(args) => {
 +                let mut iter = args.iter();
 +                let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
-                     return ReducedTy::TypeErasure;
++                    return ReducedTy::OrderedFields(ty, None);
 +                };
 +                if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
 +                    ty = sized_ty;
 +                    continue;
 +                }
 +                ReducedTy::UnorderedFields(ty)
 +            },
 +            ty::Adt(def, substs) if def.is_struct() => {
 +                let mut iter = def
 +                    .non_enum_variant()
 +                    .fields
 +                    .iter()
 +                    .map(|f| cx.tcx.bound_type_of(f.did).subst(cx.tcx, substs));
 +                let Some(sized_ty) = iter.find(|&ty| !is_zero_sized_ty(cx, ty)) else {
-                     ReducedTy::OrderedFields(ty)
++                    return ReducedTy::TypeErasure { raw_ptr_only: false };
 +                };
 +                if iter.all(|ty| is_zero_sized_ty(cx, ty)) {
 +                    ty = sized_ty;
 +                    continue;
 +                }
 +                if def.repr().inhibit_struct_field_reordering_opt() {
-                 ReducedTy::TypeErasure
++                    ReducedTy::OrderedFields(ty, Some(sized_ty))
 +                } else {
 +                    ReducedTy::UnorderedFields(ty)
 +                }
 +            },
 +            ty::Adt(def, _) if def.is_enum() && (def.variants().is_empty() || is_c_void(cx, ty)) => {
-             ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure,
-             ty::Foreign(_) => ReducedTy::TypeErasure,
-             ty::Ref(_, ty, _) => ReducedTy::Ref(ty),
-             ty::RawPtr(ty) => ReducedTy::Ref(ty.ty),
-             ty::Param(_) => ReducedTy::Param,
++                ReducedTy::TypeErasure { raw_ptr_only: false }
 +            },
 +            // TODO: Check if the conversion to or from at least one of a union's fields is valid.
++            ty::Adt(def, _) if def.is_union() => ReducedTy::TypeErasure { raw_ptr_only: false },
++            ty::Foreign(_) | ty::Param(_) => ReducedTy::TypeErasure { raw_ptr_only: false },
++            ty::Int(_) | ty::Uint(_) => ReducedTy::TypeErasure { raw_ptr_only: true },
 +            _ => ReducedTy::Other(ty),
 +        };
 +    }
 +}
 +
 +fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if_chain! {
 +        if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty);
 +        if let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty));
 +        then {
 +            layout.layout.size().bytes() == 0
 +        } else {
 +            false
 +        }
 +    }
 +}
 +
 +fn is_size_pair(ty: Ty<'_>) -> bool {
 +    if let ty::Tuple(tys) = *ty.kind()
 +        && let [ty1, ty2] = &**tys
 +    {
 +        matches!(ty1.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +            && matches!(ty2.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +    } else {
 +        false
 +    }
 +}
 +
 +fn same_except_params<'tcx>(subs1: SubstsRef<'tcx>, subs2: SubstsRef<'tcx>) -> bool {
 +    // TODO: check const parameters as well. Currently this will consider `Array<5>` the same as
 +    // `Array<6>`
 +    for (ty1, ty2) in subs1.types().zip(subs2.types()).filter(|(ty1, ty2)| ty1 != ty2) {
 +        match (ty1.kind(), ty2.kind()) {
 +            (ty::Param(_), _) | (_, ty::Param(_)) => (),
 +            (ty::Adt(adt1, subs1), ty::Adt(adt2, subs2)) if adt1 == adt2 && same_except_params(subs1, subs2) => (),
 +            _ => return false,
 +        }
 +    }
 +    true
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d8e349af7af8e46ecbcb64897054ddbade8f7cca
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,61 @@@
++use clippy_utils::consts::{constant_context, Constant};
++use clippy_utils::diagnostics::span_lint;
++use clippy_utils::is_path_diagnostic_item;
++use if_chain::if_chain;
++use rustc_ast::LitKind;
++use rustc_hir::{Expr, ExprKind};
++use rustc_lint::LateContext;
++use rustc_middle::ty::Ty;
++use rustc_span::symbol::sym;
++
++use super::TRANSMUTING_NULL;
++
++const LINT_MSG: &str = "transmuting a known null pointer into a reference";
++
++pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'tcx Expr<'_>, to_ty: Ty<'tcx>) -> bool {
++    if !to_ty.is_ref() {
++        return false;
++    }
++
++    // Catching transmute over constants that resolve to `null`.
++    let mut const_eval_context = constant_context(cx, cx.typeck_results());
++    if_chain! {
++        if let ExprKind::Path(ref _qpath) = arg.kind;
++        if let Some(Constant::RawPtr(x)) = const_eval_context.expr(arg);
++        if x == 0;
++        then {
++            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
++            return true;
++        }
++    }
++
++    // Catching:
++    // `std::mem::transmute(0 as *const i32)`
++    if_chain! {
++        if let ExprKind::Cast(inner_expr, _cast_ty) = arg.kind;
++        if let ExprKind::Lit(ref lit) = inner_expr.kind;
++        if let LitKind::Int(0, _) = lit.node;
++        then {
++            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
++            return true;
++        }
++    }
++
++    // Catching:
++    // `std::mem::transmute(std::ptr::null::<i32>())`
++    if_chain! {
++        if let ExprKind::Call(func1, []) = arg.kind;
++        if is_path_diagnostic_item(cx, func1, sym::ptr_null);
++        then {
++            span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
++            return true;
++        }
++    }
++
++    // FIXME:
++    // Also catch transmutations of variables which are known nulls.
++    // To do this, MIR const propagation seems to be the better tool.
++    // Whenever MIR const prop routines are more developed, this will
++    // become available. As of this writing (25/03/19) it is not yet.
++    false
++}
index cc64d17be05520feefc3a13d3563e149a8d344de,0000000000000000000000000000000000000000..8980283e5c82634ca1ded1a0a0be6db8672b3094
mode 100644,000000..100644
--- /dev/null
@@@ -1,142 -1,0 +1,149 @@@
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use clippy_utils::is_lint_allowed;
++use clippy_utils::macros::span_is_local;
 +use clippy_utils::source::snippet;
 +use rustc_ast::ast::LitKind;
 +use rustc_errors::Applicability;
 +use rustc_hir::{Expr, ExprKind, HirId};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +use rustc_span::source_map::Span;
 +use unicode_normalization::UnicodeNormalization;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for invisible Unicode characters in the code.
 +    ///
 +    /// ### Why is this bad?
 +    /// Having an invisible character in the code makes for all
 +    /// sorts of April fools, but otherwise is very much frowned upon.
 +    ///
 +    /// ### Example
 +    /// You don't see it, but there may be a zero-width space or soft hyphen
 +    /// some­where in this text.
 +    #[clippy::version = "1.49.0"]
 +    pub INVISIBLE_CHARACTERS,
 +    correctness,
 +    "using an invisible character in a string literal, which is confusing"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for non-ASCII characters in string and char literals.
 +    ///
 +    /// ### Why is this bad?
 +    /// Yeah, we know, the 90's called and wanted their charset
 +    /// back. Even so, there still are editors and other programs out there that
 +    /// don't work well with Unicode. So if the code is meant to be used
 +    /// internationally, on multiple operating systems, or has other portability
 +    /// requirements, activating this lint could be useful.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = String::from("€");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = String::from("\u{20ac}");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub NON_ASCII_LITERAL,
 +    restriction,
 +    "using any literal non-ASCII chars in a string literal instead of using the `\\u` escape"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for string literals that contain Unicode in a form
 +    /// that is not equal to its
 +    /// [NFC-recomposition](http://www.unicode.org/reports/tr15/#Norm_Forms).
 +    ///
 +    /// ### Why is this bad?
 +    /// If such a string is compared to another, the results
 +    /// may be surprising.
 +    ///
 +    /// ### Example
 +    /// You may not see it, but "à"" and "à"" aren't the same string. The
 +    /// former when escaped is actually `"a\u{300}"` while the latter is `"\u{e0}"`.
 +    #[clippy::version = "pre 1.29.0"]
 +    pub UNICODE_NOT_NFC,
 +    pedantic,
 +    "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)"
 +}
 +
 +declare_lint_pass!(Unicode => [INVISIBLE_CHARACTERS, NON_ASCII_LITERAL, UNICODE_NOT_NFC]);
 +
 +impl LateLintPass<'_> for Unicode {
 +    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
 +        if let ExprKind::Lit(ref lit) = expr.kind {
 +            if let LitKind::Str(_, _) | LitKind::Char(_) = lit.node {
 +                check_str(cx, lit.span, expr.hir_id);
 +            }
 +        }
 +    }
 +}
 +
 +fn escape<T: Iterator<Item = char>>(s: T) -> String {
 +    let mut result = String::new();
 +    for c in s {
 +        if c as u32 > 0x7F {
 +            for d in c.escape_unicode() {
 +                result.push(d);
 +            }
 +        } else {
 +            result.push(c);
 +        }
 +    }
 +    result
 +}
 +
 +fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
++    if !span_is_local(span) {
++        return;
++    }
++
 +    let string = snippet(cx, span, "");
 +    if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
 +        span_lint_and_sugg(
 +            cx,
 +            INVISIBLE_CHARACTERS,
 +            span,
 +            "invisible character detected",
 +            "consider replacing the string with",
 +            string
 +                .replace('\u{200B}', "\\u{200B}")
 +                .replace('\u{ad}', "\\u{AD}")
 +                .replace('\u{2060}', "\\u{2060}"),
 +            Applicability::MachineApplicable,
 +        );
 +    }
++
 +    if string.chars().any(|c| c as u32 > 0x7F) {
 +        span_lint_and_sugg(
 +            cx,
 +            NON_ASCII_LITERAL,
 +            span,
 +            "literal non-ASCII character detected",
 +            "consider replacing the string with",
 +            if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
 +                escape(string.chars())
 +            } else {
 +                escape(string.nfc())
 +            },
 +            Applicability::MachineApplicable,
 +        );
 +    }
++
 +    if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
 +        span_lint_and_sugg(
 +            cx,
 +            UNICODE_NOT_NFC,
 +            span,
 +            "non-NFC Unicode sequence detected",
 +            "consider replacing the string with",
 +            string.nfc().collect::<String>(),
 +            Applicability::MachineApplicable,
 +        );
 +    }
 +}
index 9f4c5555f11b7c20489ef384432a6e2e0ebc958b,0000000000000000000000000000000000000000..9a41603f2f4ce1068d6dccbc2e61de037b7d3653
mode 100644,000000..100644
--- /dev/null
@@@ -1,224 -1,0 +1,224 @@@
-     /// 3. If you are on nightly, `Vec::spare_capacity_mut()` is available:
 +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 1.60.0 or later, `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
 +    ///    ```
 +    #[clippy::version = "1.58.0"]
 +    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<'tcx>(
 +    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
 +                    #[expect(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<'tcx>(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 f4f5a4336a39ec7ad4ae0800207e429f986f1f4d,0000000000000000000000000000000000000000..a5afbb8ff9da49272efd9887b20732ed221c0049
mode 100644,000000..100644
--- /dev/null
@@@ -1,177 -1,0 +1,177 @@@
-                                 "".to_string()
 +use clippy_utils::diagnostics::span_lint_and_then;
 +use clippy_utils::source::snippet;
 +use clippy_utils::{contains_return, is_lang_ctor, return_ty, visitors::find_all_ret_expressions};
 +use if_chain::if_chain;
 +use rustc_errors::Applicability;
 +use rustc_hir::intravisit::FnKind;
 +use rustc_hir::LangItem::{OptionSome, ResultOk};
 +use rustc_hir::{Body, ExprKind, FnDecl, HirId, Impl, ItemKind, Node};
 +use rustc_lint::{LateContext, LateLintPass};
 +use rustc_middle::ty;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::sym;
 +use rustc_span::Span;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for private functions that only return `Ok` or `Some`.
 +    ///
 +    /// ### Why is this bad?
 +    /// It is not meaningful to wrap values when no `None` or `Err` is returned.
 +    ///
 +    /// ### Known problems
 +    /// There can be false positives if the function signature is designed to
 +    /// fit some external requirement.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// fn get_cool_number(a: bool, b: bool) -> Option<i32> {
 +    ///     if a && b {
 +    ///         return Some(50);
 +    ///     }
 +    ///     if a {
 +    ///         Some(0)
 +    ///     } else {
 +    ///         Some(10)
 +    ///     }
 +    /// }
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// fn get_cool_number(a: bool, b: bool) -> i32 {
 +    ///     if a && b {
 +    ///         return 50;
 +    ///     }
 +    ///     if a {
 +    ///         0
 +    ///     } else {
 +    ///         10
 +    ///     }
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub UNNECESSARY_WRAPS,
 +    pedantic,
 +    "functions that only return `Ok` or `Some`"
 +}
 +
 +pub struct UnnecessaryWraps {
 +    avoid_breaking_exported_api: bool,
 +}
 +
 +impl_lint_pass!(UnnecessaryWraps => [UNNECESSARY_WRAPS]);
 +
 +impl UnnecessaryWraps {
 +    pub fn new(avoid_breaking_exported_api: bool) -> Self {
 +        Self {
 +            avoid_breaking_exported_api,
 +        }
 +    }
 +}
 +
 +impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
 +    fn check_fn(
 +        &mut self,
 +        cx: &LateContext<'tcx>,
 +        fn_kind: FnKind<'tcx>,
 +        fn_decl: &FnDecl<'tcx>,
 +        body: &Body<'tcx>,
 +        span: Span,
 +        hir_id: HirId,
 +    ) {
 +        // Abort if public function/method or closure.
 +        match fn_kind {
 +            FnKind::ItemFn(..) | FnKind::Method(..) => {
 +                let def_id = cx.tcx.hir().local_def_id(hir_id);
 +                if self.avoid_breaking_exported_api && cx.access_levels.is_exported(def_id) {
 +                    return;
 +                }
 +            },
 +            FnKind::Closure => return,
 +        }
 +
 +        // Abort if the method is implementing a trait or of it a trait method.
 +        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +            if matches!(
 +                item.kind,
 +                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
 +            ) {
 +                return;
 +            }
 +        }
 +
 +        // Get the wrapper and inner types, if can't, abort.
 +        let (return_type_label, lang_item, inner_type) = if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id).kind() {
 +            if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
 +                ("Option", OptionSome, subst.type_at(0))
 +            } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
 +                ("Result", ResultOk, subst.type_at(0))
 +            } else {
 +                return;
 +            }
 +        } else {
 +            return;
 +        };
 +
 +        // Check if all return expression respect the following condition and collect them.
 +        let mut suggs = Vec::new();
 +        let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
 +            if_chain! {
 +                if !ret_expr.span.from_expansion();
 +                // Check if a function call.
 +                if let ExprKind::Call(func, [arg]) = ret_expr.kind;
 +                // Check if OPTION_SOME or RESULT_OK, depending on return type.
 +                if let ExprKind::Path(qpath) = &func.kind;
 +                if is_lang_ctor(cx, qpath, lang_item);
 +                // Make sure the function argument does not contain a return expression.
 +                if !contains_return(arg);
 +                then {
 +                    suggs.push(
 +                        (
 +                            ret_expr.span,
 +                            if inner_type.is_unit() {
++                                String::new()
 +                            } else {
 +                                snippet(cx, arg.span.source_callsite(), "..").to_string()
 +                            }
 +                        )
 +                    );
 +                    true
 +                } else {
 +                    false
 +                }
 +            }
 +        });
 +
 +        if can_sugg && !suggs.is_empty() {
 +            let (lint_msg, return_type_sugg_msg, return_type_sugg, body_sugg_msg) = if inner_type.is_unit() {
 +                (
 +                    "this function's return value is unnecessary".to_string(),
 +                    "remove the return type...".to_string(),
 +                    snippet(cx, fn_decl.output.span(), "..").to_string(),
 +                    "...and then remove returned values",
 +                )
 +            } else {
 +                (
 +                    format!(
 +                        "this function's return value is unnecessarily wrapped by `{}`",
 +                        return_type_label
 +                    ),
 +                    format!("remove `{}` from the return type...", return_type_label),
 +                    inner_type.to_string(),
 +                    "...and then change returning expressions",
 +                )
 +            };
 +
 +            span_lint_and_then(cx, UNNECESSARY_WRAPS, span, lint_msg.as_str(), |diag| {
 +                diag.span_suggestion(
 +                    fn_decl.output.span(),
 +                    return_type_sugg_msg.as_str(),
 +                    return_type_sugg,
 +                    Applicability::MaybeIncorrect,
 +                );
 +                diag.multipart_suggestion(body_sugg_msg, suggs, Applicability::MaybeIncorrect);
 +            });
 +        }
 +    }
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ac73173697e8596bcc5b013f1b7d57e38fc28ab2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,225 @@@
++use clippy_utils::diagnostics::span_lint_and_help;
++use clippy_utils::ty::{match_type, peel_mid_ty_refs_is_mutable};
++use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, paths, peel_ref_operators};
++use rustc_ast::Mutability;
++use rustc_hir::intravisit::{walk_expr, Visitor};
++use rustc_hir::lang_items::LangItem;
++use rustc_hir::{Block, Expr, ExprKind, HirId, Local, Node, PatKind, PathSegment, 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 the creation of a `peekable` iterator that is never `.peek()`ed
++    ///
++    /// ### Why is this bad?
++    /// Creating a peekable iterator without using any of its methods is likely a mistake,
++    /// or just a leftover after a refactor.
++    ///
++    /// ### Example
++    /// ```rust
++    /// let collection = vec![1, 2, 3];
++    /// let iter = collection.iter().peekable();
++    ///
++    /// for item in iter {
++    ///     // ...
++    /// }
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// let collection = vec![1, 2, 3];
++    /// let iter = collection.iter();
++    ///
++    /// for item in iter {
++    ///     // ...
++    /// }
++    /// ```
++    #[clippy::version = "1.64.0"]
++    pub UNUSED_PEEKABLE,
++    suspicious,
++    "creating a peekable iterator without using any of its methods"
++}
++
++declare_lint_pass!(UnusedPeekable => [UNUSED_PEEKABLE]);
++
++impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
++    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
++        // Don't lint `Peekable`s returned from a block
++        if let Some(expr) = block.expr
++            && let Some(ty) = cx.typeck_results().expr_ty_opt(peel_ref_operators(cx, expr))
++            && match_type(cx, ty, &paths::PEEKABLE)
++        {
++            return;
++        }
++
++        for (idx, stmt) in block.stmts.iter().enumerate() {
++            if !stmt.span.from_expansion()
++                && let StmtKind::Local(local) = stmt.kind
++                && let PatKind::Binding(_, binding, ident, _) = local.pat.kind
++                && let Some(init) = local.init
++                && !init.span.from_expansion()
++                && let Some(ty) = cx.typeck_results().expr_ty_opt(init)
++                && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
++                && match_type(cx, ty, &paths::PEEKABLE)
++            {
++                let mut vis = PeekableVisitor::new(cx, binding);
++
++                if idx + 1 == block.stmts.len() && block.expr.is_none() {
++                    return;
++                }
++
++                for stmt in &block.stmts[idx..] {
++                    vis.visit_stmt(stmt);
++                }
++
++                if let Some(expr) = block.expr {
++                    vis.visit_expr(expr);
++                }
++
++                if !vis.found_peek_call {
++                    span_lint_and_help(
++                        cx,
++                        UNUSED_PEEKABLE,
++                        ident.span,
++                        "`peek` never called on `Peekable` iterator",
++                        None,
++                        "consider removing the call to `peekable`"
++                   );
++                }
++            }
++        }
++    }
++}
++
++struct PeekableVisitor<'a, 'tcx> {
++    cx: &'a LateContext<'tcx>,
++    expected_hir_id: HirId,
++    found_peek_call: bool,
++}
++
++impl<'a, 'tcx> PeekableVisitor<'a, 'tcx> {
++    fn new(cx: &'a LateContext<'tcx>, expected_hir_id: HirId) -> Self {
++        Self {
++            cx,
++            expected_hir_id,
++            found_peek_call: false,
++        }
++    }
++}
++
++impl<'tcx> Visitor<'_> for PeekableVisitor<'_, 'tcx> {
++    fn visit_expr(&mut self, ex: &'_ Expr<'_>) {
++        if self.found_peek_call {
++            return;
++        }
++
++        if path_to_local_id(ex, self.expected_hir_id) {
++            for (_, node) in self.cx.tcx.hir().parent_iter(ex.hir_id) {
++                match node {
++                    Node::Expr(expr) => {
++                        match expr.kind {
++                            // some_function(peekable)
++                            //
++                            // If the Peekable is passed to a function, stop
++                            ExprKind::Call(_, args) => {
++                                if let Some(func_did) = fn_def_id(self.cx, expr)
++                                    && let Ok(into_iter_did) = self
++                                        .cx
++                                        .tcx
++                                        .lang_items()
++                                        .require(LangItem::IntoIterIntoIter)
++                                    && func_did == into_iter_did
++                                {
++                                    // Probably a for loop desugar, stop searching
++                                    return;
++                                }
++
++                                if args.iter().any(|arg| {
++                                    matches!(arg.kind, ExprKind::Path(_)) && arg_is_mut_peekable(self.cx, arg)
++                                }) {
++                                    self.found_peek_call = true;
++                                    return;
++                                }
++                            },
++                            // Catch anything taking a Peekable mutably
++                            ExprKind::MethodCall(
++                                PathSegment {
++                                    ident: method_name_ident,
++                                    ..
++                                },
++                                [self_arg, remaining_args @ ..],
++                                _,
++                            ) => {
++                                let method_name = method_name_ident.name.as_str();
++
++                                // `Peekable` methods
++                                if matches!(method_name, "peek" | "peek_mut" | "next_if" | "next_if_eq")
++                                    && arg_is_mut_peekable(self.cx, self_arg)
++                                {
++                                    self.found_peek_call = true;
++                                    return;
++                                }
++
++                                // foo.some_method() excluding Iterator methods
++                                if remaining_args.iter().any(|arg| arg_is_mut_peekable(self.cx, arg))
++                                    && !is_trait_method(self.cx, expr, sym::Iterator)
++                                {
++                                    self.found_peek_call = true;
++                                    return;
++                                }
++
++                                // foo.by_ref(), keep checking for `peek`
++                                if method_name == "by_ref" {
++                                    continue;
++                                }
++
++                                return;
++                            },
++                            ExprKind::AddrOf(_, Mutability::Mut, _) | ExprKind::Unary(..) | ExprKind::DropTemps(_) => {
++                            },
++                            ExprKind::AddrOf(_, Mutability::Not, _) => return,
++                            _ => {
++                                self.found_peek_call = true;
++                                return;
++                            },
++                        }
++                    },
++                    Node::Local(Local { init: Some(init), .. }) => {
++                        if arg_is_mut_peekable(self.cx, init) {
++                            self.found_peek_call = true;
++                            return;
++                        }
++
++                        break;
++                    },
++                    Node::Stmt(stmt) => match stmt.kind {
++                        StmtKind::Expr(_) | StmtKind::Semi(_) => {},
++                        _ => {
++                            self.found_peek_call = true;
++                            return;
++                        },
++                    },
++                    Node::Block(_) | Node::ExprField(_) => {},
++                    _ => {
++                        break;
++                    },
++                }
++            }
++        }
++
++        walk_expr(self, ex);
++    }
++}
++
++fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
++    if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
++        && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
++        && match_type(cx, ty, &paths::PEEKABLE)
++    {
++        true
++    } else {
++        false
++    }
++}
index e1ec357838dbd88846e97ef43c602d33500ebe85,0000000000000000000000000000000000000000..b8a5d4ea8c9fbe177429781c403313046f752194
mode 100644,000000..100644
--- /dev/null
@@@ -1,68 -1,0 +1,68 @@@
-     #[clippy::version = "1.62.0"]
 +use clippy_utils::diagnostics::span_lint_and_sugg;
 +use rustc_ast::ast::{Expr, ExprKind, LitFloatType, LitKind};
 +use rustc_errors::Applicability;
 +use rustc_lint::{EarlyContext, EarlyLintPass};
 +use rustc_session::{declare_lint_pass, declare_tool_lint};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    ///
 +    /// Detects cases where a whole-number literal float is being rounded, using
 +    /// the `floor`, `ceil`, or `round` methods.
 +    ///
 +    /// ### Why is this bad?
 +    ///
 +    /// This is unnecessary and confusing to the reader. Doing this is probably a mistake.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// let x = 1f32.ceil();
 +    /// ```
 +    /// Use instead:
 +    /// ```rust
 +    /// let x = 1f32;
 +    /// ```
++    #[clippy::version = "1.63.0"]
 +    pub UNUSED_ROUNDING,
 +    nursery,
 +    "Uselessly rounding a whole number floating-point literal"
 +}
 +declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]);
 +
 +fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> {
 +    if let ExprKind::MethodCall(name_ident, receiver, _, _) = &expr.kind
 +        && let method_name = name_ident.ident.name.as_str()
 +        && (method_name == "ceil" || method_name == "round" || method_name == "floor")
 +        && let ExprKind::Lit(spanned) = &receiver.kind
 +        && let LitKind::Float(symbol, ty) = spanned.kind {
 +            let f = symbol.as_str().parse::<f64>().unwrap();
 +            let f_str = symbol.to_string() + if let LitFloatType::Suffixed(ty) = ty {
 +                ty.name_str()
 +            } else {
 +                ""
 +            };
 +            if f.fract() == 0.0 {
 +                Some((method_name, f_str))
 +            } else {
 +                None
 +            }
 +        } else {
 +            None
 +        }
 +}
 +
 +impl EarlyLintPass for UnusedRounding {
 +    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
 +        if let Some((method_name, float)) = is_useless_rounding(expr) {
 +            span_lint_and_sugg(
 +                cx,
 +                UNUSED_ROUNDING,
 +                expr.span,
 +                &format!("used the `{}` method with a whole number float", method_name),
 +                &format!("remove the `{}` method call", method_name),
 +                float,
 +                Applicability::MachineApplicable,
 +            );
 +        }
 +    }
 +}
index 3faae9ac0d2b2af6232568015f6874955174c811,0000000000000000000000000000000000000000..84e65d5fa0b719044d3d43705141e5656aa9f63e
mode 100644,000000..100644
--- /dev/null
@@@ -1,553 -1,0 +1,557 @@@
-     (allowed_scripts: Vec<String> = ["Latin"].iter().map(ToString::to_string).collect()),
 +//! Read configurations files.
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use serde::de::{Deserializer, IgnoredAny, IntoDeserializer, MapAccess, Visitor};
 +use serde::Deserialize;
 +use std::error::Error;
 +use std::path::{Path, PathBuf};
 +use std::str::FromStr;
 +use std::{cmp, env, fmt, fs, io, iter};
 +
 +#[rustfmt::skip]
 +const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
 +    "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
 +    "DirectX",
 +    "ECMAScript",
 +    "GPLv2", "GPLv3",
 +    "GitHub", "GitLab",
 +    "IPv4", "IPv6",
 +    "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript",
 +    "NaN", "NaNs",
 +    "OAuth", "GraphQL",
 +    "OCaml",
 +    "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenDNS",
 +    "WebGL",
 +    "TensorFlow",
 +    "TrueType",
 +    "iOS", "macOS", "FreeBSD",
 +    "TeX", "LaTeX", "BibTeX", "BibLaTeX",
 +    "MinGW",
 +    "CamelCase",
 +];
 +const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
 +
 +/// Holds information used by `MISSING_ENFORCED_IMPORT_RENAMES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +pub struct Rename {
 +    pub path: String,
 +    pub rename: String,
 +}
 +
 +/// A single disallowed method, used by the `DISALLOWED_METHODS` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedMethod {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +impl DisallowedMethod {
 +    pub fn path(&self) -> &str {
 +        let (Self::Simple(path) | Self::WithReason { path, .. }) = self;
 +
 +        path
 +    }
 +}
 +
 +/// A single disallowed type, used by the `DISALLOWED_TYPES` lint.
 +#[derive(Clone, Debug, Deserialize)]
 +#[serde(untagged)]
 +pub enum DisallowedType {
 +    Simple(String),
 +    WithReason { path: String, reason: Option<String> },
 +}
 +
 +/// Conf with parse errors
 +#[derive(Default)]
 +pub struct TryConf {
 +    pub conf: Conf,
 +    pub errors: Vec<Box<dyn Error>>,
 +    pub warnings: Vec<Box<dyn Error>>,
 +}
 +
 +impl TryConf {
 +    fn from_error(error: impl Error + 'static) -> Self {
 +        Self {
 +            conf: Conf::default(),
 +            errors: vec![Box::new(error)],
 +            warnings: vec![],
 +        }
 +    }
 +}
 +
 +#[derive(Debug)]
 +struct ConfError(String);
 +
 +impl fmt::Display for ConfError {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        <String as fmt::Display>::fmt(&self.0, f)
 +    }
 +}
 +
 +impl Error for ConfError {}
 +
 +fn conf_error(s: impl Into<String>) -> Box<dyn Error> {
 +    Box::new(ConfError(s.into()))
 +}
 +
 +macro_rules! define_Conf {
 +    ($(
 +        $(#[doc = $doc:literal])+
 +        $(#[conf_deprecated($dep:literal, $new_conf:ident)])?
 +        ($name:ident: $ty:ty = $default:expr),
 +    )*) => {
 +        /// Clippy lint configuration
 +        pub struct Conf {
 +            $($(#[doc = $doc])+ pub $name: $ty,)*
 +        }
 +
 +        mod defaults {
 +            $(pub fn $name() -> $ty { $default })*
 +        }
 +
 +        impl Default for Conf {
 +            fn default() -> Self {
 +                Self { $($name: defaults::$name(),)* }
 +            }
 +        }
 +
 +        impl<'de> Deserialize<'de> for TryConf {
 +            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
 +                deserializer.deserialize_map(ConfVisitor)
 +            }
 +        }
 +
 +        #[derive(Deserialize)]
 +        #[serde(field_identifier, rename_all = "kebab-case")]
 +        #[allow(non_camel_case_types)]
 +        enum Field { $($name,)* third_party, }
 +
 +        struct ConfVisitor;
 +
 +        impl<'de> Visitor<'de> for ConfVisitor {
 +            type Value = TryConf;
 +
 +            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
 +                formatter.write_str("Conf")
 +            }
 +
 +            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapAccess<'de> {
 +                let mut errors = Vec::new();
 +                let mut warnings = Vec::new();
 +                $(let mut $name = None;)*
 +                // could get `Field` here directly, but get `str` first for diagnostics
 +                while let Some(name) = map.next_key::<&str>()? {
 +                    match Field::deserialize(name.into_deserializer())? {
 +                        $(Field::$name => {
 +                            $(warnings.push(conf_error(format!("deprecated field `{}`. {}", name, $dep)));)?
 +                            match map.next_value() {
 +                                Err(e) => errors.push(conf_error(e.to_string())),
 +                                Ok(value) => match $name {
 +                                    Some(_) => errors.push(conf_error(format!("duplicate field `{}`", name))),
 +                                    None => {
 +                                        $name = Some(value);
 +                                        // $new_conf is the same as one of the defined `$name`s, so
 +                                        // this variable is defined in line 2 of this function.
 +                                        $(match $new_conf {
 +                                            Some(_) => errors.push(conf_error(concat!(
 +                                                "duplicate field `", stringify!($new_conf),
 +                                                "` (provided as `", stringify!($name), "`)"
 +                                            ))),
 +                                            None => $new_conf = $name.clone(),
 +                                        })?
 +                                    },
 +                                }
 +                            }
 +                        })*
 +                        // white-listed; ignore
 +                        Field::third_party => drop(map.next_value::<IgnoredAny>())
 +                    }
 +                }
 +                let conf = Conf { $($name: $name.unwrap_or_else(defaults::$name),)* };
 +                Ok(TryConf { conf, errors, warnings })
 +            }
 +        }
 +
 +        #[cfg(feature = "internal")]
 +        pub mod metadata {
 +            use crate::utils::internal_lints::metadata_collector::ClippyConfiguration;
 +
 +            macro_rules! wrap_option {
 +                () => (None);
 +                ($x:literal) => (Some($x));
 +            }
 +
 +            pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
 +                vec![
 +                    $(
 +                        {
 +                            let deprecation_reason = wrap_option!($($dep)?);
 +
 +                            ClippyConfiguration::new(
 +                                stringify!($name),
 +                                stringify!($ty),
 +                                format!("{:?}", super::defaults::$name()),
 +                                concat!($($doc, '\n',)*),
 +                                deprecation_reason,
 +                            )
 +                        },
 +                    )+
 +                ]
 +            }
 +        }
 +    };
 +}
 +
 +define_Conf! {
 +    /// Lint: Arithmetic.
 +    ///
 +    /// Suppress checking of the passed type names.
 +    (arithmetic_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
 +    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
 +    ///
 +    /// Suppress lints whenever the suggested change would cause breakage for other crates.
 +    (avoid_breaking_exported_api: bool = true),
 +    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
 +    ///
 +    /// The minimum rust version that the project supports
 +    (msrv: Option<String> = None),
 +    /// DEPRECATED LINT: BLACKLISTED_NAME.
 +    ///
 +    /// Use the Disallowed Names lint instead
 +    #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
 +    (blacklisted_names: Vec<String> = Vec::new()),
 +    /// Lint: COGNITIVE_COMPLEXITY.
 +    ///
 +    /// The maximum cognitive complexity a function can have
 +    (cognitive_complexity_threshold: u64 = 25),
 +    /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
 +    ///
 +    /// Use the Cognitive Complexity lint instead.
 +    #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
 +    (cyclomatic_complexity_threshold: u64 = 25),
 +    /// Lint: DISALLOWED_NAMES.
 +    ///
 +    /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
 +    /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
 +    /// default configuration of Clippy. By default any configuration will replace the default value.
 +    (disallowed_names: Vec<String> = super::DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
 +    /// Lint: DOC_MARKDOWN.
 +    ///
 +    /// The list of words this lint should not consider as identifiers needing ticks. The value
 +    /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
 +    /// default configuration of Clippy. By default any configuraction will replace the default value. For example:
 +    /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 +    /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 +    ///
 +    /// Default list:
 +    (doc_valid_idents: Vec<String> = super::DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
 +    /// Lint: TOO_MANY_ARGUMENTS.
 +    ///
 +    /// The maximum number of argument a function or method can have
 +    (too_many_arguments_threshold: u64 = 7),
 +    /// Lint: TYPE_COMPLEXITY.
 +    ///
 +    /// The maximum complexity a type can have
 +    (type_complexity_threshold: u64 = 250),
 +    /// Lint: MANY_SINGLE_CHAR_NAMES.
 +    ///
 +    /// The maximum number of single char bindings a scope may have
 +    (single_char_binding_names_threshold: u64 = 4),
 +    /// Lint: BOXED_LOCAL, USELESS_VEC.
 +    ///
 +    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
 +    (too_large_for_stack: u64 = 200),
 +    /// Lint: ENUM_VARIANT_NAMES.
 +    ///
 +    /// The minimum number of enum variants for the lints about variant names to trigger
 +    (enum_variant_name_threshold: u64 = 3),
 +    /// Lint: LARGE_ENUM_VARIANT.
 +    ///
 +    /// The maximum size of an enum's variant to avoid box suggestion
 +    (enum_variant_size_threshold: u64 = 200),
 +    /// Lint: VERBOSE_BIT_MASK.
 +    ///
 +    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
 +    (verbose_bit_mask_threshold: u64 = 1),
 +    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
 +    ///
 +    /// The lower bound for linting decimal literals
 +    (literal_representation_threshold: u64 = 16384),
 +    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
 +    ///
 +    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
 +    (trivial_copy_size_limit: Option<u64> = None),
 +    /// Lint: LARGE_TYPE_PASS_BY_MOVE.
 +    ///
 +    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
 +    (pass_by_value_size_limit: u64 = 256),
 +    /// Lint: TOO_MANY_LINES.
 +    ///
 +    /// The maximum number of lines a function or method can have
 +    (too_many_lines_threshold: u64 = 100),
 +    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
 +    ///
 +    /// The maximum allowed size for arrays on the stack
 +    (array_size_threshold: u64 = 512_000),
 +    /// Lint: VEC_BOX.
 +    ///
 +    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
 +    (vec_box_size_threshold: u64 = 4096),
 +    /// Lint: TYPE_REPETITION_IN_BOUNDS.
 +    ///
 +    /// The maximum number of bounds a trait can have to be linted
 +    (max_trait_bounds: u64 = 3),
 +    /// Lint: STRUCT_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool fields a struct can have
 +    (max_struct_bools: u64 = 3),
 +    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
 +    ///
 +    /// The maximum number of bool parameters a function can have
 +    (max_fn_params_bools: u64 = 3),
 +    /// Lint: WILDCARD_IMPORTS.
 +    ///
 +    /// Whether to allow certain wildcard imports (prelude, super in tests).
 +    (warn_on_all_wildcard_imports: bool = false),
 +    /// Lint: DISALLOWED_METHODS.
 +    ///
 +    /// The list of disallowed methods, written as fully qualified paths.
 +    (disallowed_methods: Vec<crate::utils::conf::DisallowedMethod> = Vec::new()),
 +    /// Lint: DISALLOWED_TYPES.
 +    ///
 +    /// The list of disallowed types, written as fully qualified paths.
 +    (disallowed_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
 +    /// Lint: UNREADABLE_LITERAL.
 +    ///
 +    /// Should the fraction of a decimal be linted to include separators.
 +    (unreadable_literal_lint_fractions: bool = true),
 +    /// Lint: UPPER_CASE_ACRONYMS.
 +    ///
 +    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
 +    (upper_case_acronyms_aggressive: bool = false),
 +    /// Lint: _CARGO_COMMON_METADATA.
 +    ///
 +    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
 +    (cargo_ignore_publish: bool = false),
 +    /// Lint: NONSTANDARD_MACRO_BRACES.
 +    ///
 +    /// Enforce the named macros always use the braces specified.
 +    ///
 +    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
 +    /// is could be used with a full path two `MacroMatcher`s have to be added one with the full path
 +    /// `crate_name::macro_name` and one with just the macro name.
 +    (standard_macro_braces: Vec<crate::nonstandard_macro_braces::MacroMatcher> = Vec::new()),
 +    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
 +    ///
 +    /// The list of imports to always rename, a fully qualified path followed by the rename.
 +    (enforced_import_renames: Vec<crate::utils::conf::Rename> = Vec::new()),
 +    /// Lint: DISALLOWED_SCRIPT_IDENTS.
 +    ///
 +    /// The list of unicode scripts allowed to be used in the scope.
++    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
 +    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
 +    ///
 +    /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
 +    (enable_raw_pointer_heuristic_for_send: bool = true),
 +    /// Lint: INDEX_REFUTABLE_SLICE.
 +    ///
 +    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
 +    /// the slice pattern that is suggested. If more elements would be necessary, the lint is suppressed.
 +    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
 +    (max_suggested_slice_pattern_length: u64 = 3),
 +    /// Lint: AWAIT_HOLDING_INVALID_TYPE
 +    (await_holding_invalid_types: Vec<crate::utils::conf::DisallowedType> = Vec::new()),
 +    /// Lint: LARGE_INCLUDE_FILE.
 +    ///
 +    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
 +    (max_include_file_size: u64 = 1_000_000),
 +    /// Lint: EXPECT_USED.
 +    ///
 +    /// Whether `expect` should be allowed in test functions
 +    (allow_expect_in_tests: bool = false),
 +    /// Lint: UNWRAP_USED.
 +    ///
 +    /// Whether `unwrap` should be allowed in test functions
 +    (allow_unwrap_in_tests: bool = false),
 +    /// Lint: DBG_MACRO.
 +    ///
 +    /// Whether `dbg!` should be allowed in test functions
 +    (allow_dbg_in_tests: bool = false),
++    /// Lint: RESULT_LARGE_ERR
++    ///
++    /// The maximum size of the `Err`-variant in a `Result` returned from a function
++    (large_error_threshold: u64 = 128),
 +}
 +
 +/// Search for the configuration file.
 +pub fn lookup_conf_file() -> io::Result<Option<PathBuf>> {
 +    /// Possible filename to search for.
 +    const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
 +
 +    // Start looking for a config file in CLIPPY_CONF_DIR, or failing that, CARGO_MANIFEST_DIR.
 +    // If neither of those exist, use ".".
 +    let mut current = env::var_os("CLIPPY_CONF_DIR")
 +        .or_else(|| env::var_os("CARGO_MANIFEST_DIR"))
 +        .map_or_else(|| PathBuf::from("."), PathBuf::from);
 +
 +    let mut found_config: Option<PathBuf> = None;
 +
 +    loop {
 +        for config_file_name in &CONFIG_FILE_NAMES {
 +            if let Ok(config_file) = current.join(config_file_name).canonicalize() {
 +                match fs::metadata(&config_file) {
 +                    Err(e) if e.kind() == io::ErrorKind::NotFound => {},
 +                    Err(e) => return Err(e),
 +                    Ok(md) if md.is_dir() => {},
 +                    Ok(_) => {
 +                        // warn if we happen to find two config files #8323
 +                        if let Some(ref found_config_) = found_config {
 +                            eprintln!(
 +                                "Using config file `{}`\nWarning: `{}` will be ignored.",
 +                                found_config_.display(),
 +                                config_file.display(),
 +                            );
 +                        } else {
 +                            found_config = Some(config_file);
 +                        }
 +                    },
 +                }
 +            }
 +        }
 +
 +        if found_config.is_some() {
 +            return Ok(found_config);
 +        }
 +
 +        // If the current directory has no parent, we're done searching.
 +        if !current.pop() {
 +            return Ok(None);
 +        }
 +    }
 +}
 +
 +/// Read the `toml` configuration file.
 +///
 +/// In case of error, the function tries to continue as much as possible.
 +pub fn read(path: &Path) -> TryConf {
 +    let content = match fs::read_to_string(path) {
 +        Err(e) => return TryConf::from_error(e),
 +        Ok(content) => content,
 +    };
 +    match toml::from_str::<TryConf>(&content) {
 +        Ok(mut conf) => {
 +            extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
 +            extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
 +
 +            conf
 +        },
 +        Err(e) => TryConf::from_error(e),
 +    }
 +}
 +
 +fn extend_vec_if_indicator_present(vec: &mut Vec<String>, default: &[&str]) {
 +    if vec.contains(&"..".to_string()) {
 +        vec.extend(default.iter().map(ToString::to_string));
 +    }
 +}
 +
 +const SEPARATOR_WIDTH: usize = 4;
 +
 +// Check whether the error is "unknown field" and, if so, list the available fields sorted and at
 +// least one per line, more if `CLIPPY_TERMINAL_WIDTH` is set and allows it.
 +pub fn format_error(error: Box<dyn Error>) -> String {
 +    let s = error.to_string();
 +
 +    if_chain! {
 +        if error.downcast::<toml::de::Error>().is_ok();
 +        if let Some((prefix, mut fields, suffix)) = parse_unknown_field_message(&s);
 +        then {
 +            use fmt::Write;
 +
 +            fields.sort_unstable();
 +
 +            let (rows, column_widths) = calculate_dimensions(&fields);
 +
 +            let mut msg = String::from(prefix);
 +            for row in 0..rows {
 +                write!(msg, "\n").unwrap();
 +                for (column, column_width) in column_widths.iter().copied().enumerate() {
 +                    let index = column * rows + row;
 +                    let field = fields.get(index).copied().unwrap_or_default();
 +                    write!(
 +                        msg,
 +                        "{:separator_width$}{:field_width$}",
 +                        " ",
 +                        field,
 +                        separator_width = SEPARATOR_WIDTH,
 +                        field_width = column_width
 +                    )
 +                    .unwrap();
 +                }
 +            }
 +            write!(msg, "\n{}", suffix).unwrap();
 +            msg
 +        } else {
 +            s
 +        }
 +    }
 +}
 +
 +// `parse_unknown_field_message` will become unnecessary if
 +// https://github.com/alexcrichton/toml-rs/pull/364 is merged.
 +fn parse_unknown_field_message(s: &str) -> Option<(&str, Vec<&str>, &str)> {
 +    // An "unknown field" message has the following form:
 +    //   unknown field `UNKNOWN`, expected one of `FIELD0`, `FIELD1`, ..., `FIELDN` at line X column Y
 +    //                                           ^^      ^^^^                     ^^
 +    if_chain! {
 +        if s.starts_with("unknown field");
 +        let slices = s.split("`, `").collect::<Vec<_>>();
 +        let n = slices.len();
 +        if n >= 2;
 +        if let Some((prefix, first_field)) = slices[0].rsplit_once(" `");
 +        if let Some((last_field, suffix)) = slices[n - 1].split_once("` ");
 +        then {
 +            let fields = iter::once(first_field)
 +                .chain(slices[1..n - 1].iter().copied())
 +                .chain(iter::once(last_field))
 +                .collect::<Vec<_>>();
 +            Some((prefix, fields, suffix))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
 +    let columns = env::var("CLIPPY_TERMINAL_WIDTH")
 +        .ok()
 +        .and_then(|s| <usize as FromStr>::from_str(&s).ok())
 +        .map_or(1, |terminal_width| {
 +            let max_field_width = fields.iter().map(|field| field.len()).max().unwrap();
 +            cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width))
 +        });
 +
 +    let rows = (fields.len() + (columns - 1)) / columns;
 +
 +    let column_widths = (0..columns)
 +        .map(|column| {
 +            if column < columns - 1 {
 +                (0..rows)
 +                    .map(|row| {
 +                        let index = column * rows + row;
 +                        let field = fields.get(index).copied().unwrap_or_default();
 +                        field.len()
 +                    })
 +                    .max()
 +                    .unwrap()
 +            } else {
 +                // Avoid adding extra space to the last column.
 +                0
 +            }
 +        })
 +        .collect::<Vec<_>>();
 +
 +    (rows, column_widths)
 +}
index 5dcacd604be45f6b0a4c4844e05ece2e13287198,0000000000000000000000000000000000000000..eb34085a2abf3a5e5631bf182173a33cb14abb9c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1438 -1,0 +1,1438 @@@
-             if let ast::AttrKind::Normal(ref attr_kind, _) = &attr.kind;
-             if let [tool_name, attr_name] = &attr_kind.path.segments[..];
 +use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 +use clippy_utils::consts::{constant_simple, Constant};
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 +use clippy_utils::macros::root_macro_call_first_node;
 +use clippy_utils::source::snippet;
 +use clippy_utils::ty::match_type;
 +use clippy_utils::{
 +    def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
 +    method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
 +};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_ast::ast::{Crate, ItemKind, LitKind, ModKind, NodeId};
 +use rustc_ast::visit::FnKind;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_errors::Applicability;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::hir_id::CRATE_HIR_ID;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{
 +    BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
 +    TyKind, UnOp,
 +};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 +use rustc_middle::hir::nested_filter;
 +use rustc_middle::mir::interpret::ConstValue;
 +use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 +use rustc_span::source_map::Spanned;
 +use rustc_span::symbol::Symbol;
 +use rustc_span::{sym, BytePos, Span};
 +use rustc_typeck::hir_ty_to_ty;
 +
 +use std::borrow::{Borrow, Cow};
 +
 +#[cfg(feature = "internal")]
 +pub mod metadata_collector;
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for various things we like to keep tidy in clippy.
 +    ///
 +    /// ### Why is this bad?
 +    /// We like to pretend we're an example of tidy code.
 +    ///
 +    /// ### Example
 +    /// Wrong ordering of the util::paths constants.
 +    pub CLIPPY_LINTS_INTERNAL,
 +    internal,
 +    "various things that will negatively affect your clippy experience"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Ensures every lint is associated to a `LintPass`.
 +    ///
 +    /// ### Why is this bad?
 +    /// The compiler only knows lints via a `LintPass`. Without
 +    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
 +    /// know the name of the lint.
 +    ///
 +    /// ### Known problems
 +    /// Only checks for lints associated using the
 +    /// `declare_lint_pass!`, `impl_lint_pass!`, and `lint_array!` macros.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_lint! { pub LINT_1, ... }
 +    /// declare_lint! { pub LINT_2, ... }
 +    /// declare_lint! { pub FORGOTTEN_LINT, ... }
 +    /// // ...
 +    /// declare_lint_pass!(Pass => [LINT_1, LINT_2]);
 +    /// // missing FORGOTTEN_LINT
 +    /// ```
 +    pub LINT_WITHOUT_LINT_PASS,
 +    internal,
 +    "declaring a lint without associating it in a LintPass"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
 +    /// variant of the function.
 +    ///
 +    /// ### Why is this bad?
 +    /// The `utils::*` variants also add a link to the Clippy documentation to the
 +    /// warning/error messages.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// cx.span_lint(LINT_NAME, "message");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```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
 +    /// ```rust,ignore
 +    /// expr.span.ctxt().outer().expn_data()
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```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
 +    /// ```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
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "default lint description" }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_lint! { pub COOL_LINT, nursery, "a great new lint" }
 +    /// ```
 +    pub DEFAULT_LINT,
 +    internal,
 +    "found 'default lint description' in a lint declaration"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// 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
 +    /// ```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);
 +    /// });
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```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
 +    /// ```rust,ignore
 +    /// utils::match_type(cx, ty, &paths::VEC)
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```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
 +    /// ```rust,ignore
 +    /// let _ = sym!(f32);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// let _ = sym::f32;
 +    /// ```
 +    pub INTERNING_DEFINED_SYMBOL,
 +    internal,
 +    "interning a symbol that is pre-interned and defined as a constant"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for unnecessary conversion from Symbol to a string.
 +    ///
 +    /// ### Why is this bad?
 +    /// It's faster use symbols directly instead of strings.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// symbol.as_str() == "clippy";
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// symbol == sym::clippy;
 +    /// ```
 +    pub UNNECESSARY_SYMBOL_STR,
 +    internal,
 +    "unnecessary conversion between Symbol and string"
 +}
 +
 +declare_clippy_lint! {
 +    /// Finds unidiomatic usage of `if_chain!`
 +    pub IF_CHAIN_STYLE,
 +    internal,
 +    "non-idiomatic `if_chain!` usage"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for invalid `clippy::version` attributes.
 +    ///
 +    /// Valid values are:
 +    /// * "pre 1.29.0"
 +    /// * any valid semantic version
 +    pub INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found an invalid `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for declared clippy lints without the `clippy::version` attribute.
 +    ///
 +    pub MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +    internal,
 +    "found clippy lint without `clippy::version` attribute"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Check that the `extract_msrv_attr!` macro is used, when a lint has a MSRV.
 +    ///
 +    pub MISSING_MSRV_ATTR_IMPL,
 +    internal,
 +    "checking if all necessary steps were taken when adding a MSRV to a lint"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
 +    /// i.e. `"default deprecation note"`.
 +    ///
 +    /// ### Why is this bad?
 +    /// Indicates that the documentation is incomplete.
 +    ///
 +    /// ### Example
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// TODO
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "default deprecation note"
 +    /// }
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust,ignore
 +    /// declare_deprecated_lint! {
 +    ///     /// ### What it does
 +    ///     /// Nothing. This lint has been deprecated.
 +    ///     ///
 +    ///     /// ### Deprecation reason
 +    ///     /// This lint has been replaced by `cooler_lint`
 +    ///     #[clippy::version = "1.63.0"]
 +    ///     pub COOL_LINT,
 +    ///     "this lint has been replaced by `cooler_lint`"
 +    /// }
 +    /// ```
 +    pub DEFAULT_DEPRECATION_REASON,
 +    internal,
 +    "found 'default deprecation note' in a deprecated lint declaration"
 +}
 +
 +declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 +
 +impl EarlyLintPass for ClippyLintsInternal {
 +    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
 +        if let Some(utils) = krate.items.iter().find(|item| item.ident.name.as_str() == "utils") {
 +            if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = utils.kind {
 +                if let Some(paths) = items.iter().find(|item| item.ident.name.as_str() == "paths") {
 +                    if let ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) = paths.kind {
 +                        let mut last_name: Option<&str> = None;
 +                        for item in items {
 +                            let name = item.ident.as_str();
 +                            if let Some(last_name) = last_name {
 +                                if *last_name > *name {
 +                                    span_lint(
 +                                        cx,
 +                                        CLIPPY_LINTS_INTERNAL,
 +                                        item.span,
 +                                        "this constant should be before the previous constant due to lexical \
 +                                         ordering",
 +                                    );
 +                                }
 +                            }
 +                            last_name = Some(name);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Clone, Debug, Default)]
 +pub struct LintWithoutLintPass {
 +    declared_lints: FxHashMap<Symbol, Span>,
 +    registered_lints: FxHashSet<Symbol>,
 +}
 +
 +impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
 +
 +impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
 +            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
 +        {
 +            return;
 +        }
 +
 +        if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
 +            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
 +            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
 +                check_invalid_clippy_version_attribute(cx, item);
 +
 +                let expr = &cx.tcx.hir().body(body_id).value;
 +                let fields;
 +                if is_lint_ref_ty {
 +                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
 +                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
 +                            fields = struct_fields;
 +                    } else {
 +                        return;
 +                    }
 +                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
 +                    fields = struct_fields;
 +                } else {
 +                    return;
 +                }
 +
 +                let field = fields
 +                    .iter()
 +                    .find(|f| f.ident.as_str() == "desc")
 +                    .expect("lints must have a description field");
 +
 +                if let ExprKind::Lit(Spanned {
 +                    node: LitKind::Str(ref sym, _),
 +                    ..
 +                }) = field.expr.kind
 +                {
 +                    let sym_str = sym.as_str();
 +                    if is_lint_ref_ty {
 +                        if sym_str == "default lint description" {
 +                            span_lint(
 +                                cx,
 +                                DEFAULT_LINT,
 +                                item.span,
 +                                &format!("the lint `{}` has the default lint description", item.ident.name),
 +                            );
 +                        }
 +
 +                        self.declared_lints.insert(item.ident.name, item.span);
 +                    } else if sym_str == "default deprecation note" {
 +                        span_lint(
 +                            cx,
 +                            DEFAULT_DEPRECATION_REASON,
 +                            item.span,
 +                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
 +                        );
 +                    }
 +                }
 +            }
 +        } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
 +            if !matches!(
 +                cx.tcx.item_name(macro_call.def_id).as_str(),
 +                "impl_lint_pass" | "declare_lint_pass"
 +            ) {
 +                return;
 +            }
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: None,
 +                items: impl_item_refs,
 +                ..
 +            }) = item.kind
 +            {
 +                let mut collector = LintCollector {
 +                    output: &mut self.registered_lints,
 +                    cx,
 +                };
 +                let body_id = cx.tcx.hir().body_owned_by(
 +                    cx.tcx.hir().local_def_id(
 +                        impl_item_refs
 +                            .iter()
 +                            .find(|iiref| iiref.ident.as_str() == "get_lints")
 +                            .expect("LintPass needs to implement get_lints")
 +                            .id
 +                            .hir_id(),
 +                    ),
 +                );
 +                collector.visit_expr(&cx.tcx.hir().body(body_id).value);
 +            }
 +        }
 +    }
 +
 +    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
 +        if is_lint_allowed(cx, LINT_WITHOUT_LINT_PASS, CRATE_HIR_ID) {
 +            return;
 +        }
 +
 +        for (lint_name, &lint_span) in &self.declared_lints {
 +            // When using the `declare_tool_lint!` macro, the original `lint_span`'s
 +            // file points to "<rustc macros>".
 +            // `compiletest-rs` thinks that's an error in a different file and
 +            // just ignores it. This causes the test in compile-fail/lint_pass
 +            // not able to capture the error.
 +            // Therefore, we need to climb the macro expansion tree and find the
 +            // actual span that invoked `declare_tool_lint!`:
 +            let lint_span = lint_span.ctxt().outer_expn_data().call_site;
 +
 +            if !self.registered_lints.contains(lint_name) {
 +                span_lint(
 +                    cx,
 +                    LINT_WITHOUT_LINT_PASS,
 +                    lint_span,
 +                    &format!("the lint `{}` is not added to any `LintPass`", lint_name),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn is_lint_ref_type<'tcx>(cx: &LateContext<'tcx>, ty: &Ty<'_>) -> bool {
 +    if let TyKind::Rptr(
 +        _,
 +        MutTy {
 +            ty: inner,
 +            mutbl: Mutability::Not,
 +        },
 +    ) = ty.kind
 +    {
 +        if let TyKind::Path(ref path) = inner.kind {
 +            if let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id) {
 +                return match_def_path(cx, def_id, &paths::LINT);
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
 +    if let Some(value) = extract_clippy_version_value(cx, item) {
 +        // The `sym!` macro doesn't work as it only expects a single token.
 +        // It's better to keep it this way and have a direct `Symbol::intern` call here.
 +        if value == Symbol::intern("pre 1.29.0") {
 +            return;
 +        }
 +
 +        if RustcVersion::parse(value.as_str()).is_err() {
 +            span_lint_and_help(
 +                cx,
 +                INVALID_CLIPPY_VERSION_ATTRIBUTE,
 +                item.span,
 +                "this item has an invalid `clippy::version` attribute",
 +                None,
 +                "please use a valid semantic version, see `doc/adding_lints.md`",
 +            );
 +        }
 +    } else {
 +        span_lint_and_help(
 +            cx,
 +            MISSING_CLIPPY_VERSION_ATTRIBUTE,
 +            item.span,
 +            "this lint is missing the `clippy::version` attribute or version value",
 +            None,
 +            "please use a `clippy::version` attribute, see `doc/adding_lints.md`",
 +        );
 +    }
 +}
 +
 +/// This function extracts the version value of a `clippy::version` attribute if the given value has
 +/// one
 +fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    attrs.iter().find_map(|attr| {
 +        if_chain! {
 +            // Identify attribute
++            if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind;
++            if let [tool_name, attr_name] = &attr_kind.item.path.segments[..];
 +            if tool_name.ident.name == sym::clippy;
 +            if attr_name.ident.name == sym::version;
 +            if let Some(version) = attr.value_str();
 +            then {
 +                Some(version)
 +            } else {
 +                None
 +            }
 +        }
 +    })
 +}
 +
 +struct LintCollector<'a, 'tcx> {
 +    output: &'a mut FxHashSet<Symbol>,
 +    cx: &'a LateContext<'tcx>,
 +}
 +
 +impl<'a, 'tcx> Visitor<'tcx> for LintCollector<'a, 'tcx> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn visit_path(&mut self, path: &'tcx Path<'_>, _: HirId) {
 +        if path.segments.len() == 1 {
 +            self.output.insert(path.segments[0].ident.name);
 +        }
 +    }
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +}
 +
 +#[derive(Clone, Default)]
 +pub struct CompilerLintFunctions {
 +    map: FxHashMap<&'static str, &'static str>,
 +}
 +
 +impl CompilerLintFunctions {
 +    #[must_use]
 +    pub fn new() -> Self {
 +        let mut map = FxHashMap::default();
 +        map.insert("span_lint", "utils::span_lint");
 +        map.insert("struct_span_lint", "utils::span_lint");
 +        map.insert("lint", "utils::span_lint");
 +        map.insert("span_lint_note", "utils::span_lint_and_note");
 +        map.insert("span_lint_help", "utils::span_lint_and_help");
 +        Self { map }
 +    }
 +}
 +
 +impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
 +            let fn_name = path.ident;
 +            if let Some(sugg) = self.map.get(fn_name.as_str());
 +            let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, ty, &paths::EARLY_CONTEXT)
 +                || match_type(cx, ty, &paths::LATE_CONTEXT);
 +            then {
 +                span_lint_and_help(
 +                    cx,
 +                    COMPILER_LINT_FUNCTIONS,
 +                    path.ident.span,
 +                    "usage of a compiler lint function",
 +                    None,
 +                    &format!("please use the Clippy variant of this function: `{}`", sugg),
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(OuterExpnDataPass => [OUTER_EXPN_EXPN_DATA]);
 +
 +impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, OUTER_EXPN_EXPN_DATA, expr.hir_id) {
 +            return;
 +        }
 +
 +        let (method_names, arg_lists, spans) = method_calls(expr, 2);
 +        let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
 +        if_chain! {
 +            if let ["expn_data", "outer_expn"] = method_names.as_slice();
 +            let args = arg_lists[1];
 +            if args.len() == 1;
 +            let self_arg = &args[0];
 +            let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
 +            if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    OUTER_EXPN_EXPN_DATA,
 +                    spans[1].with_hi(expr.span.hi()),
 +                    "usage of `outer_expn().expn_data()`",
 +                    "try",
 +                    "outer_expn_data()".to_string(),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(ProduceIce => [PRODUCE_ICE]);
 +
 +impl EarlyLintPass for ProduceIce {
 +    fn check_fn(&mut self, _: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
 +        assert!(!is_trigger_fn(fn_kind), "Would you like some help with that?");
 +    }
 +}
 +
 +fn is_trigger_fn(fn_kind: FnKind<'_>) -> bool {
 +    match fn_kind {
 +        FnKind::Fn(_, ident, ..) => ident.name.as_str() == "it_looks_like_you_are_trying_to_kill_clippy",
 +        FnKind::Closure(..) => false,
 +    }
 +}
 +
 +declare_lint_pass!(CollapsibleCalls => [COLLAPSIBLE_SPAN_LINT_CALLS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, COLLAPSIBLE_SPAN_LINT_CALLS, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            if let ExprKind::Call(func, and_then_args) = expr.kind;
 +            if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
 +            if and_then_args.len() == 5;
 +            if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
 +            let body = cx.tcx.hir().body(body);
 +            let only_expr = peel_blocks_with_stmt(&body.value);
 +            if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
 +            if let ExprKind::Path(..) = span_call_args[0].kind;
 +            then {
 +                let and_then_snippets = get_and_then_snippets(cx, and_then_args);
 +                let mut sle = SpanlessEq::new(cx).deny_side_effects();
 +                match ps.ident.as_str() {
 +                    "span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
 +                    },
 +                    "span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
 +                    },
 +                    "span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
 +                        let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
 +                    },
 +                    "help" => {
 +                        let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
 +                    }
 +                    "note" => {
 +                        let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
 +                        suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
 +                    }
 +                    _  => (),
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +struct AndThenSnippets<'a> {
 +    cx: Cow<'a, str>,
 +    lint: Cow<'a, str>,
 +    span: Cow<'a, str>,
 +    msg: Cow<'a, str>,
 +}
 +
 +fn get_and_then_snippets<'a, 'hir>(cx: &LateContext<'_>, and_then_snippets: &'hir [Expr<'hir>]) -> AndThenSnippets<'a> {
 +    let cx_snippet = snippet(cx, and_then_snippets[0].span, "cx");
 +    let lint_snippet = snippet(cx, and_then_snippets[1].span, "..");
 +    let span_snippet = snippet(cx, and_then_snippets[2].span, "span");
 +    let msg_snippet = snippet(cx, and_then_snippets[3].span, r#""...""#);
 +
 +    AndThenSnippets {
 +        cx: cx_snippet,
 +        lint: lint_snippet,
 +        span: span_snippet,
 +        msg: msg_snippet,
 +    }
 +}
 +
 +struct SpanSuggestionSnippets<'a> {
 +    help: Cow<'a, str>,
 +    sugg: Cow<'a, str>,
 +    applicability: Cow<'a, str>,
 +}
 +
 +fn span_suggestion_snippets<'a, 'hir>(
 +    cx: &LateContext<'_>,
 +    span_call_args: &'hir [Expr<'hir>],
 +) -> SpanSuggestionSnippets<'a> {
 +    let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
 +    let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
 +    let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
 +
 +    SpanSuggestionSnippets {
 +        help: help_snippet,
 +        sugg: sugg_snippet,
 +        applicability: applicability_snippet,
 +    }
 +}
 +
 +fn suggest_suggestion(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    span_suggestion_snippets: &SpanSuggestionSnippets<'_>,
 +) {
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_sugg({}, {}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            span_suggestion_snippets.help,
 +            span_suggestion_snippets.sugg,
 +            span_suggestion_snippets.applicability
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_help(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    help: &str,
 +    with_span: bool,
 +) {
 +    let option_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_help({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            &option_span,
 +            help
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +fn suggest_note(
 +    cx: &LateContext<'_>,
 +    expr: &Expr<'_>,
 +    and_then_snippets: &AndThenSnippets<'_>,
 +    note: &str,
 +    with_span: bool,
 +) {
 +    let note_span = if with_span {
 +        format!("Some({})", and_then_snippets.span)
 +    } else {
 +        "None".to_string()
 +    };
 +
 +    span_lint_and_sugg(
 +        cx,
 +        COLLAPSIBLE_SPAN_LINT_CALLS,
 +        expr.span,
 +        "this call is collapsible",
 +        "collapse into",
 +        format!(
 +            "span_lint_and_note({}, {}, {}, {}, {}, {})",
 +            and_then_snippets.cx,
 +            and_then_snippets.lint,
 +            and_then_snippets.span,
 +            and_then_snippets.msg,
 +            note_span,
 +            note
 +        ),
 +        Applicability::MachineApplicable,
 +    );
 +}
 +
 +declare_lint_pass!(MatchTypeOnDiagItem => [MATCH_TYPE_ON_DIAGNOSTIC_ITEM]);
 +
 +impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        if is_lint_allowed(cx, MATCH_TYPE_ON_DIAGNOSTIC_ITEM, expr.hir_id) {
 +            return;
 +        }
 +
 +        if_chain! {
 +            // Check if this is a call to utils::match_type()
 +            if let ExprKind::Call(fn_path, [context, ty, ty_path]) = expr.kind;
 +            if is_expr_path_def_path(cx, fn_path, &["clippy_utils", "ty", "match_type"]);
 +            // Extract the path to the matched type
 +            if let Some(segments) = path_to_matched_type(cx, ty_path);
 +            let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
 +            if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
 +            // Check if the matched type is a diagnostic item
 +            if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
 +            then {
 +                // TODO: check paths constants from external crates.
 +                let cx_snippet = snippet(cx, context.span, "_");
 +                let ty_snippet = snippet(cx, ty.span, "_");
 +
 +                span_lint_and_sugg(
 +                    cx,
 +                    MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
 +                    expr.span,
 +                    "usage of `clippy_utils::ty::match_type()` on a type diagnostic item",
 +                    "try",
 +                    format!("clippy_utils::ty::is_type_diagnostic_item({}, {}, sym::{})", cx_snippet, ty_snippet, item_name),
 +                    Applicability::MaybeIncorrect,
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Vec<Symbol>> {
 +    use rustc_hir::ItemKind;
 +
 +    match &expr.kind {
 +        ExprKind::AddrOf(.., expr) => return path_to_matched_type(cx, expr),
 +        ExprKind::Path(qpath) => match cx.qpath_res(qpath, expr.hir_id) {
 +            Res::Local(hir_id) => {
 +                let parent_id = cx.tcx.hir().get_parent_node(hir_id);
 +                if let Some(Node::Local(local)) = cx.tcx.hir().find(parent_id) {
 +                    if let Some(init) = local.init {
 +                        return path_to_matched_type(cx, init);
 +                    }
 +                }
 +            },
 +            Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
 +                if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
 +                    if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
 +                        let body = cx.tcx.hir().body(body_id);
 +                        return path_to_matched_type(cx, &body.value);
 +                    }
 +                }
 +            },
 +            _ => {},
 +        },
 +        ExprKind::Array(exprs) => {
 +            let segments: Vec<Symbol> = exprs
 +                .iter()
 +                .filter_map(|expr| {
 +                    if let ExprKind::Lit(lit) = &expr.kind {
 +                        if let LitKind::Str(sym, _) = lit.node {
 +                            return Some(sym);
 +                        }
 +                    }
 +
 +                    None
 +                })
 +                .collect();
 +
 +            if segments.len() == exprs.len() {
 +                return Some(segments);
 +            }
 +        },
 +        _ => {},
 +    }
 +
 +    None
 +}
 +
 +// This is not a complete resolver for paths. It works on all the paths currently used in the paths
 +// module.  That's all it does and all it needs to do.
 +pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
 +    if def_path_res(cx, path) != Res::Err {
 +        return true;
 +    }
 +
 +    // Some implementations can't be found by `path_to_res`, particularly inherent
 +    // implementations of native types. Check lang items.
 +    let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
 +    let lang_items = cx.tcx.lang_items();
 +    // This list isn't complete, but good enough for our current list of paths.
 +    let incoherent_impls = [
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
 +        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
 +        SimplifiedTypeGen::SliceSimplifiedType,
 +        SimplifiedTypeGen::StrSimplifiedType,
 +    ]
 +    .iter()
 +    .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
 +    for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
 +        let lang_item_path = cx.get_def_path(*item_def_id);
 +        if path_syms.starts_with(&lang_item_path) {
 +            if let [item] = &path_syms[lang_item_path.len()..] {
 +                if matches!(
 +                    cx.tcx.def_kind(*item_def_id),
 +                    DefKind::Mod | DefKind::Enum | DefKind::Trait
 +                ) {
 +                    for child in cx.tcx.module_children(*item_def_id) {
 +                        if child.ident.name == *item {
 +                            return true;
 +                        }
 +                    }
 +                } else {
 +                    for child in cx.tcx.associated_item_def_ids(*item_def_id) {
 +                        if cx.tcx.item_name(*child) == *item {
 +                            return true;
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    false
 +}
 +
 +declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
 +    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
 +        let local_def_id = &cx.tcx.parent_module(item.hir_id());
 +        let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
 +        if_chain! {
 +            if mod_name.as_str() == "paths";
 +            if let hir::ItemKind::Const(ty, body_id) = item.kind;
 +            let ty = hir_ty_to_ty(cx.tcx, ty);
 +            if let ty::Array(el_ty, _) = &ty.kind();
 +            if let ty::Ref(_, el_ty, _) = &el_ty.kind();
 +            if el_ty.is_str();
 +            let body = cx.tcx.hir().body(body_id);
 +            let typeck_results = cx.tcx.typeck_body(body_id);
 +            if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
 +            let path: Vec<&str> = path.iter().map(|x| {
 +                    if let Constant::Str(s) = x {
 +                        s.as_str()
 +                    } else {
 +                        // We checked the type of the constant above
 +                        unreachable!()
 +                    }
 +                }).collect();
 +            if !check_path(cx, &path[..]);
 +            then {
 +                span_lint(cx, INVALID_PATHS, item.span, "invalid path");
 +            }
 +        }
 +    }
 +}
 +
 +#[derive(Default)]
 +pub struct InterningDefinedSymbol {
 +    // Maps the symbol value to the constant DefId.
 +    symbol_map: FxHashMap<u32, DefId>,
 +}
 +
 +impl_lint_pass!(InterningDefinedSymbol => [INTERNING_DEFINED_SYMBOL, UNNECESSARY_SYMBOL_STR]);
 +
 +impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
 +    fn check_crate(&mut self, cx: &LateContext<'_>) {
 +        if !self.symbol_map.is_empty() {
 +            return;
 +        }
 +
 +        for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
 +            if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
 +                for item in cx.tcx.module_children(def_id).iter() {
 +                    if_chain! {
 +                        if let Res::Def(DefKind::Const, item_def_id) = item.res;
 +                        let ty = cx.tcx.type_of(item_def_id);
 +                        if match_type(cx, ty, &paths::SYMBOL);
 +                        if let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id);
 +                        if let Ok(value) = value.to_u32();
 +                        then {
 +                            self.symbol_map.insert(value, item_def_id);
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 +        if_chain! {
 +            if let ExprKind::Call(func, [arg]) = &expr.kind;
 +            if let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind();
 +            if match_def_path(cx, *def_id, &paths::SYMBOL_INTERN);
 +            if let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg);
 +            let value = Symbol::intern(&arg).as_u32();
 +            if let Some(&def_id) = self.symbol_map.get(&value);
 +            then {
 +                span_lint_and_sugg(
 +                    cx,
 +                    INTERNING_DEFINED_SYMBOL,
 +                    is_expn_of(expr.span, "sym").unwrap_or(expr.span),
 +                    "interning a defined symbol",
 +                    "try",
 +                    cx.tcx.def_path_str(def_id),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +        if let ExprKind::Binary(op, left, right) = expr.kind {
 +            if matches!(op.node, BinOpKind::Eq | BinOpKind::Ne) {
 +                let data = [
 +                    (left, self.symbol_str_expr(left, cx)),
 +                    (right, self.symbol_str_expr(right, cx)),
 +                ];
 +                match data {
 +                    // both operands are a symbol string
 +                    [(_, Some(left)), (_, Some(right))] => {
 +                        span_lint_and_sugg(
 +                            cx,
 +                            UNNECESSARY_SYMBOL_STR,
 +                            expr.span,
 +                            "unnecessary `Symbol` to string conversion",
 +                            "try",
 +                            format!(
 +                                "{} {} {}",
 +                                left.as_symbol_snippet(cx),
 +                                op.node.as_str(),
 +                                right.as_symbol_snippet(cx),
 +                            ),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                    // one of the operands is a symbol string
 +                    [(expr, Some(symbol)), _] | [_, (expr, Some(symbol))] => {
 +                        // creating an owned string for comparison
 +                        if matches!(symbol, SymbolStrExpr::Expr { is_to_owned: true, .. }) {
 +                            span_lint_and_sugg(
 +                                cx,
 +                                UNNECESSARY_SYMBOL_STR,
 +                                expr.span,
 +                                "unnecessary string allocation",
 +                                "try",
 +                                format!("{}.as_str()", symbol.as_symbol_snippet(cx)),
 +                                Applicability::MachineApplicable,
 +                            );
 +                        }
 +                    },
 +                    // nothing found
 +                    [(_, None), (_, None)] => {},
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +impl InterningDefinedSymbol {
 +    fn symbol_str_expr<'tcx>(&self, expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> Option<SymbolStrExpr<'tcx>> {
 +        static IDENT_STR_PATHS: &[&[&str]] = &[&paths::IDENT_AS_STR, &paths::TO_STRING_METHOD];
 +        static SYMBOL_STR_PATHS: &[&[&str]] = &[
 +            &paths::SYMBOL_AS_STR,
 +            &paths::SYMBOL_TO_IDENT_STRING,
 +            &paths::TO_STRING_METHOD,
 +        ];
 +        let call = if_chain! {
 +            if let ExprKind::AddrOf(_, _, e) = expr.kind;
 +            if let ExprKind::Unary(UnOp::Deref, e) = e.kind;
 +            then { e } else { expr }
 +        };
 +        if_chain! {
 +            // is a method call
 +            if let ExprKind::MethodCall(_, [item], _) = call.kind;
 +            if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
 +            let ty = cx.typeck_results().expr_ty(item);
 +            // ...on either an Ident or a Symbol
 +            if let Some(is_ident) = if match_type(cx, ty, &paths::SYMBOL) {
 +                Some(false)
 +            } else if match_type(cx, ty, &paths::IDENT) {
 +                Some(true)
 +            } else {
 +                None
 +            };
 +            // ...which converts it to a string
 +            let paths = if is_ident { IDENT_STR_PATHS } else { SYMBOL_STR_PATHS };
 +            if let Some(path) = paths.iter().find(|path| match_def_path(cx, did, path));
 +            then {
 +                let is_to_owned = path.last().unwrap().ends_with("string");
 +                return Some(SymbolStrExpr::Expr {
 +                    item,
 +                    is_ident,
 +                    is_to_owned,
 +                });
 +            }
 +        }
 +        // is a string constant
 +        if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
 +            let value = Symbol::intern(&s).as_u32();
 +            // ...which matches a symbol constant
 +            if let Some(&def_id) = self.symbol_map.get(&value) {
 +                return Some(SymbolStrExpr::Const(def_id));
 +            }
 +        }
 +        None
 +    }
 +}
 +
 +enum SymbolStrExpr<'tcx> {
 +    /// a string constant with a corresponding symbol constant
 +    Const(DefId),
 +    /// a "symbol to string" expression like `symbol.as_str()`
 +    Expr {
 +        /// part that evaluates to `Symbol` or `Ident`
 +        item: &'tcx Expr<'tcx>,
 +        is_ident: bool,
 +        /// whether an owned `String` is created like `to_ident_string()`
 +        is_to_owned: bool,
 +    },
 +}
 +
 +impl<'tcx> SymbolStrExpr<'tcx> {
 +    /// Returns a snippet that evaluates to a `Symbol` and is const if possible
 +    fn as_symbol_snippet(&self, cx: &LateContext<'_>) -> Cow<'tcx, str> {
 +        match *self {
 +            Self::Const(def_id) => cx.tcx.def_path_str(def_id).into(),
 +            Self::Expr { item, is_ident, .. } => {
 +                let mut snip = snippet(cx, item.span.source_callsite(), "..");
 +                if is_ident {
 +                    // get `Ident.name`
 +                    snip.to_mut().push_str(".name");
 +                }
 +                snip
 +            },
 +        }
 +    }
 +}
 +
 +declare_lint_pass!(IfChainStyle => [IF_CHAIN_STYLE]);
 +
 +impl<'tcx> LateLintPass<'tcx> for IfChainStyle {
 +    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
 +        let (local, after, if_chain_span) = if_chain! {
 +            if let [Stmt { kind: StmtKind::Local(local), .. }, after @ ..] = block.stmts;
 +            if let Some(if_chain_span) = is_expn_of(block.span, "if_chain");
 +            then { (local, after, if_chain_span) } else { return }
 +        };
 +        if is_first_if_chain_expr(cx, block.hir_id, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be above the `if_chain!`",
 +            );
 +        } else if local.span.ctxt() == block.span.ctxt() && is_if_chain_then(after, block.expr, if_chain_span) {
 +            span_lint(
 +                cx,
 +                IF_CHAIN_STYLE,
 +                if_chain_local_span(cx, local, if_chain_span),
 +                "`let` expression should be inside `then { .. }`",
 +            );
 +        }
 +    }
 +
 +    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 +        let (cond, then, els) = if let Some(higher::IfOrIfLet { cond, r#else, then }) = higher::IfOrIfLet::hir(expr) {
 +            (cond, then, r#else.is_some())
 +        } else {
 +            return;
 +        };
 +        let then_block = match then.kind {
 +            ExprKind::Block(block, _) => block,
 +            _ => return,
 +        };
 +        let if_chain_span = is_expn_of(expr.span, "if_chain");
 +        if !els {
 +            check_nested_if_chains(cx, expr, then_block, if_chain_span);
 +        }
 +        let if_chain_span = match if_chain_span {
 +            None => return,
 +            Some(span) => span,
 +        };
 +        // check for `if a && b;`
 +        if_chain! {
 +            if let ExprKind::Binary(op, _, _) = cond.kind;
 +            if op.node == BinOpKind::And;
 +            if cx.sess().source_map().is_multiline(cond.span);
 +            then {
 +                span_lint(cx, IF_CHAIN_STYLE, cond.span, "`if a && b;` should be `if a; if b;`");
 +            }
 +        }
 +        if is_first_if_chain_expr(cx, expr.hir_id, if_chain_span)
 +            && is_if_chain_then(then_block.stmts, then_block.expr, if_chain_span)
 +        {
 +            span_lint(cx, IF_CHAIN_STYLE, expr.span, "`if_chain!` only has one `if`");
 +        }
 +    }
 +}
 +
 +fn check_nested_if_chains(
 +    cx: &LateContext<'_>,
 +    if_expr: &Expr<'_>,
 +    then_block: &Block<'_>,
 +    if_chain_span: Option<Span>,
 +) {
 +    #[rustfmt::skip]
 +    let (head, tail) = match *then_block {
 +        Block { stmts, expr: Some(tail), .. } => (stmts, tail),
 +        Block {
 +            stmts: &[
 +                ref head @ ..,
 +                Stmt { kind: StmtKind::Expr(tail) | StmtKind::Semi(tail), .. }
 +            ],
 +            ..
 +        } => (head, tail),
 +        _ => return,
 +    };
 +    if_chain! {
 +        if let Some(higher::IfOrIfLet { r#else: None, .. }) = higher::IfOrIfLet::hir(tail);
 +        let sm = cx.sess().source_map();
 +        if head
 +            .iter()
 +            .all(|stmt| matches!(stmt.kind, StmtKind::Local(..)) && !sm.is_multiline(stmt.span));
 +        if if_chain_span.is_some() || !is_else_clause(cx.tcx, if_expr);
 +        then {} else { return }
 +    }
 +    let (span, msg) = match (if_chain_span, is_expn_of(tail.span, "if_chain")) {
 +        (None, Some(_)) => (if_expr.span, "this `if` can be part of the inner `if_chain!`"),
 +        (Some(_), None) => (tail.span, "this `if` can be part of the outer `if_chain!`"),
 +        (Some(a), Some(b)) if a != b => (b, "this `if_chain!` can be merged with the outer `if_chain!`"),
 +        _ => return,
 +    };
 +    span_lint_and_then(cx, IF_CHAIN_STYLE, span, msg, |diag| {
 +        let (span, msg) = match head {
 +            [] => return,
 +            [stmt] => (stmt.span, "this `let` statement can also be in the `if_chain!`"),
 +            [a, .., b] => (
 +                a.span.to(b.span),
 +                "these `let` statements can also be in the `if_chain!`",
 +            ),
 +        };
 +        diag.span_help(span, msg);
 +    });
 +}
 +
 +fn is_first_if_chain_expr(cx: &LateContext<'_>, hir_id: HirId, if_chain_span: Span) -> bool {
 +    cx.tcx
 +        .hir()
 +        .parent_iter(hir_id)
 +        .find(|(_, node)| {
 +            #[rustfmt::skip]
 +            !matches!(node, Node::Expr(Expr { kind: ExprKind::Block(..), .. }) | Node::Stmt(_))
 +        })
 +        .map_or(false, |(id, _)| {
 +            is_expn_of(cx.tcx.hir().span(id), "if_chain") != Some(if_chain_span)
 +        })
 +}
 +
 +/// Checks a trailing slice of statements and expression of a `Block` to see if they are part
 +/// of the `then {..}` portion of an `if_chain!`
 +fn is_if_chain_then(stmts: &[Stmt<'_>], expr: Option<&Expr<'_>>, if_chain_span: Span) -> bool {
 +    let span = if let [stmt, ..] = stmts {
 +        stmt.span
 +    } else if let Some(expr) = expr {
 +        expr.span
 +    } else {
 +        // empty `then {}`
 +        return true;
 +    };
 +    is_expn_of(span, "if_chain").map_or(true, |span| span != if_chain_span)
 +}
 +
 +/// Creates a `Span` for `let x = ..;` in an `if_chain!` call.
 +fn if_chain_local_span(cx: &LateContext<'_>, local: &Local<'_>, if_chain_span: Span) -> Span {
 +    let mut span = local.pat.span;
 +    if let Some(init) = local.init {
 +        span = span.to(init.span);
 +    }
 +    span.adjust(if_chain_span.ctxt().outer_expn());
 +    let sm = cx.sess().source_map();
 +    let span = sm.span_extend_to_prev_str(span, "let", false, true).unwrap_or(span);
 +    let span = sm.span_extend_to_next_char(span, ';', false);
 +    Span::new(
 +        span.lo() - BytePos(3),
 +        span.hi() + BytePos(1),
 +        span.ctxt(),
 +        span.parent(),
 +    )
 +}
 +
 +declare_lint_pass!(MsrvAttrImpl => [MISSING_MSRV_ATTR_IMPL]);
 +
 +impl LateLintPass<'_> for MsrvAttrImpl {
 +    fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
 +        if_chain! {
 +            if let hir::ItemKind::Impl(hir::Impl {
 +                of_trait: Some(lint_pass_trait_ref),
 +                self_ty,
 +                items,
 +                ..
 +            }) = &item.kind;
 +            if let Some(lint_pass_trait_def_id) = lint_pass_trait_ref.trait_def_id();
 +            let is_late_pass = match_def_path(cx, lint_pass_trait_def_id, &paths::LATE_LINT_PASS);
 +            if is_late_pass || match_def_path(cx, lint_pass_trait_def_id, &paths::EARLY_LINT_PASS);
 +            let self_ty = hir_ty_to_ty(cx.tcx, self_ty);
 +            if let ty::Adt(self_ty_def, _) = self_ty.kind();
 +            if self_ty_def.is_struct();
 +            if self_ty_def.all_fields().any(|f| {
 +                cx.tcx
 +                    .type_of(f.did)
 +                    .walk()
 +                    .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
 +                    .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION))
 +            });
 +            if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs));
 +            then {
 +                let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
 +                let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
 +                let span = cx.sess().source_map().span_through_char(item.span, '{');
 +                span_lint_and_sugg(
 +                    cx,
 +                    MISSING_MSRV_ATTR_IMPL,
 +                    span,
 +                    &format!("`extract_msrv_attr!` macro missing from `{lint_pass}` implementation"),
 +                    &format!("add `extract_msrv_attr!({context})` to the `{lint_pass}` implementation"),
 +                    format!("{}\n    extract_msrv_attr!({context});", snippet(cx, span, "..")),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +}
index 92cf42c7ad43f0459fe10051fd2ea4baac6109eb,0000000000000000000000000000000000000000..b1148bccc2a283d025cb530f18197437bfbe9079
mode 100644,000000..100644
--- /dev/null
@@@ -1,1169 -1,0 +1,1169 @@@
-         &[Ident::with_dummy_span(sym::clippy)].into_iter().collect(),
 +//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
 +//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
 +//!
 +//! This module and therefore the entire lint is guarded by a feature flag called `internal`
 +//!
 +//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
 +//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 +//! a simple mistake)
 +
 +use crate::renamed_lints::RENAMED_LINTS;
 +use crate::utils::internal_lints::{extract_clippy_version_value, is_lint_ref_type};
 +
 +use clippy_utils::diagnostics::span_lint;
 +use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
 +use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
 +use if_chain::if_chain;
 +use rustc_ast as ast;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_hir::{
 +    self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
 +};
 +use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 +use rustc_middle::hir::nested_filter;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Loc, Span, Symbol};
 +use serde::{ser::SerializeStruct, Serialize, Serializer};
 +use std::collections::BinaryHeap;
 +use std::fmt;
 +use std::fmt::Write as _;
 +use std::fs::{self, OpenOptions};
 +use std::io::prelude::*;
 +use std::path::Path;
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +/// This is the output file of the lint collector.
 +const OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
 +/// These lints are excluded from the export.
 +const BLACK_LISTED_LINTS: &[&str] = &["lint_author", "dump_hir", "internal_metadata_collector"];
 +/// These groups will be ignored by the lint group matcher. This is useful for collections like
 +/// `clippy::all`
 +const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
 +/// Lints within this group will be excluded from the collection. These groups
 +/// have to be defined without the `clippy::` prefix.
 +const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
 +/// Collected deprecated lint will be assigned to this group in the JSON output
 +const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
 +/// This is the lint level for deprecated lints that will be displayed in the lint list
 +const DEPRECATED_LINT_LEVEL: &str = "none";
 +/// This array holds Clippy's lint groups with their corresponding default lint level. The
 +/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
 +const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
 +    ("correctness", "deny"),
 +    ("suspicious", "warn"),
 +    ("restriction", "allow"),
 +    ("style", "warn"),
 +    ("pedantic", "allow"),
 +    ("complexity", "warn"),
 +    ("perf", "warn"),
 +    ("cargo", "allow"),
 +    ("nursery", "allow"),
 +];
 +/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
 +/// to only keep the actual lint group in the output.
 +const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
 +
 +/// This template will be used to format the configuration section in the lint documentation.
 +/// The `configurations` parameter will be replaced with one or multiple formatted
 +/// `ClippyConfiguration` instances. See `CONFIGURATION_VALUE_TEMPLATE` for further customizations
 +macro_rules! CONFIGURATION_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Configuration
 +This lint has the following configuration variables:
 +
 +{configurations}
 +"#
 +    };
 +}
 +/// This template will be used to format an individual `ClippyConfiguration` instance in the
 +/// lint documentation.
 +///
 +/// The format function will provide strings for the following parameters: `name`, `ty`, `doc` and
 +/// `default`
 +macro_rules! CONFIGURATION_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`: `{ty}`: {doc} (defaults to `{default}`)\n"
 +    };
 +}
 +
 +macro_rules! RENAMES_SECTION_TEMPLATE {
 +    () => {
 +        r#"
 +### Past names
 +
 +{names}
 +"#
 +    };
 +}
 +macro_rules! RENAME_VALUE_TEMPLATE {
 +    () => {
 +        "* `{name}`\n"
 +    };
 +}
 +
 +const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
 +    &["clippy_utils", "diagnostics", "span_lint"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_help"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_note"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
 +    &["clippy_utils", "diagnostics", "span_lint_and_then"],
 +    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
 +];
 +const SUGGESTION_DIAGNOSTIC_BUILDER_METHODS: [(&str, bool); 9] = [
 +    ("span_suggestion", false),
 +    ("span_suggestion_short", false),
 +    ("span_suggestion_verbose", false),
 +    ("span_suggestion_hidden", false),
 +    ("tool_only_span_suggestion", false),
 +    ("multipart_suggestion", true),
 +    ("multipart_suggestions", true),
 +    ("tool_only_multipart_suggestion", true),
 +    ("span_suggestions", true),
 +];
 +const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
 +    &["clippy_utils", "diagnostics", "multispan_sugg"],
 +    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
 +];
 +const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 +
 +/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 +const APPLICABILITY_NAME_INDEX: usize = 2;
 +/// This applicability will be set for unresolved applicability values.
 +const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
 +/// The version that will be displayed if none has been defined
 +const VERSION_DEFAULT_STR: &str = "Unknown";
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Collects metadata about clippy lints for the website.
 +    ///
 +    /// This lint will be used to report problems of syntax parsing. You should hopefully never
 +    /// see this but never say never I guess ^^
 +    ///
 +    /// ### Why is this bad?
 +    /// This is not a bad thing but definitely a hacky way to do it. See
 +    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
 +    /// about the implementation.
 +    ///
 +    /// ### Known problems
 +    /// Hopefully none. It would be pretty uncool to have a problem here :)
 +    ///
 +    /// ### Example output
 +    /// ```json,ignore
 +    /// {
 +    ///     "id": "internal_metadata_collector",
 +    ///     "id_span": {
 +    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
 +    ///         "line": 1
 +    ///     },
 +    ///     "group": "clippy::internal",
 +    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
 +    /// }
 +    /// ```
 +    #[clippy::version = "1.56.0"]
 +    pub INTERNAL_METADATA_COLLECTOR,
 +    internal_warn,
 +    "A busy bee collection metadata about lints"
 +}
 +
 +impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
 +
 +#[allow(clippy::module_name_repetitions)]
 +#[derive(Debug, Clone)]
 +pub struct MetadataCollector {
 +    /// All collected lints
 +    ///
 +    /// We use a Heap here to have the lints added in alphabetic order in the export
 +    lints: BinaryHeap<LintMetadata>,
 +    applicability_info: FxHashMap<String, ApplicabilityInfo>,
 +    config: Vec<ClippyConfiguration>,
 +    clippy_project_root: PathBuf,
 +}
 +
 +impl MetadataCollector {
 +    pub fn new() -> Self {
 +        Self {
 +            lints: BinaryHeap::<LintMetadata>::default(),
 +            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
 +            config: collect_configs(),
 +            clippy_project_root: std::env::current_dir()
 +                .expect("failed to get current dir")
 +                .ancestors()
 +                .nth(1)
 +                .expect("failed to get project root")
 +                .to_path_buf(),
 +        }
 +    }
 +
 +    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
 +        self.config
 +            .iter()
 +            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
 +            .map(ToString::to_string)
 +            .reduce(|acc, x| acc + &x)
 +            .map(|configurations| format!(CONFIGURATION_SECTION_TEMPLATE!(), configurations = configurations))
 +    }
 +}
 +
 +impl Drop for MetadataCollector {
 +    /// You might ask: How hacky is this?
 +    /// My answer:     YES
 +    fn drop(&mut self) {
 +        // The metadata collector gets dropped twice, this makes sure that we only write
 +        // when the list is full
 +        if self.lints.is_empty() {
 +            return;
 +        }
 +
 +        let mut applicability_info = std::mem::take(&mut self.applicability_info);
 +
 +        // Mapping the final data
 +        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
 +        for x in &mut lints {
 +            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
 +            replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
 +        }
 +
 +        collect_renames(&mut lints);
 +
 +        // Outputting
 +        if Path::new(OUTPUT_FILE).exists() {
 +            fs::remove_file(OUTPUT_FILE).unwrap();
 +        }
 +        let mut file = OpenOptions::new().write(true).create(true).open(OUTPUT_FILE).unwrap();
 +        writeln!(file, "{}", serde_json::to_string_pretty(&lints).unwrap()).unwrap();
 +    }
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct LintMetadata {
 +    id: String,
 +    id_span: SerializableSpan,
 +    group: String,
 +    level: String,
 +    docs: String,
 +    version: String,
 +    /// This field is only used in the output and will only be
 +    /// mapped shortly before the actual output.
 +    applicability: Option<ApplicabilityInfo>,
 +}
 +
 +impl LintMetadata {
 +    fn new(
 +        id: String,
 +        id_span: SerializableSpan,
 +        group: String,
 +        level: &'static str,
 +        version: String,
 +        docs: String,
 +    ) -> Self {
 +        Self {
 +            id,
 +            id_span,
 +            group,
 +            level: level.to_string(),
 +            version,
 +            docs,
 +            applicability: None,
 +        }
 +    }
 +}
 +
 +fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
 +    let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
 +    let mut lines = doc_lines.iter_mut();
 +
 +    'outer: loop {
 +        // Find the start of the example
 +
 +        // ```rust
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start().starts_with("```rust") => {
 +                    if line.contains("ignore") || line.contains("no_run") {
 +                        // A {{produces}} marker may have been put on a ignored code block by mistake,
 +                        // just seek to the end of the code block and continue checking.
 +                        if lines.any(|line| line.trim_start().starts_with("```")) {
 +                            continue;
 +                        }
 +
 +                        panic!("lint `{}` has an unterminated code block", lint_name)
 +                    }
 +
 +                    break;
 +                },
 +                Some(line) if line.trim_start() == "{{produces}}" => {
 +                    panic!(
 +                        "lint `{}` has marker {{{{produces}}}} with an ignored or missing code block",
 +                        lint_name
 +                    )
 +                },
 +                Some(line) => {
 +                    let line = line.trim();
 +                    // These are the two most common markers of the corrections section
 +                    if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
 +                        break 'outer;
 +                    }
 +                },
 +                None => break 'outer,
 +            }
 +        }
 +
 +        // Collect the example
 +        let mut example = Vec::new();
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.trim_start() == "```" => break,
 +                Some(line) => example.push(line),
 +                None => panic!("lint `{}` has an unterminated code block", lint_name),
 +            }
 +        }
 +
 +        // Find the {{produces}} and attempt to generate the output
 +        loop {
 +            match lines.next() {
 +                Some(line) if line.is_empty() => {},
 +                Some(line) if line.trim() == "{{produces}}" => {
 +                    let output = get_lint_output(lint_name, &example, clippy_project_root);
 +                    line.replace_range(
 +                        ..,
 +                        &format!(
 +                            "<details>\
 +                            <summary>Produces</summary>\n\
 +                            \n\
 +                            ```text\n\
 +                            {}\n\
 +                            ```\n\
 +                        </details>",
 +                            output
 +                        ),
 +                    );
 +
 +                    break;
 +                },
 +                // No {{produces}}, we can move on to the next example
 +                Some(_) => break,
 +                None => break 'outer,
 +            }
 +        }
 +    }
 +
 +    *docs = cleanup_docs(&doc_lines);
 +}
 +
 +fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
 +    let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
 +    let file = dir.path().join("lint_example.rs");
 +
 +    let mut source = String::new();
 +    let unhidden = example
 +        .iter()
 +        .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
 +
 +    // Get any attributes
 +    let mut lines = unhidden.peekable();
 +    while let Some(line) = lines.peek() {
 +        if line.starts_with("#!") {
 +            source.push_str(line);
 +            source.push('\n');
 +            lines.next();
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    let needs_main = !example.iter().any(|line| line.contains("fn main"));
 +    if needs_main {
 +        source.push_str("fn main() {\n");
 +    }
 +
 +    for line in lines {
 +        source.push_str(line);
 +        source.push('\n');
 +    }
 +
 +    if needs_main {
 +        source.push_str("}\n");
 +    }
 +
 +    if let Err(e) = fs::write(&file, &source) {
 +        panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
 +    }
 +
 +    let prefixed_name = format!("{}{lint_name}", CLIPPY_LINT_GROUP_PREFIX);
 +
 +    let mut cmd = Command::new("cargo");
 +
 +    cmd.current_dir(clippy_project_root)
 +        .env("CARGO_INCREMENTAL", "0")
 +        .env("CLIPPY_ARGS", "")
 +        .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
 +        // We need to disable this to enable all lints
 +        .env("ENABLE_METADATA_COLLECTION", "0")
 +        .args(["run", "--bin", "clippy-driver"])
 +        .args(["--target-dir", "./clippy_lints/target"])
 +        .args(["--", "--error-format=json"])
 +        .args(["--edition", "2021"])
 +        .arg("-Cdebuginfo=0")
 +        .args(["-A", "clippy::all"])
 +        .args(["-W", &prefixed_name])
 +        .args(["-L", "./target/debug"])
 +        .args(["-Z", "no-codegen"]);
 +
 +    let output = cmd
 +        .arg(file.as_path())
 +        .output()
 +        .unwrap_or_else(|e| panic!("failed to run `{:?}`: {e}", cmd));
 +
 +    let tmp_file_path = file.to_string_lossy();
 +    let stderr = std::str::from_utf8(&output.stderr).unwrap();
 +    let msgs = stderr
 +        .lines()
 +        .filter(|line| line.starts_with('{'))
 +        .map(|line| serde_json::from_str(line).unwrap())
 +        .collect::<Vec<serde_json::Value>>();
 +
 +    let mut rendered = String::new();
 +    let iter = msgs
 +        .iter()
 +        .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
 +
 +    for message in iter {
 +        let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
 +        rendered.push_str(rendered_part);
 +    }
 +
 +    if rendered.is_empty() {
 +        let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
 +        let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
 +        panic!(
 +            "did not find lint `{}` in output of example, got:\n{}\n{}",
 +            lint_name,
 +            non_json.join("\n"),
 +            rendered.join("\n")
 +        );
 +    }
 +
 +    // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
 +    rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
 +}
 +
 +#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 +struct SerializableSpan {
 +    path: String,
 +    line: usize,
 +}
 +
 +impl fmt::Display for SerializableSpan {
 +    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 +        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
 +    }
 +}
 +
 +impl SerializableSpan {
 +    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
 +        Self::from_span(cx, item.ident.span)
 +    }
 +
 +    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
 +        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
 +
 +        Self {
 +            path: format!("{}", loc.file.name.prefer_remapped()),
 +            line: loc.line,
 +        }
 +    }
 +}
 +
 +#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
 +struct ApplicabilityInfo {
 +    /// Indicates if any of the lint emissions uses multiple spans. This is related to
 +    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
 +    /// currently not be applied automatically.
 +    is_multi_part_suggestion: bool,
 +    applicability: Option<usize>,
 +}
 +
 +impl Serialize for ApplicabilityInfo {
 +    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
 +    where
 +        S: Serializer,
 +    {
 +        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
 +        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
 +        if let Some(index) = self.applicability {
 +            s.serialize_field(
 +                "applicability",
 +                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
 +            )?;
 +        } else {
 +            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
 +        }
 +        s.end()
 +    }
 +}
 +
 +// ==================================================================
 +// Configuration
 +// ==================================================================
 +#[derive(Debug, Clone, Default)]
 +pub struct ClippyConfiguration {
 +    name: String,
 +    config_type: &'static str,
 +    default: String,
 +    lints: Vec<String>,
 +    doc: String,
 +    #[allow(dead_code)]
 +    deprecation_reason: Option<&'static str>,
 +}
 +
 +impl ClippyConfiguration {
 +    pub fn new(
 +        name: &'static str,
 +        config_type: &'static str,
 +        default: String,
 +        doc_comment: &'static str,
 +        deprecation_reason: Option<&'static str>,
 +    ) -> Self {
 +        let (lints, doc) = parse_config_field_doc(doc_comment)
 +            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
 +
 +        Self {
 +            name: to_kebab(name),
 +            lints,
 +            doc,
 +            config_type,
 +            default,
 +            deprecation_reason,
 +        }
 +    }
 +}
 +
 +fn collect_configs() -> Vec<ClippyConfiguration> {
 +    crate::utils::conf::metadata::get_configuration_metadata()
 +}
 +
 +/// This parses the field documentation of the config struct.
 +///
 +/// ```rust, ignore
 +/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
 +/// ```
 +///
 +/// Would yield:
 +/// ```rust, ignore
 +/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
 +/// ```
 +fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
 +    const DOC_START: &str = " Lint: ";
 +    if_chain! {
 +        if doc_comment.starts_with(DOC_START);
 +        if let Some(split_pos) = doc_comment.find('.');
 +        then {
 +            let mut doc_comment = doc_comment.to_string();
 +            let mut documentation = doc_comment.split_off(split_pos);
 +
 +            // Extract lints
 +            doc_comment.make_ascii_lowercase();
 +            let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
 +
 +            // Format documentation correctly
 +            // split off leading `.` from lint name list and indent for correct formatting
 +            documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
 +
 +            Some((lints, documentation))
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
 +fn to_kebab(config_name: &str) -> String {
 +    config_name.replace('_', "-")
 +}
 +
 +impl fmt::Display for ClippyConfiguration {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> fmt::Result {
 +        write!(
 +            f,
 +            CONFIGURATION_VALUE_TEMPLATE!(),
 +            name = self.name,
 +            ty = self.config_type,
 +            doc = self.doc,
 +            default = self.default
 +        )
 +    }
 +}
 +
 +// ==================================================================
 +// Lint pass
 +// ==================================================================
 +impl<'hir> LateLintPass<'hir> for MetadataCollector {
 +    /// Collecting lint declarations like:
 +    /// ```rust, ignore
 +    /// declare_clippy_lint! {
 +    ///     /// ### What it does
 +    ///     /// Something IDK.
 +    ///     pub SOME_LINT,
 +    ///     internal,
 +    ///     "Who am I?"
 +    /// }
 +    /// ```
 +    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
 +        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
 +            // Normal lint
 +            if_chain! {
 +                // item validation
 +                if is_lint_ref_type(cx, ty);
 +                // disallow check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // metadata extraction
 +                if let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item);
 +                if let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
 +                        raw_docs.push_str(&configuration_section);
 +                    }
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        group,
 +                        level,
 +                        version,
 +                        raw_docs,
 +                    ));
 +                }
 +            }
 +
 +            if_chain! {
 +                if is_deprecated_lint(cx, ty);
 +                // disallow check
 +                let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase();
 +                if !BLACK_LISTED_LINTS.contains(&lint_name.as_str());
 +                // Metadata the little we can get from a deprecated lint
 +                if let Some(raw_docs) = extract_attr_docs_or_lint(cx, item);
 +                then {
 +                    let version = get_lint_version(cx, item);
 +
 +                    self.lints.push(LintMetadata::new(
 +                        lint_name,
 +                        SerializableSpan::from_item(cx, item),
 +                        DEPRECATED_LINT_GROUP_STR.to_string(),
 +                        DEPRECATED_LINT_LEVEL,
 +                        version,
 +                        raw_docs,
 +                    ));
 +                }
 +            }
 +        }
 +    }
 +
 +    /// Collecting constant applicability from the actual lint emissions
 +    ///
 +    /// Example:
 +    /// ```rust, ignore
 +    /// span_lint_and_sugg(
 +    ///     cx,
 +    ///     SOME_LINT,
 +    ///     item.span,
 +    ///     "Le lint message",
 +    ///     "Here comes help:",
 +    ///     "#![allow(clippy::all)]",
 +    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
 +    /// );
 +    /// ```
 +    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
 +        if let Some(args) = match_lint_emission(cx, expr) {
 +            let emission_info = extract_emission_info(cx, args);
 +            if emission_info.is_empty() {
 +                // See:
 +                // - src/misc.rs:734:9
 +                // - src/methods/mod.rs:3545:13
 +                // - src/methods/mod.rs:3496:13
 +                // We are basically unable to resolve the lint name itself.
 +                return;
 +            }
 +
 +            for (lint_name, applicability, is_multi_part) in emission_info {
 +                let app_info = self.applicability_info.entry(lint_name).or_default();
 +                app_info.applicability = applicability;
 +                app_info.is_multi_part_suggestion = is_multi_part;
 +            }
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint definition extraction
 +// ==================================================================
 +fn sym_to_string(sym: Symbol) -> String {
 +    sym.as_str().to_string()
 +}
 +
 +fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    extract_attr_docs(cx, item).or_else(|| {
 +        lint_collection_error_item(cx, item, "could not collect the lint documentation");
 +        None
 +    })
 +}
 +
 +/// This function collects all documentation that has been added to an item using
 +/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
 +///
 +/// ```ignore
 +/// #[doc = r"Hello world!"]
 +/// #[doc = r"=^.^="]
 +/// struct SomeItem {}
 +/// ```
 +///
 +/// Would result in `Hello world!\n=^.^=\n`
 +fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
 +    let attrs = cx.tcx.hir().attrs(item.hir_id());
 +    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
 +
 +    if let Some(line) = lines.next() {
 +        let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n");
 +        return Some(raw_docs);
 +    }
 +
 +    None
 +}
 +
 +/// This function may modify the doc comment to ensure that the string can be displayed using a
 +/// markdown viewer in Clippy's lint list. The following modifications could be applied:
 +/// * Removal of leading space after a new line. (Important to display tables)
 +/// * Ensures that code blocks only contain language information
 +fn cleanup_docs(docs_collection: &Vec<String>) -> String {
 +    let mut in_code_block = false;
 +    let mut is_code_block_rust = false;
 +
 +    let mut docs = String::new();
 +    for line in docs_collection {
 +        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
 +        if is_code_block_rust && line.trim_start().starts_with("# ") {
 +            continue;
 +        }
 +
 +        // The line should be represented in the lint list, even if it's just an empty line
 +        docs.push('\n');
 +        if let Some(info) = line.trim_start().strip_prefix("```") {
 +            in_code_block = !in_code_block;
 +            is_code_block_rust = false;
 +            if in_code_block {
 +                let lang = info
 +                    .trim()
 +                    .split(',')
 +                    // remove rustdoc directives
 +                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
 +                    // if no language is present, fill in "rust"
 +                    .unwrap_or("rust");
 +                docs.push_str("```");
 +                docs.push_str(lang);
 +
 +                is_code_block_rust = lang == "rust";
 +                continue;
 +            }
 +        }
 +        // This removes the leading space that the macro translation introduces
 +        if let Some(stripped_doc) = line.strip_prefix(' ') {
 +            docs.push_str(stripped_doc);
 +        } else if !line.is_empty() {
 +            docs.push_str(line);
 +        }
 +    }
 +
 +    docs
 +}
 +
 +fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
 +    extract_clippy_version_value(cx, item).map_or_else(
 +        || VERSION_DEFAULT_STR.to_string(),
 +        |version| version.as_str().to_string(),
 +    )
 +}
 +
 +fn get_lint_group_and_level_or_lint(
 +    cx: &LateContext<'_>,
 +    lint_name: &str,
 +    item: &Item<'_>,
 +) -> Option<(String, &'static str)> {
 +    let result = cx.lint_store.check_lint_name(
 +        lint_name,
 +        Some(sym::clippy),
++        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
 +    );
 +    if let CheckLintNameResult::Tool(Ok(lint_lst)) = result {
 +        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
 +            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
 +                return None;
 +            }
 +
 +            if let Some(level) = get_lint_level_from_group(&group) {
 +                Some((group, level))
 +            } else {
 +                lint_collection_error_item(
 +                    cx,
 +                    item,
 +                    &format!("Unable to determine lint level for found group `{}`", group),
 +                );
 +                None
 +            }
 +        } else {
 +            lint_collection_error_item(cx, item, "Unable to determine lint group");
 +            None
 +        }
 +    } else {
 +        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
 +        None
 +    }
 +}
 +
 +fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 +    for (group_name, lints, _) in cx.lint_store.get_lint_groups() {
 +        if IGNORED_LINT_GROUPS.contains(&group_name) {
 +            continue;
 +        }
 +
 +        if lints.iter().any(|group_lint| *group_lint == lint_id) {
 +            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
 +            return Some((*group).to_string());
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
 +    DEFAULT_LINT_LEVELS
 +        .iter()
 +        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
 +}
 +
 +pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let hir::TyKind::Path(ref path) = ty.kind {
 +        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
 +            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
 +        }
 +    }
 +
 +    false
 +}
 +
 +fn collect_renames(lints: &mut Vec<LintMetadata>) {
 +    for lint in lints {
 +        let mut collected = String::new();
 +        let mut names = vec![lint.id.clone()];
 +
 +        loop {
 +            if let Some(lint_name) = names.pop() {
 +                for (k, v) in RENAMED_LINTS {
 +                    if_chain! {
 +                        if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        if name == lint_name;
 +                        if let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX);
 +                        then {
 +                            write!(collected, RENAME_VALUE_TEMPLATE!(), name = past_name).unwrap();
 +                            names.push(past_name.to_string());
 +                        }
 +                    }
 +                }
 +
 +                continue;
 +            }
 +
 +            break;
 +        }
 +
 +        if !collected.is_empty() {
 +            write!(&mut lint.docs, RENAMES_SECTION_TEMPLATE!(), names = collected).unwrap();
 +        }
 +    }
 +}
 +
 +// ==================================================================
 +// Lint emission
 +// ==================================================================
 +fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
 +    span_lint(
 +        cx,
 +        INTERNAL_METADATA_COLLECTOR,
 +        item.ident.span,
 +        &format!("metadata collection error for `{}`: {}", item.ident.name, message),
 +    );
 +}
 +
 +// ==================================================================
 +// Applicability
 +// ==================================================================
 +/// This function checks if a given expression is equal to a simple lint emission function call.
 +/// It will return the function arguments if the emission matched any function.
 +fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
 +    LINT_EMISSION_FUNCTIONS
 +        .iter()
 +        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
 +}
 +
 +fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
 +    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
 +}
 +
 +fn extract_emission_info<'hir>(
 +    cx: &LateContext<'hir>,
 +    args: &'hir [hir::Expr<'hir>],
 +) -> Vec<(String, Option<usize>, bool)> {
 +    let mut lints = Vec::new();
 +    let mut applicability = None;
 +    let mut multi_part = false;
 +
 +    for arg in args {
 +        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
 +
 +        if match_type(cx, arg_ty, &paths::LINT) {
 +            // If we found the lint arg, extract the lint name
 +            let mut resolved_lints = resolve_lints(cx, arg);
 +            lints.append(&mut resolved_lints);
 +        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
 +            applicability = resolve_applicability(cx, arg);
 +        } else if arg_ty.is_closure() {
 +            multi_part |= check_is_multi_part(cx, arg);
 +            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
 +        }
 +    }
 +
 +    lints
 +        .into_iter()
 +        .map(|lint_name| (lint_name, applicability, multi_part))
 +        .collect()
 +}
 +
 +/// Resolves the possible lints that this expression could reference
 +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
 +    let mut resolver = LintResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.lints
 +}
 +
 +/// This function tries to resolve the linked applicability to the given expression.
 +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
 +    let mut resolver = ApplicabilityResolver::new(cx);
 +    resolver.visit_expr(expr);
 +    resolver.complete()
 +}
 +
 +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
 +    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
 +        let mut scanner = IsMultiSpanScanner::new(cx);
 +        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
 +        return scanner.is_multi_part();
 +    } else if let Some(local) = get_parent_local(cx, closure_expr) {
 +        if let Some(local_init) = local.init {
 +            return check_is_multi_part(cx, local_init);
 +        }
 +    }
 +
 +    false
 +}
 +
 +struct LintResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    lints: Vec<String>,
 +}
 +
 +impl<'a, 'hir> LintResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            lints: Vec::<String>::default(),
 +        }
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for LintResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        if_chain! {
 +            if let ExprKind::Path(qpath) = &expr.kind;
 +            if let QPath::Resolved(_, path) = qpath;
 +
 +            let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +            if match_type(self.cx, expr_ty, &paths::LINT);
 +            then {
 +                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
 +                    let lint_name = last_path_segment(qpath).ident.name;
 +                    self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
 +                } else if let Some(local) = get_parent_local(self.cx, expr) {
 +                    if let Some(local_init) = local.init {
 +                        intravisit::walk_expr(self, local_init);
 +                    }
 +                }
 +            }
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct ApplicabilityResolver<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
 +    applicability_index: Option<usize>,
 +}
 +
 +impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            applicability_index: None,
 +        }
 +    }
 +
 +    fn add_new_index(&mut self, new_index: usize) {
 +        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
 +    }
 +
 +    fn complete(self) -> Option<usize> {
 +        self.applicability_index
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_path(&mut self, path: &'hir hir::Path<'hir>, _id: hir::HirId) {
 +        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
 +            if match_path(path, enum_value) {
 +                self.add_new_index(index);
 +                return;
 +            }
 +        }
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
 +
 +        if_chain! {
 +            if match_type(self.cx, expr_ty, &paths::APPLICABILITY);
 +            if let Some(local) = get_parent_local(self.cx, expr);
 +            if let Some(local_init) = local.init;
 +            then {
 +                intravisit::walk_expr(self, local_init);
 +            }
 +        };
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
 +
 +/// This returns the parent local node if the expression is a reference one
 +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> {
 +    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
 +        if let hir::def::Res::Local(local_hir) = path.res {
 +            return get_parent_local_hir_id(cx, local_hir);
 +        }
 +    }
 +
 +    None
 +}
 +
 +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> {
 +    let map = cx.tcx.hir();
 +
 +    match map.find(map.get_parent_node(hir_id)) {
 +        Some(hir::Node::Local(local)) => Some(local),
 +        Some(hir::Node::Pat(pattern)) => get_parent_local_hir_id(cx, pattern.hir_id),
 +        _ => None,
 +    }
 +}
 +
 +/// This visitor finds the highest applicability value in the visited expressions
 +struct IsMultiSpanScanner<'a, 'hir> {
 +    cx: &'a LateContext<'hir>,
 +    suggestion_count: usize,
 +}
 +
 +impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
 +    fn new(cx: &'a LateContext<'hir>) -> Self {
 +        Self {
 +            cx,
 +            suggestion_count: 0,
 +        }
 +    }
 +
 +    /// Add a new single expression suggestion to the counter
 +    fn add_single_span_suggestion(&mut self) {
 +        self.suggestion_count += 1;
 +    }
 +
 +    /// Signals that a suggestion with possible multiple spans was found
 +    fn add_multi_part_suggestion(&mut self) {
 +        self.suggestion_count += 2;
 +    }
 +
 +    /// Checks if the suggestions include multiple spans
 +    fn is_multi_part(&self) -> bool {
 +        self.suggestion_count > 1
 +    }
 +}
 +
 +impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
 +    type NestedFilter = nested_filter::All;
 +
 +    fn nested_visit_map(&mut self) -> Self::Map {
 +        self.cx.tcx.hir()
 +    }
 +
 +    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 +        // Early return if the lint is already multi span
 +        if self.is_multi_part() {
 +            return;
 +        }
 +
 +        match &expr.kind {
 +            ExprKind::Call(fn_expr, _args) => {
 +                let found_function = SUGGESTION_FUNCTIONS
 +                    .iter()
 +                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
 +                if found_function {
 +                    // These functions are all multi part suggestions
 +                    self.add_single_span_suggestion();
 +                }
 +            },
 +            ExprKind::MethodCall(path, arg, _arg_span) => {
 +                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
 +                if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
 +                    let called_method = path.ident.name.as_str().to_string();
 +                    for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {
 +                        if *method_name == called_method {
 +                            if *is_multi_part {
 +                                self.add_multi_part_suggestion();
 +                            } else {
 +                                self.add_single_span_suggestion();
 +                            }
 +                            break;
 +                        }
 +                    }
 +                }
 +            },
 +            _ => {},
 +        }
 +
 +        intravisit::walk_expr(self, expr);
 +    }
 +}
index fa2383066f3f622af5353b45d5431fe33d7df445,0000000000000000000000000000000000000000..5533840b166f8c783c94e076b25d670bf26fbe71
mode 100644,000000..100644
--- /dev/null
@@@ -1,709 -1,0 +1,826 @@@
- use clippy_utils::source::{snippet_opt, snippet_with_applicability};
 +use std::borrow::Cow;
 +use std::iter;
 +use std::ops::{Deref, Range};
 +
 +use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-     WRITE_LITERAL
++use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 +use rustc_ast::ast::{Expr, ExprKind, Impl, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
++use rustc_ast::ptr::P;
 +use rustc_ast::token::{self, LitKind};
 +use rustc_ast::tokenstream::TokenStream;
 +use rustc_errors::{Applicability, DiagnosticBuilder};
 +use rustc_lexer::unescape::{self, EscapeError};
 +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 +use rustc_parse::parser;
 +use rustc_session::{declare_tool_lint, impl_lint_pass};
 +use rustc_span::symbol::{kw, Symbol};
 +use rustc_span::{sym, BytePos, InnerSpan, Span, DUMMY_SP};
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `println!("")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// println!();
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINTLN_EMPTY_STRING,
 +    style,
 +    "using `println!(\"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `print!()` with a format
 +    /// string that ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `println!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let name = "World";
 +    /// print!("Hello {}!\n", name);
 +    /// ```
 +    /// use println!() instead
 +    /// ```rust
 +    /// # let name = "World";
 +    /// println!("Hello {}!", name);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_WITH_NEWLINE,
 +    style,
 +    "using `print!()` with a format string that ends in a single newline"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stdout*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stdout* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// * Only catches `print!` and `println!` calls.
 +    /// * The lint level is unaffected by crate attributes. The level can still
 +    ///   be set for functions, modules and other items. To change the level for
 +    ///   the entire crate, please use command line flags. More information and a
 +    ///   configuration example can be found in [clippy#6610].
 +    ///
 +    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("Hello world!");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_STDOUT,
 +    restriction,
 +    "printing on stdout"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for printing on *stderr*. The purpose of this lint
 +    /// is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// People often print on *stderr* while debugging an
 +    /// application and might forget to remove those prints afterward.
 +    ///
 +    /// ### Known problems
 +    /// * Only catches `eprint!` and `eprintln!` calls.
 +    /// * The lint level is unaffected by crate attributes. The level can still
 +    ///   be set for functions, modules and other items. To change the level for
 +    ///   the entire crate, please use command line flags. More information and a
 +    ///   configuration example can be found in [clippy#6610].
 +    ///
 +    /// [clippy#6610]: https://github.com/rust-lang/rust-clippy/issues/6610#issuecomment-977120558
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// eprintln!("Hello world!");
 +    /// ```
 +    #[clippy::version = "1.50.0"]
 +    pub PRINT_STDERR,
 +    restriction,
 +    "printing on stderr"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// Checks for use of `Debug` formatting. The purpose of this
 +    /// lint is to catch debugging remnants.
 +    ///
 +    /// ### Why is this bad?
 +    /// The purpose of the `Debug` trait is to facilitate
 +    /// debugging Rust code. It should not be used in user-facing output.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # let foo = "bar";
 +    /// println!("{:?}", foo);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub USE_DEBUG,
 +    restriction,
 +    "use of `Debug`-based formatting"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `print!`/`println!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `println!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Known problems
 +    /// Will also warn with macro calls as arguments that expand to literals
 +    /// -- e.g., `println!("{}", env!("FOO"))`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// println!("{}", "foo");
 +    /// ```
 +    /// use the literal without formatting:
 +    /// ```rust
 +    /// println!("foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub PRINT_LITERAL,
 +    style,
 +    "printing a literal with a format string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `writeln!(buf, "")` to
 +    /// print a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!(buf)`, which is simpler.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITELN_EMPTY_STRING,
 +    style,
 +    "using `writeln!(buf, \"\")` with an empty string"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns when you use `write!()` with a format
 +    /// string that
 +    /// ends in a newline.
 +    ///
 +    /// ### Why is this bad?
 +    /// You should use `writeln!()` instead, which appends the
 +    /// newline.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// write!(buf, "Hello {}!\n", name);
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let name = "World";
 +    /// writeln!(buf, "Hello {}!", name);
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_WITH_NEWLINE,
 +    style,
 +    "using `write!()` with a format string that ends in a single newline"
 +}
 +
 +declare_clippy_lint! {
 +    /// ### What it does
 +    /// This lint warns about the use of literals as `write!`/`writeln!` args.
 +    ///
 +    /// ### Why is this bad?
 +    /// Using literals as `writeln!` args is inefficient
 +    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
 +    /// (i.e., just put the literal in the format string)
 +    ///
 +    /// ### Known problems
 +    /// Will also warn with macro calls as arguments that expand to literals
 +    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
 +    ///
 +    /// ### Example
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "{}", "foo");
 +    /// ```
 +    ///
 +    /// Use instead:
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// writeln!(buf, "foo");
 +    /// ```
 +    #[clippy::version = "pre 1.29.0"]
 +    pub WRITE_LITERAL,
 +    style,
 +    "writing a literal with a format string"
 +}
 +
++declare_clippy_lint! {
++    /// ### What it does
++    /// This lint warns when a named parameter in a format string is used as a positional one.
++    ///
++    /// ### Why is this bad?
++    /// It may be confused for an assignment and obfuscates which parameter is being used.
++    ///
++    /// ### Example
++    /// ```rust
++    /// println!("{}", x = 10);
++    /// ```
++    ///
++    /// Use instead:
++    /// ```rust
++    /// println!("{x}", x = 10);
++    /// ```
++    #[clippy::version = "1.63.0"]
++    pub POSITIONAL_NAMED_FORMAT_PARAMETERS,
++    suspicious,
++    "named parameter in a format string is used positionally"
++}
++
 +#[derive(Default)]
 +pub struct Write {
 +    in_debug_impl: bool,
 +}
 +
 +impl_lint_pass!(Write => [
 +    PRINT_WITH_NEWLINE,
 +    PRINTLN_EMPTY_STRING,
 +    PRINT_STDOUT,
 +    PRINT_STDERR,
 +    USE_DEBUG,
 +    PRINT_LITERAL,
 +    WRITE_WITH_NEWLINE,
 +    WRITELN_EMPTY_STRING,
++    WRITE_LITERAL,
++    POSITIONAL_NAMED_FORMAT_PARAMETERS,
 +]);
 +
 +impl EarlyLintPass for Write {
 +    fn check_item(&mut self, _: &EarlyContext<'_>, item: &Item) {
 +        if let ItemKind::Impl(box Impl {
 +            of_trait: Some(trait_ref),
 +            ..
 +        }) = &item.kind
 +        {
 +            let trait_name = trait_ref
 +                .path
 +                .segments
 +                .iter()
 +                .last()
 +                .expect("path has at least one segment")
 +                .ident
 +                .name;
 +            if trait_name == sym::Debug {
 +                self.in_debug_impl = true;
 +            }
 +        }
 +    }
 +
 +    fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &Item) {
 +        self.in_debug_impl = false;
 +    }
 +
 +    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        fn is_build_script(cx: &EarlyContext<'_>) -> bool {
 +            // Cargo sets the crate name for build scripts to `build_script_build`
 +            cx.sess()
 +                .opts
 +                .crate_name
 +                .as_ref()
 +                .map_or(false, |crate_name| crate_name == "build_script_build")
 +        }
 +
 +        if mac.path == sym!(print) {
 +            if !is_build_script(cx) {
 +                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `print!`");
 +            }
 +            self.lint_print_with_newline(cx, mac);
 +        } else if mac.path == sym!(println) {
 +            if !is_build_script(cx) {
 +                span_lint(cx, PRINT_STDOUT, mac.span(), "use of `println!`");
 +            }
 +            self.lint_println_empty_string(cx, mac);
 +        } else if mac.path == sym!(eprint) {
 +            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprint!`");
 +            self.lint_print_with_newline(cx, mac);
 +        } else if mac.path == sym!(eprintln) {
 +            span_lint(cx, PRINT_STDERR, mac.span(), "use of `eprintln!`");
 +            self.lint_println_empty_string(cx, mac);
 +        } else if mac.path == sym!(write) {
 +            if let (Some(fmt_str), dest) = self.check_tts(cx, mac.args.inner_tokens(), true) {
 +                if check_newlines(&fmt_str) {
 +                    let (nl_span, only_nl) = newline_span(&fmt_str);
 +                    let nl_span = match (dest, only_nl) {
 +                        // Special case of `write!(buf, "\n")`: Mark everything from the end of
 +                        // `buf` for removal so no trailing comma [`writeln!(buf, )`] remains.
 +                        (Some(dest_expr), true) => nl_span.with_lo(dest_expr.span.hi()),
 +                        _ => nl_span,
 +                    };
 +                    span_lint_and_then(
 +                        cx,
 +                        WRITE_WITH_NEWLINE,
 +                        mac.span(),
 +                        "using `write!()` with a format string that ends in a single newline",
 +                        |err| {
 +                            err.multipart_suggestion(
 +                                "use `writeln!()` instead",
 +                                vec![(mac.path.span, String::from("writeln")), (nl_span, String::new())],
 +                                Applicability::MachineApplicable,
 +                            );
 +                        },
 +                    );
 +                }
 +            }
 +        } else if mac.path == sym!(writeln) {
 +            if let (Some(fmt_str), expr) = self.check_tts(cx, mac.args.inner_tokens(), true) {
 +                if fmt_str.symbol == kw::Empty {
 +                    let mut applicability = Applicability::MachineApplicable;
 +                    let suggestion = if let Some(e) = expr {
 +                        snippet_with_applicability(cx, e.span, "v", &mut applicability)
 +                    } else {
 +                        applicability = Applicability::HasPlaceholders;
 +                        Cow::Borrowed("v")
 +                    };
 +
 +                    span_lint_and_sugg(
 +                        cx,
 +                        WRITELN_EMPTY_STRING,
 +                        mac.span(),
 +                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
 +                        "replace it with",
 +                        format!("writeln!({})", suggestion),
 +                        applicability,
 +                    );
 +                }
 +            }
 +        }
 +    }
 +}
 +
 +/// Given a format string that ends in a newline and its span, calculates the span of the
 +/// newline, or the format string itself if the format string consists solely of a newline.
 +/// Return this and a boolean indicating whether it only consisted of a newline.
 +fn newline_span(fmtstr: &StrLit) -> (Span, bool) {
 +    let sp = fmtstr.span;
 +    let contents = fmtstr.symbol.as_str();
 +
 +    if contents == r"\n" {
 +        return (sp, true);
 +    }
 +
 +    let newline_sp_hi = sp.hi()
 +        - match fmtstr.style {
 +            StrStyle::Cooked => BytePos(1),
 +            StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
 +        };
 +
 +    let newline_sp_len = if contents.ends_with('\n') {
 +        BytePos(1)
 +    } else if contents.ends_with(r"\n") {
 +        BytePos(2)
 +    } else {
 +        panic!("expected format string to contain a newline");
 +    };
 +
 +    (sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi), false)
 +}
 +
 +/// Stores a list of replacement spans for each argument, but only if all the replacements used an
 +/// empty format string.
 +#[derive(Default)]
 +struct SimpleFormatArgs {
 +    unnamed: Vec<Vec<Span>>,
++    complex_unnamed: Vec<Vec<Span>>,
 +    named: Vec<(Symbol, Vec<Span>)>,
 +}
 +impl SimpleFormatArgs {
 +    fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
 +        self.unnamed.iter().map(|x| match x.as_slice() {
 +            // Ignore the dummy span added from out of order format arguments.
 +            [DUMMY_SP] => &[],
 +            x => x,
 +        })
 +    }
 +
++    fn get_complex_unnamed(&self) -> impl Iterator<Item = &[Span]> {
++        self.complex_unnamed.iter().map(Vec::as_slice)
++    }
++
 +    fn get_named(&self, n: &Path) -> &[Span] {
 +        self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
 +    }
 +
 +    fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
 +        use rustc_parse_format::{
 +            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
 +        };
 +
 +        const SIMPLE: FormatSpec<'_> = FormatSpec {
 +            fill: None,
 +            align: AlignUnknown,
 +            flags: 0,
 +            precision: CountImplied,
 +            precision_span: None,
 +            width: CountImplied,
 +            width_span: None,
 +            ty: "",
 +            ty_span: None,
 +        };
 +
 +        match arg.position {
 +            ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
 +                if self.unnamed.len() <= n {
 +                    // Use a dummy span to mark all unseen arguments.
 +                    self.unnamed.resize_with(n, || vec![DUMMY_SP]);
 +                    if arg.format == SIMPLE {
 +                        self.unnamed.push(vec![span]);
 +                    } else {
 +                        self.unnamed.push(Vec::new());
 +                    }
 +                } else {
 +                    let args = &mut self.unnamed[n];
 +                    match (args.as_mut_slice(), arg.format == SIMPLE) {
 +                        // A non-empty format string has been seen already.
 +                        ([], _) => (),
 +                        // Replace the dummy span, if it exists.
 +                        ([dummy @ DUMMY_SP], true) => *dummy = span,
 +                        ([_, ..], true) => args.push(span),
 +                        ([_, ..], false) => *args = Vec::new(),
 +                    }
 +                }
 +            },
 +            ArgumentNamed(n) => {
 +                let n = Symbol::intern(n);
 +                if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
 +                    match x.1.as_slice() {
 +                        // A non-empty format string has been seen already.
 +                        [] => (),
 +                        [_, ..] if arg.format == SIMPLE => x.1.push(span),
 +                        [_, ..] => x.1 = Vec::new(),
 +                    }
 +                } else if arg.format == SIMPLE {
 +                    self.named.push((n, vec![span]));
 +                } else {
 +                    self.named.push((n, Vec::new()));
 +                }
 +            },
 +        };
 +    }
++
++    fn push_to_complex(&mut self, span: Span, position: usize) {
++        if self.complex_unnamed.len() <= position {
++            self.complex_unnamed.resize_with(position, Vec::new);
++            self.complex_unnamed.push(vec![span]);
++        } else {
++            let args: &mut Vec<Span> = &mut self.complex_unnamed[position];
++            args.push(span);
++        }
++    }
++
++    fn push_complex(
++        &mut self,
++        cx: &EarlyContext<'_>,
++        arg: rustc_parse_format::Argument<'_>,
++        str_lit_span: Span,
++        fmt_span: Span,
++    ) {
++        use rustc_parse_format::{ArgumentImplicitlyIs, ArgumentIs, CountIsParam};
++
++        let snippet = snippet_opt(cx, fmt_span);
++
++        let end = snippet
++            .as_ref()
++            .and_then(|s| s.find(':'))
++            .or_else(|| fmt_span.hi().0.checked_sub(fmt_span.lo().0 + 1).map(|u| u as usize));
++
++        if let (ArgumentIs(n) | ArgumentImplicitlyIs(n), Some(end)) = (arg.position, end) {
++            let span = fmt_span.from_inner(InnerSpan::new(1, end));
++            self.push_to_complex(span, n);
++        };
++
++        if let (CountIsParam(n), Some(span)) = (arg.format.precision, arg.format.precision_span) {
++            // We need to do this hack as precision spans should be converted from .* to .foo$
++            let hack = if snippet.as_ref().and_then(|s| s.find('*')).is_some() {
++                0
++            } else {
++                1
++            };
++
++            let span = str_lit_span.from_inner(InnerSpan {
++                start: span.start + 1,
++                end: span.end - hack,
++            });
++            self.push_to_complex(span, n);
++        };
++
++        if let (CountIsParam(n), Some(span)) = (arg.format.width, arg.format.width_span) {
++            let span = str_lit_span.from_inner(InnerSpan {
++                start: span.start,
++                end: span.end - 1,
++            });
++            self.push_to_complex(span, n);
++        };
++    }
 +}
 +
 +impl Write {
 +    /// Parses a format string into a collection of spans for each argument. This only keeps track
 +    /// of empty format arguments. Will also lint usages of debug format strings outside of debug
 +    /// impls.
 +    fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
 +        use rustc_parse_format::{ParseMode, Parser, Piece};
 +
 +        let str_sym = str_lit.symbol_unescaped.as_str();
 +        let style = match str_lit.style {
 +            StrStyle::Cooked => None,
 +            StrStyle::Raw(n) => Some(n as usize),
 +        };
 +
 +        let mut parser = Parser::new(str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
 +        let mut args = SimpleFormatArgs::default();
 +
 +        while let Some(arg) = parser.next() {
 +            let arg = match arg {
 +                Piece::String(_) => continue,
 +                Piece::NextArgument(arg) => arg,
 +            };
 +            let span = parser
 +                .arg_places
 +                .last()
 +                .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(InnerSpan::new(x.start, x.end)));
 +
 +            if !self.in_debug_impl && arg.format.ty == "?" {
 +                // FIXME: modify rustc's fmt string parser to give us the current span
 +                span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
 +            }
-                 ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
-                     (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
-                     _ => continue,
 +            args.push(arg, span);
++            args.push_complex(cx, arg, str_lit.span, span);
 +        }
 +
 +        parser.errors.is_empty().then_some(args)
 +    }
 +
 +    /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
 +    /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
 +    /// the contents of the string, whether it's a raw string, and the span of the literal in the
 +    /// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
 +    /// `format_str` should be written to.
 +    ///
 +    /// Example:
 +    ///
 +    /// Calling this function on
 +    /// ```rust
 +    /// # use std::fmt::Write;
 +    /// # let mut buf = String::new();
 +    /// # let something = "something";
 +    /// writeln!(buf, "string to write: {}", something);
 +    /// ```
 +    /// will return
 +    /// ```rust,ignore
 +    /// (Some("string to write: {}"), Some(buf))
 +    /// ```
 +    fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
 +        let mut parser = parser::Parser::new(&cx.sess().parse_sess, tts, false, None);
 +        let expr = if is_write {
 +            match parser
 +                .parse_expr()
 +                .map(rustc_ast::ptr::P::into_inner)
 +                .map_err(DiagnosticBuilder::cancel)
 +            {
 +                // write!(e, ...)
 +                Ok(p) if parser.eat(&token::Comma) => Some(p),
 +                // write!(e) or error
 +                e => return (None, e.ok()),
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let fmtstr = match parser.parse_str_lit() {
 +            Ok(fmtstr) => fmtstr,
 +            Err(_) => return (None, expr),
 +        };
 +
 +        let args = match self.parse_fmt_string(cx, &fmtstr) {
 +            Some(args) => args,
 +            None => return (Some(fmtstr), expr),
 +        };
 +
 +        let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
 +        let mut unnamed_args = args.get_unnamed();
++        let mut complex_unnamed_args = args.get_complex_unnamed();
 +        loop {
 +            if !parser.eat(&token::Comma) {
 +                return (Some(fmtstr), expr);
 +            }
 +
 +            let comma_span = parser.prev_token.span;
 +            let token_expr = if let Ok(expr) = parser.parse_expr().map_err(DiagnosticBuilder::cancel) {
 +                expr
 +            } else {
 +                return (Some(fmtstr), None);
 +            };
++            let complex_unnamed_arg = complex_unnamed_args.next();
++
 +            let (fmt_spans, lit) = match &token_expr.kind {
 +                ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
++                ExprKind::Assign(lhs, rhs, _) => {
++                    if let Some(span) = complex_unnamed_arg {
++                        for x in span {
++                            Self::report_positional_named_param(cx, *x, lhs, rhs);
++                        }
++                    }
++                    match (&lhs.kind, &rhs.kind) {
++                        (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
++                        _ => continue,
++                    }
 +                },
 +                _ => {
 +                    unnamed_args.next();
 +                    continue;
 +                },
 +            };
 +
 +            let replacement: String = match lit.token_lit.kind {
 +                LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
 +                    lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
 +                },
 +                LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
 +                    lit.token_lit.symbol.as_str().replace('{', "{{").replace('}', "}}")
 +                },
 +                LitKind::StrRaw(_)
 +                | LitKind::Str
 +                | LitKind::ByteStrRaw(_)
 +                | LitKind::ByteStr
 +                | LitKind::Integer
 +                | LitKind::Float
 +                | LitKind::Err => continue,
 +                LitKind::Byte | LitKind::Char => match lit.token_lit.symbol.as_str() {
 +                    "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
 +                    "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
 +                    "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
 +                    "\\'" => "'",
 +                    "{" => "{{",
 +                    "}" => "}}",
 +                    x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
 +                    x => x,
 +                }
 +                .into(),
 +                LitKind::Bool => lit.token_lit.symbol.as_str().deref().into(),
 +            };
 +
 +            if !fmt_spans.is_empty() {
 +                span_lint_and_then(
 +                    cx,
 +                    lint,
 +                    token_expr.span,
 +                    "literal with an empty format string",
 +                    |diag| {
 +                        diag.multipart_suggestion(
 +                            "try this",
 +                            iter::once((comma_span.to(token_expr.span), String::new()))
 +                                .chain(fmt_spans.iter().copied().zip(iter::repeat(replacement)))
 +                                .collect(),
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +
++    fn report_positional_named_param(cx: &EarlyContext<'_>, span: Span, lhs: &P<Expr>, _rhs: &P<Expr>) {
++        if let ExprKind::Path(_, _p) = &lhs.kind {
++            let mut applicability = Applicability::MachineApplicable;
++            let name = snippet_with_applicability(cx, lhs.span, "name", &mut applicability);
++            // We need to do this hack as precision spans should be converted from .* to .foo$
++            let hack = snippet(cx, span, "").contains('*');
++
++            span_lint_and_sugg(
++                cx,
++                POSITIONAL_NAMED_FORMAT_PARAMETERS,
++                span,
++                &format!("named parameter {} is used as a positional parameter", name),
++                "replace it with",
++                if hack {
++                    format!("{}$", name)
++                } else {
++                    format!("{}", name)
++                },
++                applicability,
++            );
++        };
++    }
++
 +    fn lint_println_empty_string(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
 +            if fmt_str.symbol == kw::Empty {
 +                let name = mac.path.segments[0].ident.name;
 +                span_lint_and_sugg(
 +                    cx,
 +                    PRINTLN_EMPTY_STRING,
 +                    mac.span(),
 +                    &format!("using `{}!(\"\")`", name),
 +                    "replace it with",
 +                    format!("{}!()", name),
 +                    Applicability::MachineApplicable,
 +                );
 +            }
 +        }
 +    }
 +
 +    fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
 +        if let (Some(fmt_str), _) = self.check_tts(cx, mac.args.inner_tokens(), false) {
 +            if check_newlines(&fmt_str) {
 +                let name = mac.path.segments[0].ident.name;
 +                let suggested = format!("{}ln", name);
 +                span_lint_and_then(
 +                    cx,
 +                    PRINT_WITH_NEWLINE,
 +                    mac.span(),
 +                    &format!("using `{}!()` with a format string that ends in a single newline", name),
 +                    |err| {
 +                        err.multipart_suggestion(
 +                            &format!("use `{}!` instead", suggested),
 +                            vec![(mac.path.span, suggested), (newline_span(&fmt_str).0, String::new())],
 +                            Applicability::MachineApplicable,
 +                        );
 +                    },
 +                );
 +            }
 +        }
 +    }
 +}
 +
 +/// Checks if the format string contains a single newline that terminates it.
 +///
 +/// Literal and escaped newlines are both checked (only literal for raw strings).
 +fn check_newlines(fmtstr: &StrLit) -> bool {
 +    let mut has_internal_newline = false;
 +    let mut last_was_cr = false;
 +    let mut should_lint = false;
 +
 +    let contents = fmtstr.symbol.as_str();
 +
 +    let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
 +        let c = c.unwrap();
 +
 +        if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
 +            should_lint = true;
 +        } else {
 +            last_was_cr = c == '\r';
 +            if c == '\n' {
 +                has_internal_newline = true;
 +            }
 +        }
 +    };
 +
 +    match fmtstr.style {
 +        StrStyle::Cooked => unescape::unescape_literal(contents, unescape::Mode::Str, &mut cb),
 +        StrStyle::Raw(_) => unescape::unescape_literal(contents, unescape::Mode::RawStr, &mut cb),
 +    }
 +
 +    should_lint
 +}
index a688050f63a6ad659603b248aa78ca785edcf093,0000000000000000000000000000000000000000..c36bca06507d67b8544dd51850235166b9cf0015
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,19 @@@
 +[package]
 +name = "clippy_utils"
 +version = "0.1.65"
 +edition = "2021"
 +publish = false
 +
 +[dependencies]
 +arrayvec = { version = "0.7", default-features = false }
 +if_chain = "1.0"
++itertools = "0.10.1"
 +rustc-semver = "1.1"
 +
 +[features]
 +deny-warnings = []
 +internal = []
 +
 +[package.metadata.rust-analyzer]
 +# This crate uses #[feature(rustc_private)]
 +rustc_private = true
index 313f1f1d9a6f1dd0d64c9d7bc287ae13b970ef1d,0000000000000000000000000000000000000000..997e773b5da4eecc3cf2ea61ae2dc0a1f131c552
mode 100644,000000..100644
--- /dev/null
@@@ -1,2306 -1,0 +1,2325 @@@
- /// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
 +#![feature(array_chunks)]
 +#![feature(box_patterns)]
 +#![feature(control_flow_enum)]
 +#![feature(let_else)]
 +#![feature(let_chains)]
 +#![feature(lint_reasons)]
 +#![feature(once_cell)]
 +#![feature(rustc_private)]
 +#![recursion_limit = "512"]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
 +// warn on the same lints as `clippy_lints`
 +#![warn(trivial_casts, trivial_numeric_casts)]
 +// warn on lints, that are included in `rust-lang/rust`s bootstrap
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +// warn on rustc internal lints
 +#![warn(rustc::internal)]
 +
 +// FIXME: switch to something more ergonomic here, once available.
 +// (Currently there is no way to opt into sysroot crates without `extern crate`.)
 +extern crate rustc_ast;
 +extern crate rustc_ast_pretty;
 +extern crate rustc_attr;
 +extern crate rustc_data_structures;
 +extern crate rustc_errors;
 +extern crate rustc_hir;
 +extern crate rustc_infer;
 +extern crate rustc_lexer;
 +extern crate rustc_lint;
 +extern crate rustc_middle;
++extern crate rustc_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]
 +pub mod sym_helper;
 +
 +pub mod ast_utils;
 +pub mod attrs;
 +mod check_proc_macro;
 +pub mod comparisons;
 +pub mod consts;
 +pub mod diagnostics;
 +pub mod eager_or_lazy;
 +pub mod higher;
 +mod hir_utils;
 +pub mod macros;
 +pub mod msrvs;
 +pub mod numeric_literal;
 +pub mod paths;
 +pub mod ptr;
 +pub mod qualify_min_const_fn;
 +pub mod source;
 +pub mod str_utils;
 +pub mod sugg;
 +pub mod ty;
 +pub mod usage;
 +pub mod visitors;
 +
 +pub use self::attrs::*;
 +pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 +pub use self::hir_utils::{
 +    both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
 +};
 +
 +use std::collections::hash_map::Entry;
 +use std::hash::BuildHasherDefault;
 +use std::sync::OnceLock;
 +use std::sync::{Mutex, MutexGuard};
 +
 +use if_chain::if_chain;
 +use rustc_ast::ast::{self, LitKind};
 +use rustc_ast::Attribute;
 +use rustc_data_structures::fx::FxHashMap;
 +use rustc_data_structures::unhash::UnhashMap;
 +use rustc_hir as hir;
 +use rustc_hir::def::{DefKind, Res};
 +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, CRATE_DEF_ID};
 +use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 +use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 +use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 +use rustc_hir::{
 +    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
 +    ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
 +    Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
 +    TraitRef, TyKind, UnOp,
 +};
++use rustc_lexer::{tokenize, TokenKind};
 +use rustc_lint::{LateContext, Level, Lint, LintContext};
 +use rustc_middle::hir::place::PlaceBase;
 +use rustc_middle::ty as rustc_ty;
 +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 +use rustc_middle::ty::binding::BindingMode;
 +use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
 +    ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
 +    PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
 +};
 +use rustc_middle::ty::{
 +    layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
 +};
 +use rustc_middle::ty::{FloatTy, IntTy, UintTy};
 +use rustc_semver::RustcVersion;
 +use rustc_session::Session;
 +use rustc_span::hygiene::{ExpnKind, MacroKind};
 +use rustc_span::source_map::original_sp;
++use rustc_span::source_map::SourceMap;
 +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, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
 +    if let Ok(version) = RustcVersion::parse(msrv) {
 +        return Some(version);
 +    } else if let Some(sess) = sess {
 +        if let Some(span) = span {
 +            sess.span_err(span, &format!("`{}` is not a valid Rust version", msrv));
 +        }
 +    }
 +    None
 +}
 +
 +pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
 +    msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
 +}
 +
 +#[macro_export]
 +macro_rules! extract_msrv_attr {
 +    ($context:ident) => {
 +        fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
 +            let sess = rustc_lint::LintContext::sess(cx);
 +            match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
 +                Some(msrv_attr) => {
 +                    if let Some(msrv) = msrv_attr.value_str() {
 +                        self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
 +                    } else {
 +                        sess.span_err(msrv_attr.span, "bad clippy attribute");
 +                    }
 +                },
 +                _ => (),
 +            }
 +        }
 +    };
 +}
 +
 +/// If the given expression is a local binding, find the initializer expression.
 +/// If that initializer expression is another local binding, find its initializer again.
 +/// This process repeats as long as possible (but usually no more than once). Initializer
 +/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 +/// instead.
 +///
 +/// Examples:
 +/// ```
 +/// let abc = 1;
 +/// //        ^ output
 +/// let def = abc;
 +/// dbg!(def);
 +/// //   ^^^ input
 +///
 +/// // or...
 +/// let abc = 1;
 +/// let def = abc + 2;
 +/// //        ^^^^^^^ output
 +/// dbg!(def);
 +/// //   ^^^ input
 +/// ```
 +pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
 +    while let Some(init) = path_to_local(expr)
 +        .and_then(|id| find_binding_init(cx, id))
 +        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
 +    {
 +        expr = init;
 +    }
 +    expr
 +}
 +
 +/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
 +/// By only considering immutable bindings, we guarantee that the returned expression represents the
 +/// value of the binding wherever it is referenced.
 +///
 +/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
 +/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
 +/// canonical binding `HirId`.
 +pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
 +    let hir = cx.tcx.hir();
 +    if_chain! {
 +        if let Some(Node::Pat(pat)) = hir.find(hir_id);
 +        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
 +        let parent = hir.get_parent_node(hir_id);
 +        if let Some(Node::Local(local)) = hir.find(parent);
 +        then {
 +            return local.init;
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns `true` if the given `NodeId` is inside a constant context
 +///
 +/// # Example
 +///
 +/// ```rust,ignore
 +/// if in_constant(cx, expr.hir_id) {
 +///     // Do something
 +/// }
 +/// ```
 +pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
 +    let parent_id = cx.tcx.hir().get_parent_item(id);
 +    match cx.tcx.hir().get_by_def_id(parent_id) {
 +        Node::Item(&Item {
 +            kind: ItemKind::Const(..) | ItemKind::Static(..),
 +            ..
 +        })
 +        | Node::TraitItem(&TraitItem {
 +            kind: TraitItemKind::Const(..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Const(..),
 +            ..
 +        })
 +        | Node::AnonConst(_) => true,
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(ref sig, ..),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(ref sig, _),
 +            ..
 +        }) => sig.header.constness == Constness::Const,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if a `QPath` resolves to a constructor of a `LangItem`.
 +/// For example, use this to check whether a function call or a pattern is `Some(..)`.
 +pub fn is_lang_ctor(cx: &LateContext<'_>, qpath: &QPath<'_>, lang_item: LangItem) -> bool {
 +    if let QPath::Resolved(_, path) = qpath {
 +        if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
 +            if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
 +                return cx.tcx.parent(ctor_id) == item_id;
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
 +    matches!(
 +        expr.kind,
 +        ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr: None,
 +                ..
 +            },
 +            _
 +        ) | ExprKind::Tup([])
 +    )
 +}
 +
 +/// Checks if given pattern is a wildcard (`_`)
 +pub fn is_wild(pat: &Pat<'_>) -> bool {
 +    matches!(pat.kind, PatKind::Wild)
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +/// This is a deprecated function, consider using [`is_trait_method`].
 +pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
 +    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
 +    let trt_id = cx.tcx.trait_of_item(def_id);
 +    trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 +}
 +
 +/// Checks if a method is defined in an impl of a diagnostic item
 +pub fn is_diag_item_method(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +        if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +            return cx.tcx.is_diagnostic_item(diag_item, adt.did());
 +        }
 +    }
 +    false
 +}
 +
 +/// Checks if a method is in a diagnostic item trait
 +pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
 +    if let Some(trait_did) = cx.tcx.trait_of_item(def_id) {
 +        return cx.tcx.is_diagnostic_item(diag_item, trait_did);
 +    }
 +    false
 +}
 +
 +/// Checks if the method call given in `expr` belongs to the given trait.
 +pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    cx.typeck_results()
 +        .type_dependent_def_id(expr.hir_id)
 +        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
 +}
 +
 +/// Checks if the given expression is a path referring an item on the trait
 +/// that is marked with the given diagnostic item.
 +///
 +/// For checking method call expressions instead of path expressions, use
 +/// [`is_trait_method`].
 +///
 +/// For example, this can be used to find if an expression like `u64::default`
 +/// refers to an item of the trait `Default`, which is associated with the
 +/// `diag_item` of `sym::Default`.
 +pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
 +    if let hir::ExprKind::Path(ref qpath) = expr.kind {
 +        cx.qpath_res(qpath, expr.hir_id)
 +            .opt_def_id()
 +            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
 +    match *path {
 +        QPath::Resolved(_, path) => path.segments.last().expect("A path must have at least one segment"),
 +        QPath::TypeRelative(_, seg) => seg,
 +        QPath::LangItem(..) => panic!("last_path_segment: lang item has no path segments"),
 +    }
 +}
 +
 +pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
 +    last_path_segment(qpath)
 +        .args
 +        .map_or(&[][..], |a| a.args)
 +        .iter()
 +        .filter_map(|a| match a {
 +            hir::GenericArg::Type(ty) => Some(ty),
 +            _ => None,
 +        })
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `QPath` against a slice of segment string literals.
 +///
 +/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 +/// `rustc_hir::QPath`.
 +///
 +/// # Examples
 +/// ```rust,ignore
 +/// match_qpath(path, &["std", "rt", "begin_unwind"])
 +/// ```
 +pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 +    match *path {
 +        QPath::Resolved(_, path) => match_path(path, segments),
 +        QPath::TypeRelative(ty, segment) => match ty.kind {
 +            TyKind::Path(ref inner_path) => {
 +                if let [prefix @ .., end] = segments {
 +                    if match_qpath(inner_path, prefix) {
 +                        return segment.ident.name.as_str() == *end;
 +                    }
 +                }
 +                false
 +            },
 +            _ => false,
 +        },
 +        QPath::LangItem(..) => false,
 +    }
 +}
 +
 +/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 +///
- /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
- /// diagnostic item.
- pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
-     path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
++/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
 +pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
 +    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 +}
 +
++/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
++/// it matches the given diagnostic item.
++pub fn is_path_diagnostic_item<'tcx>(
++    cx: &LateContext<'_>,
++    maybe_path: &impl MaybePath<'tcx>,
++    diag_item: Symbol,
++) -> bool {
++    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 +}
 +
 +/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
 +/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
 +/// `QPath::Resolved.1.res.opt_def_id()`.
 +///
 +/// Matches a `Path` against a slice of segment string literals.
 +///
 +/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 +/// `rustc_hir::Path`.
 +///
 +/// # Examples
 +///
 +/// ```rust,ignore
 +/// if match_path(&trait_ref.path, &paths::HASH) {
 +///     // This is the `std::hash::Hash` trait.
 +/// }
 +///
 +/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
 +///     // This is a `rustc_middle::lint::Lint`.
 +/// }
 +/// ```
 +pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
 +    path.segments
 +        .iter()
 +        .rev()
 +        .zip(segments.iter().rev())
 +        .all(|(a, b)| a.ident.name.as_str() == *b)
 +}
 +
 +/// If the expression is a path to a local, returns the canonical `HirId` of the local.
 +pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
 +    if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind {
 +        if let Res::Local(id) = path.res {
 +            return Some(id);
 +        }
 +    }
 +    None
 +}
 +
 +/// Returns true if the expression is a path to a local with the specified `HirId`.
 +/// Use this function to see if an expression matches a function argument or a match binding.
 +pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
 +    path_to_local(expr) == Some(id)
 +}
 +
 +pub trait MaybePath<'hir> {
 +    fn hir_id(&self) -> HirId;
 +    fn qpath_opt(&self) -> Option<&QPath<'hir>>;
 +}
 +
 +macro_rules! maybe_path {
 +    ($ty:ident, $kind:ident) => {
 +        impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn qpath_opt(&self) -> Option<&QPath<'hir>> {
 +                match &self.kind {
 +                    hir::$kind::Path(qpath) => Some(qpath),
 +                    _ => None,
 +                }
 +            }
 +        }
 +    };
 +}
 +maybe_path!(Expr, ExprKind);
 +maybe_path!(Pat, PatKind);
 +maybe_path!(Ty, TyKind);
 +
 +/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
 +pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
 +    match maybe_path.qpath_opt() {
 +        None => Res::Err,
 +        Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
 +    }
 +}
 +
 +/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
 +pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
 +    path_res(cx, maybe_path).opt_def_id()
 +}
 +
 +/// Resolves a def path like `std::vec::Vec`.
 +/// This function is expensive and should be used sparingly.
 +pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
 +    fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str) -> Option<Res> {
 +        match tcx.def_kind(def_id) {
 +            DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
 +                .module_children(def_id)
 +                .iter()
 +                .find(|item| item.ident.name.as_str() == name)
 +                .map(|child| child.res.expect_non_local()),
 +            DefKind::Impl => tcx
 +                .associated_item_def_ids(def_id)
 +                .iter()
 +                .copied()
 +                .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
 +                .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
 +            _ => None,
 +        }
 +    }
 +    fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
 +        let single = |ty| tcx.incoherent_impls(ty).iter().copied();
 +        let empty = || [].iter().copied();
 +        match name {
 +            "bool" => single(BoolSimplifiedType),
 +            "char" => single(CharSimplifiedType),
 +            "str" => single(StrSimplifiedType),
 +            "array" => single(ArraySimplifiedType),
 +            "slice" => single(SliceSimplifiedType),
 +            // FIXME: rustdoc documents these two using just `pointer`.
 +            //
 +            // Maybe this is something we should do here too.
 +            "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
 +            "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
 +            "isize" => single(IntSimplifiedType(IntTy::Isize)),
 +            "i8" => single(IntSimplifiedType(IntTy::I8)),
 +            "i16" => single(IntSimplifiedType(IntTy::I16)),
 +            "i32" => single(IntSimplifiedType(IntTy::I32)),
 +            "i64" => single(IntSimplifiedType(IntTy::I64)),
 +            "i128" => single(IntSimplifiedType(IntTy::I128)),
 +            "usize" => single(UintSimplifiedType(UintTy::Usize)),
 +            "u8" => single(UintSimplifiedType(UintTy::U8)),
 +            "u16" => single(UintSimplifiedType(UintTy::U16)),
 +            "u32" => single(UintSimplifiedType(UintTy::U32)),
 +            "u64" => single(UintSimplifiedType(UintTy::U64)),
 +            "u128" => single(UintSimplifiedType(UintTy::U128)),
 +            "f32" => single(FloatSimplifiedType(FloatTy::F32)),
 +            "f64" => single(FloatSimplifiedType(FloatTy::F64)),
 +            _ => empty(),
 +        }
 +    }
 +    fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
 +        tcx.crates(())
 +            .iter()
 +            .copied()
 +            .find(|&num| tcx.crate_name(num).as_str() == name)
 +            .map(CrateNum::as_def_id)
 +    }
 +
 +    let (base, first, path) = match *path {
 +        [base, first, ref path @ ..] => (base, first, path),
 +        [primitive] => {
 +            return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
 +        },
 +        _ => return Res::Err,
 +    };
 +    let tcx = cx.tcx;
 +    let starts = find_primitive(tcx, base)
 +        .chain(find_crate(tcx, base))
 +        .filter_map(|id| item_child_by_name(tcx, id, first));
 +
 +    for first in starts {
 +        let last = path
 +            .iter()
 +            .copied()
 +            // for each segment, find the child item
 +            .try_fold(first, |res, segment| {
 +                let def_id = res.def_id();
 +                if let Some(item) = item_child_by_name(tcx, def_id, segment) {
 +                    Some(item)
 +                } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
 +                    // it is not a child item so check inherent impl items
 +                    tcx.inherent_impls(def_id)
 +                        .iter()
 +                        .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment))
 +                } else {
 +                    None
 +                }
 +            });
 +
 +        if let Some(last) = last {
 +            return last;
 +        }
 +    }
 +
 +    Res::Err
 +}
 +
 +/// Convenience function to get the `DefId` of a trait by path.
 +/// It could be a trait or trait alias.
 +pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
 +    match def_path_res(cx, path) {
 +        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
 +        _ => None,
 +    }
 +}
 +
 +/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 +///
 +/// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
 +///
 +/// ```rust
 +/// struct Point(isize, isize);
 +///
 +/// impl std::ops::Add for Point {
 +///     type Output = Self;
 +///
 +///     fn add(self, other: Self) -> Self {
 +///         Point(0, 0)
 +///     }
 +/// }
 +/// ```
 +pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, def_id: LocalDefId) -> Option<&'tcx TraitRef<'tcx>> {
 +    // Get the implemented trait for the current function
 +    let hir_id = cx.tcx.hir().local_def_id_to_hir_id(def_id);
 +    let parent_impl = cx.tcx.hir().get_parent_item(hir_id);
 +    if_chain! {
 +        if parent_impl != CRATE_DEF_ID;
 +        if let hir::Node::Item(item) = cx.tcx.hir().get_by_def_id(parent_impl);
 +        if let hir::ItemKind::Impl(impl_) = &item.kind;
 +        then {
 +            return impl_.of_trait.as_ref();
 +        }
 +    }
 +    None
 +}
 +
 +/// This method will return tuple of projection stack and root of the expression,
 +/// used in `can_mut_borrow_both`.
 +///
 +/// For example, if `e` represents the `v[0].a.b[x]`
 +/// this method will return a tuple, composed of a `Vec`
 +/// containing the `Expr`s for `v[0], v[0].a, v[0].a.b, v[0].a.b[x]`
 +/// and an `Expr` for root of them, `v`
 +fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'a Expr<'hir>) {
 +    let mut result = vec![];
 +    let root = loop {
 +        match e.kind {
 +            ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => {
 +                result.push(e);
 +                e = ep;
 +            },
 +            _ => break e,
 +        };
 +    };
 +    result.reverse();
 +    (result, root)
 +}
 +
 +/// Gets the mutability of the custom deref adjustment, if any.
 +pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<Mutability> {
 +    cx.typeck_results()
 +        .expr_adjustments(e)
 +        .iter()
 +        .find_map(|a| match a.kind {
 +            Adjust::Deref(Some(d)) => Some(Some(d.mutbl)),
 +            Adjust::Deref(None) => None,
 +            _ => Some(None),
 +        })
 +        .and_then(|x| x)
 +}
 +
 +/// Checks if two expressions can be mutably borrowed simultaneously
 +/// and they aren't dependent on borrowing same thing twice
 +pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -> bool {
 +    let (s1, r1) = projection_stack(e1);
 +    let (s2, r2) = projection_stack(e2);
 +    if !eq_expr_value(cx, r1, r2) {
 +        return true;
 +    }
 +    if expr_custom_deref_adjustment(cx, r1).is_some() || expr_custom_deref_adjustment(cx, r2).is_some() {
 +        return false;
 +    }
 +
 +    for (x1, x2) in s1.iter().zip(s2.iter()) {
 +        if expr_custom_deref_adjustment(cx, x1).is_some() || expr_custom_deref_adjustment(cx, x2).is_some() {
 +            return false;
 +        }
 +
 +        match (&x1.kind, &x2.kind) {
 +            (ExprKind::Field(_, i1), ExprKind::Field(_, i2)) => {
 +                if i1 != i2 {
 +                    return true;
 +                }
 +            },
 +            (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => {
 +                if !eq_expr_value(cx, i1, i2) {
 +                    return false;
 +                }
 +            },
 +            _ => return false,
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
 +/// constructor from the std library
 +fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
 +    let std_types_symbols = &[
 +        sym::String,
 +        sym::Vec,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::HashMap,
 +        sym::BTreeMap,
 +        sym::HashSet,
 +        sym::BTreeSet,
 +        sym::BinaryHeap,
 +    ];
 +
 +    if let QPath::TypeRelative(_, method) = path {
 +        if method.ident.name == sym::new {
 +            if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
 +                if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
 +                    return std_types_symbols
 +                        .iter()
 +                        .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
 +                }
 +            }
 +        }
 +    }
 +    false
 +}
 +
 +/// Return true if the expr is equal to `Default::default` when evaluated.
 +pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) -> bool {
 +    if_chain! {
 +        if let hir::ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
 +        if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
 +        if is_diag_trait_item(cx, repl_def_id, sym::Default)
 +            || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
 +        then { true } else { false }
 +    }
 +}
 +
 +/// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
 +/// It doesn't cover all cases, for example indirect function calls (some of std
 +/// functions are supported) but it is the best we have.
 +pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    match &e.kind {
 +        ExprKind::Lit(lit) => match lit.node {
 +            LitKind::Bool(false) | LitKind::Int(0, _) => true,
 +            LitKind::Str(s, _) => s.is_empty(),
 +            _ => false,
 +        },
 +        ExprKind::Tup(items) | ExprKind::Array(items) => items.iter().all(|x| is_default_equivalent(cx, x)),
 +        ExprKind::Repeat(x, ArrayLen::Body(len)) => if_chain! {
 +            if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind;
 +            if let LitKind::Int(v, _) = const_lit.node;
 +            if v <= 32 && is_default_equivalent(cx, x);
 +            then {
 +                true
 +            }
 +            else {
 +                false
 +            }
 +        },
 +        ExprKind::Call(repl_func, _) => is_default_equivalent_call(cx, repl_func),
 +        ExprKind::Path(qpath) => is_lang_ctor(cx, qpath, OptionNone),
 +        ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the top level expression can be moved into a closure as is.
 +/// Currently checks for:
 +/// * Break/Continue outside the given loop HIR ids.
 +/// * Yield/Return statements.
 +/// * Inline assembly.
 +/// * Usages of a field of a local where the type of the local can be partially moved.
 +///
 +/// For example, given the following function:
 +///
 +/// ```
 +/// fn f<'a>(iter: &mut impl Iterator<Item = (usize, &'a mut String)>) {
 +///     for item in iter {
 +///         let s = item.1;
 +///         if item.0 > 10 {
 +///             continue;
 +///         } else {
 +///             s.clear();
 +///         }
 +///     }
 +/// }
 +/// ```
 +///
 +/// When called on the expression `item.0` this will return false unless the local `item` is in the
 +/// `ignore_locals` set. The type `(usize, &mut String)` can have the second element moved, so it
 +/// isn't always safe to move into a closure when only a single field is needed.
 +///
 +/// When called on the `continue` expression this will return false unless the outer loop expression
 +/// is in the `loop_ids` set.
 +///
 +/// Note that this check is not recursive, so passing the `if` expression will always return true
 +/// even though sub-expressions might return false.
 +pub fn can_move_expr_to_closure_no_visit<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    loop_ids: &[HirId],
 +    ignore_locals: &HirIdSet,
 +) -> bool {
 +    match expr.kind {
 +        ExprKind::Break(Destination { target_id: Ok(id), .. }, _)
 +        | ExprKind::Continue(Destination { target_id: Ok(id), .. })
 +            if loop_ids.contains(&id) =>
 +        {
 +            true
 +        },
 +        ExprKind::Break(..)
 +        | ExprKind::Continue(_)
 +        | ExprKind::Ret(_)
 +        | ExprKind::Yield(..)
 +        | ExprKind::InlineAsm(_) => false,
 +        // Accessing a field of a local value can only be done if the type isn't
 +        // partially moved.
 +        ExprKind::Field(
 +            &Expr {
 +                hir_id,
 +                kind:
 +                    ExprKind::Path(QPath::Resolved(
 +                        _,
 +                        Path {
 +                            res: Res::Local(local_id),
 +                            ..
 +                        },
 +                    )),
 +                ..
 +            },
 +            _,
 +        ) if !ignore_locals.contains(local_id) && can_partially_move_ty(cx, cx.typeck_results().node_type(hir_id)) => {
 +            // TODO: check if the local has been partially moved. Assume it has for now.
 +            false
 +        },
 +        _ => true,
 +    }
 +}
 +
 +/// How a local is captured by a closure
 +#[derive(Debug, Clone, Copy, PartialEq, Eq)]
 +pub enum CaptureKind {
 +    Value,
 +    Ref(Mutability),
 +}
 +impl CaptureKind {
 +    pub fn is_imm_ref(self) -> bool {
 +        self == Self::Ref(Mutability::Not)
 +    }
 +}
 +impl std::ops::BitOr for CaptureKind {
 +    type Output = Self;
 +    fn bitor(self, rhs: Self) -> Self::Output {
 +        match (self, rhs) {
 +            (CaptureKind::Value, _) | (_, CaptureKind::Value) => CaptureKind::Value,
 +            (CaptureKind::Ref(Mutability::Mut), CaptureKind::Ref(_))
 +            | (CaptureKind::Ref(_), CaptureKind::Ref(Mutability::Mut)) => CaptureKind::Ref(Mutability::Mut),
 +            (CaptureKind::Ref(Mutability::Not), CaptureKind::Ref(Mutability::Not)) => CaptureKind::Ref(Mutability::Not),
 +        }
 +    }
 +}
 +impl std::ops::BitOrAssign for CaptureKind {
 +    fn bitor_assign(&mut self, rhs: Self) {
 +        *self = *self | rhs;
 +    }
 +}
 +
 +/// Given an expression referencing a local, determines how it would be captured in a closure.
 +/// Note as this will walk up to parent expressions until the capture can be determined it should
 +/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 +/// function argument (other than a receiver).
 +pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
 +    fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
 +        let mut capture = CaptureKind::Ref(Mutability::Not);
 +        pat.each_binding_or_first(&mut |_, id, span, _| match cx
 +            .typeck_results()
 +            .extract_binding_mode(cx.sess(), id, span)
 +            .unwrap()
 +        {
 +            BindingMode::BindByValue(_) if !is_copy(cx, cx.typeck_results().node_type(id)) => {
 +                capture = CaptureKind::Value;
 +            },
 +            BindingMode::BindByReference(Mutability::Mut) if capture != CaptureKind::Value => {
 +                capture = CaptureKind::Ref(Mutability::Mut);
 +            },
 +            _ => (),
 +        });
 +        capture
 +    }
 +
 +    debug_assert!(matches!(
 +        e.kind,
 +        ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
 +    ));
 +
 +    let mut child_id = e.hir_id;
 +    let mut capture = CaptureKind::Value;
 +    let mut capture_expr_ty = e;
 +
 +    for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) {
 +        if let [
 +            Adjustment {
 +                kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
 +                target,
 +            },
 +            ref adjust @ ..,
 +        ] = *cx
 +            .typeck_results()
 +            .adjustments()
 +            .get(child_id)
 +            .map_or(&[][..], |x| &**x)
 +        {
 +            if let rustc_ty::RawPtr(TypeAndMut { mutbl: mutability, .. }) | rustc_ty::Ref(_, _, mutability) =
 +                *adjust.last().map_or(target, |a| a.target).kind()
 +            {
 +                return CaptureKind::Ref(mutability);
 +            }
 +        }
 +
 +        match parent {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
 +                ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
 +                ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
 +                    return CaptureKind::Ref(Mutability::Mut);
 +                },
 +                ExprKind::Field(..) => {
 +                    if capture == CaptureKind::Value {
 +                        capture_expr_ty = e;
 +                    }
 +                },
 +                ExprKind::Let(let_expr) => {
 +                    let mutability = match pat_capture_kind(cx, let_expr.pat) {
 +                        CaptureKind::Value => Mutability::Not,
 +                        CaptureKind::Ref(m) => m,
 +                    };
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                ExprKind::Match(_, arms, _) => {
 +                    let mut mutability = Mutability::Not;
 +                    for capture in arms.iter().map(|arm| pat_capture_kind(cx, arm.pat)) {
 +                        match capture {
 +                            CaptureKind::Value => break,
 +                            CaptureKind::Ref(Mutability::Mut) => mutability = Mutability::Mut,
 +                            CaptureKind::Ref(Mutability::Not) => (),
 +                        }
 +                    }
 +                    return CaptureKind::Ref(mutability);
 +                },
 +                _ => break,
 +            },
 +            Node::Local(l) => match pat_capture_kind(cx, l.pat) {
 +                CaptureKind::Value => break,
 +                capture @ CaptureKind::Ref(_) => return capture,
 +            },
 +            _ => break,
 +        }
 +
 +        child_id = parent_id;
 +    }
 +
 +    if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
 +        // Copy types are never automatically captured by value.
 +        CaptureKind::Ref(Mutability::Not)
 +    } else {
 +        capture
 +    }
 +}
 +
 +/// Checks if the expression can be moved into a closure as is. This will return a list of captures
 +/// if so, otherwise, `None`.
 +pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<HirIdMap<CaptureKind>> {
 +    struct V<'cx, 'tcx> {
 +        cx: &'cx LateContext<'tcx>,
 +        // Stack of potential break targets contained in the expression.
 +        loops: Vec<HirId>,
 +        /// Local variables created in the expression. These don't need to be captured.
 +        locals: HirIdSet,
 +        /// Whether this expression can be turned into a closure.
 +        allow_closure: bool,
 +        /// Locals which need to be captured, and whether they need to be by value, reference, or
 +        /// mutable reference.
 +        captures: HirIdMap<CaptureKind>,
 +    }
 +    impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
 +        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
 +            if !self.allow_closure {
 +                return;
 +            }
 +
 +            match e.kind {
 +                ExprKind::Path(QPath::Resolved(None, &Path { res: Res::Local(l), .. })) => {
 +                    if !self.locals.contains(&l) {
 +                        let cap = capture_local_usage(self.cx, e);
 +                        self.captures.entry(l).and_modify(|e| *e |= cap).or_insert(cap);
 +                    }
 +                },
 +                ExprKind::Closure { .. } => {
 +                    let closure_id = self.cx.tcx.hir().local_def_id(e.hir_id);
 +                    for capture in self.cx.typeck_results().closure_min_captures_flattened(closure_id) {
 +                        let local_id = match capture.place.base {
 +                            PlaceBase::Local(id) => id,
 +                            PlaceBase::Upvar(var) => var.var_path.hir_id,
 +                            _ => continue,
 +                        };
 +                        if !self.locals.contains(&local_id) {
 +                            let capture = match capture.info.capture_kind {
 +                                UpvarCapture::ByValue => CaptureKind::Value,
 +                                UpvarCapture::ByRef(kind) => match kind {
 +                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
 +                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
 +                                        CaptureKind::Ref(Mutability::Mut)
 +                                    },
 +                                },
 +                            };
 +                            self.captures
 +                                .entry(local_id)
 +                                .and_modify(|e| *e |= capture)
 +                                .or_insert(capture);
 +                        }
 +                    }
 +                },
 +                ExprKind::Loop(b, ..) => {
 +                    self.loops.push(e.hir_id);
 +                    self.visit_block(b);
 +                    self.loops.pop();
 +                },
 +                _ => {
 +                    self.allow_closure &= can_move_expr_to_closure_no_visit(self.cx, e, &self.loops, &self.locals);
 +                    walk_expr(self, e);
 +                },
 +            }
 +        }
 +
 +        fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
 +            p.each_binding_or_first(&mut |_, id, _, _| {
 +                self.locals.insert(id);
 +            });
 +        }
 +    }
 +
 +    let mut v = V {
 +        cx,
 +        allow_closure: true,
 +        loops: Vec::new(),
 +        locals: HirIdSet::default(),
 +        captures: HirIdMap::default(),
 +    };
 +    v.visit_expr(expr);
 +    v.allow_closure.then_some(v.captures)
 +}
 +
 +/// Returns the method names and argument list of nested method call expressions that make up
 +/// `expr`. method/span lists are sorted with the most recent call first.
 +pub fn method_calls<'tcx>(
 +    expr: &'tcx Expr<'tcx>,
 +    max_depth: usize,
 +) -> (Vec<Symbol>, Vec<&'tcx [Expr<'tcx>]>, Vec<Span>) {
 +    let mut method_names = Vec::with_capacity(max_depth);
 +    let mut arg_lists = Vec::with_capacity(max_depth);
 +    let mut spans = Vec::with_capacity(max_depth);
 +
 +    let mut current = expr;
 +    for _ in 0..max_depth {
 +        if let ExprKind::MethodCall(path, args, _) = &current.kind {
 +            if args.iter().any(|e| e.span.from_expansion()) {
 +                break;
 +            }
 +            method_names.push(path.ident.name);
 +            arg_lists.push(&**args);
 +            spans.push(path.ident.span);
 +            current = &args[0];
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    (method_names, arg_lists, spans)
 +}
 +
 +/// Matches an `Expr` against a chain of methods, and return the matched `Expr`s.
 +///
 +/// For example, if `expr` represents the `.baz()` in `foo.bar().baz()`,
 +/// `method_chain_args(expr, &["bar", "baz"])` will return a `Vec`
 +/// containing the `Expr`s for
 +/// `.bar()` and `.baz()`
 +pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec<&'a [Expr<'a>]>> {
 +    let mut current = expr;
 +    let mut matched = Vec::with_capacity(methods.len());
 +    for method_name in methods.iter().rev() {
 +        // method chains are stored last -> first
 +        if let ExprKind::MethodCall(path, args, _) = current.kind {
 +            if path.ident.name.as_str() == *method_name {
 +                if args.iter().any(|e| e.span.from_expansion()) {
 +                    return None;
 +                }
 +                matched.push(args); // build up `matched` backwards
 +                current = &args[0]; // go to parent expression
 +            } else {
 +                return None;
 +            }
 +        } else {
 +            return None;
 +        }
 +    }
 +    // Reverse `matched` so that it is in the same order as `methods`.
 +    matched.reverse();
 +    Some(matched)
 +}
 +
 +/// Returns `true` if the provided `def_id` is an entrypoint to a program.
 +pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    cx.tcx
 +        .entry_fn(())
 +        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 +}
 +
 +/// Returns `true` if the expression is in the program's `#[panic_handler]`.
 +pub fn is_in_panic_handler(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    let parent = cx.tcx.hir().get_parent_item(e.hir_id);
 +    Some(parent.to_def_id()) == cx.tcx.lang_items().panic_impl()
 +}
 +
 +/// Gets the name of the item the expression is in, if available.
 +pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 +    let parent_id = cx.tcx.hir().get_parent_item(expr.hir_id);
 +    match cx.tcx.hir().find_by_def_id(parent_id) {
 +        Some(
 +            Node::Item(Item { ident, .. })
 +            | Node::TraitItem(TraitItem { ident, .. })
 +            | Node::ImplItem(ImplItem { ident, .. }),
 +        ) => Some(ident.name),
 +        _ => None,
 +    }
 +}
 +
 +pub struct ContainsName {
 +    pub name: Symbol,
 +    pub result: bool,
 +}
 +
 +impl<'tcx> Visitor<'tcx> for ContainsName {
 +    fn visit_name(&mut self, _: Span, name: Symbol) {
 +        if self.name == name {
 +            self.result = true;
 +        }
 +    }
 +}
 +
 +/// Checks if an `Expr` contains a certain name.
 +pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
 +    let mut cn = ContainsName { name, result: false };
 +    cn.visit_expr(expr);
 +    cn.result
 +}
 +
 +/// Returns `true` if `expr` contains a return expression
 +pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
 +    let mut found = false;
 +    expr_visitor_no_bodies(|expr| {
 +        if !found {
 +            if let hir::ExprKind::Ret(..) = &expr.kind {
 +                found = true;
 +            }
 +        }
 +        !found
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +/// Extends the span to the beginning of the spans line, incl. whitespaces.
 +///
 +/// ```rust
 +///        let x = ();
 +/// //             ^^
 +/// // will be converted to
 +///        let x = ();
 +/// // ^^^^^^^^^^^^^^
 +/// ```
 +fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 +    let span = original_sp(span, DUMMY_SP);
 +    let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
 +    let line_no = source_map_and_line.line;
 +    let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
 +    span.with_lo(line_start)
 +}
 +
 +/// Gets the parent node, if any.
 +pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
 +    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 +}
 +
 +/// Gets the parent expression, if any –- this is useful to constrain a lint.
 +pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    get_parent_expr_for_hir(cx, e.hir_id)
 +}
 +
 +/// This retrieves the parent for the given `HirId` if it's an expression. This is useful for
 +/// constraint lints
 +pub fn get_parent_expr_for_hir<'tcx>(cx: &LateContext<'tcx>, hir_id: hir::HirId) -> Option<&'tcx Expr<'tcx>> {
 +    match get_parent_node(cx.tcx, hir_id) {
 +        Some(Node::Expr(parent)) => Some(parent),
 +        _ => None,
 +    }
 +}
 +
 +pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
 +    let map = &cx.tcx.hir();
 +    let enclosing_node = map
 +        .get_enclosing_scope(hir_id)
 +        .and_then(|enclosing_id| map.find(enclosing_id));
 +    enclosing_node.and_then(|node| match node {
 +        Node::Block(block) => Some(block),
 +        Node::Item(&Item {
 +            kind: ItemKind::Fn(_, _, eid),
 +            ..
 +        })
 +        | Node::ImplItem(&ImplItem {
 +            kind: ImplItemKind::Fn(_, eid),
 +            ..
 +        }) => match cx.tcx.hir().body(eid).value.kind {
 +            ExprKind::Block(block, _) => Some(block),
 +            _ => None,
 +        },
 +        _ => None,
 +    })
 +}
 +
 +/// Gets the loop or closure enclosing the given expression, if any.
 +pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &Expr<'_>,
 +) -> Option<&'tcx Expr<'tcx>> {
 +    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
 +        match node {
 +            Node::Expr(e) => match e.kind {
 +                ExprKind::Closure { .. } => {
 +                    if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
 +                        && subs.as_closure().kind() == ClosureKind::FnOnce
 +                    {
 +                        continue;
 +                    }
 +                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
 +                        let Node::Expr(e) = node else {
 +                            return None;
 +                        };
 +                        match e.kind {
 +                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
 +                            ExprKind::Call(f, args) => {
 +                                let i = args.iter().position(|arg| arg.hir_id == id)?;
 +                                let sig = expr_sig(cx, f)?;
 +                                let predicates = sig
 +                                    .predicates_id()
 +                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
 +                                    .caller_bounds();
 +                                sig.input(i).and_then(|ty| {
 +                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
 +                                })
 +                            },
 +                            ExprKind::MethodCall(_, args, _) => {
 +                                let i = args.iter().position(|arg| arg.hir_id == id)?;
 +                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
 +                                let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
 +                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
 +                            },
 +                            _ => None,
 +                        }
 +                    })
 +                    .is_some();
 +                    if !is_once {
 +                        return Some(e);
 +                    }
 +                },
 +                ExprKind::Loop(..) => return Some(e),
 +                _ => (),
 +            },
 +            Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
 +            _ => break,
 +        }
 +    }
 +    None
 +}
 +
 +/// Gets the parent node if it's an impl block.
 +pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
 +    match tcx.hir().parent_iter(id).next() {
 +        Some((
 +            _,
 +            Node::Item(Item {
 +                kind: ItemKind::Impl(imp),
 +                ..
 +            }),
 +        )) => Some(imp),
 +        _ => None,
 +    }
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// and no statements. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{{ x }}`          -> `x`
 +///  * `{ x; }`           -> `{ x; }`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Removes blocks around an expression, only if the block contains just one expression
 +/// or just one expression statement with a semicolon. Unsafe blocks are not removed.
 +///
 +/// Examples:
 +///  * `{}`               -> `{}`
 +///  * `{ x }`            -> `x`
 +///  * `{ x; }`           -> `x`
 +///  * `{{ x; }}`         -> `x`
 +///  * `{ x; y }`         -> `{ x; y }`
 +///  * `{ unsafe { x } }` -> `unsafe { x }`
 +pub fn peel_blocks_with_stmt<'a>(mut expr: &'a Expr<'a>) -> &'a Expr<'a> {
 +    while let ExprKind::Block(
 +        Block {
 +            stmts: [],
 +            expr: Some(inner),
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        }
 +        | Block {
 +            stmts:
 +                [
 +                    Stmt {
 +                        kind: StmtKind::Expr(inner) | StmtKind::Semi(inner),
 +                        ..
 +                    },
 +                ],
 +            expr: None,
 +            rules: BlockCheckMode::DefaultBlock,
 +            ..
 +        },
 +        _,
 +    ) = expr.kind
 +    {
 +        expr = inner;
 +    }
 +    expr
 +}
 +
 +/// Checks if the given expression is the else clause of either an `if` or `if let` expression.
 +pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    let mut iter = tcx.hir().parent_iter(expr.hir_id);
 +    match iter.next() {
 +        Some((
 +            _,
 +            Node::Expr(Expr {
 +                kind: ExprKind::If(_, _, Some(else_expr)),
 +                ..
 +            }),
 +        )) => else_expr.hir_id == expr.hir_id,
 +        _ => false,
 +    }
 +}
 +
 +/// Checks whether the given expression is a constant integer of the given value.
 +/// unlike `is_integer_literal`, this version does const folding
 +pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool {
 +    if is_integer_literal(e, value) {
 +        return true;
 +    }
 +    let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
 +    if let Some((Constant::Int(v), _)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
 +        return value == v;
 +    }
 +    false
 +}
 +
 +/// Checks whether the given expression is a constant literal of the given value.
 +pub fn is_integer_literal(expr: &Expr<'_>, value: u128) -> bool {
 +    // FIXME: use constant folding
 +    if let ExprKind::Lit(ref spanned) = expr.kind {
 +        if let LitKind::Int(v, _) = spanned.node {
 +            return v == value;
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if the given `Expr` has been coerced before.
 +///
 +/// Examples of coercions can be found in the Nomicon at
 +/// <https://doc.rust-lang.org/nomicon/coercions.html>.
 +///
 +/// See `rustc_middle::ty::adjustment::Adjustment` and `rustc_typeck::check::coercion` for more
 +/// information on adjustments and coercions.
 +pub fn is_adjusted(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
 +    cx.typeck_results().adjustments().get(e.hir_id).is_some()
 +}
 +
 +/// Returns the pre-expansion span if this comes from an expansion of the
 +/// macro `name`.
 +/// See also [`is_direct_expn_of`].
 +#[must_use]
 +pub fn is_expn_of(mut span: Span, name: &str) -> Option<Span> {
 +    loop {
 +        if span.from_expansion() {
 +            let data = span.ctxt().outer_expn_data();
 +            let new_span = data.call_site;
 +
 +            if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +                if mac_name.as_str() == name {
 +                    return Some(new_span);
 +                }
 +            }
 +
 +            span = new_span;
 +        } else {
 +            return None;
 +        }
 +    }
 +}
 +
 +/// Returns the pre-expansion span if the span directly comes from an expansion
 +/// of the macro `name`.
 +/// The difference with [`is_expn_of`] is that in
 +/// ```rust
 +/// # macro_rules! foo { ($name:tt!$args:tt) => { $name!$args } }
 +/// # macro_rules! bar { ($e:expr) => { $e } }
 +/// foo!(bar!(42));
 +/// ```
 +/// `42` is considered expanded from `foo!` and `bar!` by `is_expn_of` but only
 +/// from `bar!` by `is_direct_expn_of`.
 +#[must_use]
 +pub fn is_direct_expn_of(span: Span, name: &str) -> Option<Span> {
 +    if span.from_expansion() {
 +        let data = span.ctxt().outer_expn_data();
 +        let new_span = data.call_site;
 +
 +        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = data.kind {
 +            if mac_name.as_str() == name {
 +                return Some(new_span);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Convenience function to get the return type of a function.
 +pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let ret_ty = cx.tcx.fn_sig(fn_def_id).output();
 +    cx.tcx.erase_late_bound_regions(ret_ty)
 +}
 +
 +/// Convenience function to get the nth argument type of a function.
 +pub fn nth_arg<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId, nth: usize) -> Ty<'tcx> {
 +    let fn_def_id = cx.tcx.hir().local_def_id(fn_item);
 +    let arg = cx.tcx.fn_sig(fn_def_id).input(nth);
 +    cx.tcx.erase_late_bound_regions(arg)
 +}
 +
 +/// Checks if an expression is constructing a tuple-like enum variant or struct
 +pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    if let ExprKind::Call(fun, _) = expr.kind {
 +        if let ExprKind::Path(ref qp) = fun.kind {
 +            let res = cx.qpath_res(qp, fun.hir_id);
 +            return match res {
 +                def::Res::Def(DefKind::Variant | DefKind::Ctor(..), ..) => true,
 +                def::Res::Def(_, def_id) => cx.tcx.is_promotable_const_fn(def_id),
 +                _ => false,
 +            };
 +        }
 +    }
 +    false
 +}
 +
 +/// Returns `true` if a pattern is refutable.
 +// TODO: should be implemented using rustc/mir_build/thir machinery
 +pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 +    fn is_enum_variant(cx: &LateContext<'_>, qpath: &QPath<'_>, id: HirId) -> bool {
 +        matches!(
 +            cx.qpath_res(qpath, id),
 +            def::Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(def::CtorOf::Variant, _), _)
 +        )
 +    }
 +
 +    fn are_refutable<'a, I: IntoIterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, i: I) -> bool {
 +        i.into_iter().any(|pat| is_refutable(cx, pat))
 +    }
 +
 +    match pat.kind {
 +        PatKind::Wild => false,
 +        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
 +        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
 +        PatKind::Lit(..) | PatKind::Range(..) => true,
 +        PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
 +        PatKind::Or(pats) => {
 +            // TODO: should be the honest check, that pats is exhaustive set
 +            are_refutable(cx, pats)
 +        },
 +        PatKind::Tuple(pats, _) => are_refutable(cx, pats),
 +        PatKind::Struct(ref qpath, fields, _) => {
 +            is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, fields.iter().map(|field| field.pat))
 +        },
 +        PatKind::TupleStruct(ref qpath, pats, _) => is_enum_variant(cx, qpath, pat.hir_id) || are_refutable(cx, pats),
 +        PatKind::Slice(head, middle, tail) => {
 +            match &cx.typeck_results().node_type(pat.hir_id).kind() {
 +                rustc_ty::Slice(..) => {
 +                    // [..] is the only irrefutable slice pattern.
 +                    !head.is_empty() || middle.is_none() || !tail.is_empty()
 +                },
 +                rustc_ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter())),
 +                _ => {
 +                    // unreachable!()
 +                    true
 +                },
 +            }
 +        },
 +    }
 +}
 +
 +/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
 +/// the function once on the given pattern.
 +pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
 +    if let PatKind::Or(pats) = pat.kind {
 +        pats.iter().for_each(f);
 +    } else {
 +        f(pat);
 +    }
 +}
 +
 +pub fn is_self(slf: &Param<'_>) -> bool {
 +    if let PatKind::Binding(.., name, _) = slf.pat.kind {
 +        name.name == kw::SelfLower
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn is_self_ty(slf: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(None, path)) = slf.kind {
 +        if let Res::SelfTy { .. } = path.res {
 +            return true;
 +        }
 +    }
 +    false
 +}
 +
 +pub fn iter_input_pats<'tcx>(decl: &FnDecl<'_>, body: &'tcx Body<'_>) -> impl Iterator<Item = &'tcx Param<'tcx>> {
 +    (0..decl.inputs.len()).map(move |i| &body.params[i])
 +}
 +
 +/// Checks if a given expression is a match expression expanded from the `?`
 +/// operator or the `try` macro.
 +pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
 +    fn is_ok(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if_chain! {
 +            if let PatKind::TupleStruct(ref path, pat, None) = arm.pat.kind;
 +            if is_lang_ctor(cx, path, ResultOk);
 +            if let PatKind::Binding(_, hir_id, _, None) = pat[0].kind;
 +            if path_to_local_id(arm.body, hir_id);
 +            then {
 +                return true;
 +            }
 +        }
 +        false
 +    }
 +
 +    fn is_err(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 +        if let PatKind::TupleStruct(ref path, _, _) = arm.pat.kind {
 +            is_lang_ctor(cx, path, ResultErr)
 +        } else {
 +            false
 +        }
 +    }
 +
 +    if let ExprKind::Match(_, arms, ref source) = expr.kind {
 +        // desugared from a `?` operator
 +        if *source == MatchSource::TryDesugar {
 +            return Some(expr);
 +        }
 +
 +        if_chain! {
 +            if arms.len() == 2;
 +            if arms[0].guard.is_none();
 +            if arms[1].guard.is_none();
 +            if (is_ok(cx, &arms[0]) && is_err(cx, &arms[1])) || (is_ok(cx, &arms[1]) && is_err(cx, &arms[0]));
 +            then {
 +                return Some(expr);
 +            }
 +        }
 +    }
 +
 +    None
 +}
 +
 +/// Returns `true` if the lint is allowed in the current context. This is useful for
 +/// skipping long running code when it's unnecessary
 +///
 +/// This function should check the lint level for the same node, that the lint will
 +/// be emitted at. If the information is buffered to be emitted at a later point, please
 +/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
 +/// expectations at the checked nodes will be fulfilled.
 +pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
 +    cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 +}
 +
 +pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
 +    while let PatKind::Ref(subpat, _) = pat.kind {
 +        pat = subpat;
 +    }
 +    pat
 +}
 +
 +pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
 +    Integer::from_int_ty(&tcx, ity).size().bits()
 +}
 +
 +#[expect(clippy::cast_possible_wrap)]
 +/// Turn a constant int byte representation into an i128
 +pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as i128) << amt) >> amt
 +}
 +
 +#[expect(clippy::cast_sign_loss)]
 +/// clip unused bytes
 +pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
 +    let amt = 128 - int_bits(tcx, ity);
 +    ((u as u128) << amt) >> amt
 +}
 +
 +/// clip unused bytes
 +pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
 +    let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
 +    let amt = 128 - bits;
 +    (u << amt) >> amt
 +}
 +
 +pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
 +    attrs.iter().any(|attr| attr.has_name(symbol))
 +}
 +
 +pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
 +    let map = &tcx.hir();
 +    let mut prev_enclosing_node = None;
 +    let mut enclosing_node = node;
 +    while Some(enclosing_node) != prev_enclosing_node {
 +        if has_attr(map.attrs(enclosing_node), symbol) {
 +            return true;
 +        }
 +        prev_enclosing_node = Some(enclosing_node);
 +        enclosing_node = map.local_def_id_to_hir_id(map.get_parent_item(enclosing_node));
 +    }
 +
 +    false
 +}
 +
 +pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
 +    any_parent_has_attr(tcx, node, sym::automatically_derived)
 +}
 +
 +/// Matches a function call with the given path and returns the arguments.
 +///
 +/// Usage:
 +///
 +/// ```rust,ignore
 +/// if let Some(args) = match_function_call(cx, cmp_max_call, &paths::CMP_MAX);
 +/// ```
 +pub fn match_function_call<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    expr: &'tcx Expr<'_>,
 +    path: &[&str],
 +) -> Option<&'tcx [Expr<'tcx>]> {
 +    if_chain! {
 +        if let ExprKind::Call(fun, args) = expr.kind;
 +        if let ExprKind::Path(ref qpath) = fun.kind;
 +        if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
 +        if match_def_path(cx, fun_def_id, path);
 +        then {
 +            return Some(args);
 +        }
 +    };
 +    None
 +}
 +
 +/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 +/// any.
 +///
 +/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
 +pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
 +    let search_path = cx.get_def_path(did);
 +    paths
 +        .iter()
 +        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 +}
 +
 +/// Checks if the given `DefId` matches the path.
 +pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
 +    // We should probably move to Symbols in Clippy as well rather than interning every time.
 +    let path = cx.get_def_path(did);
 +    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
 +}
 +
 +/// Checks if the given `DefId` matches the `libc` item.
 +pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
 +    let path = cx.get_def_path(did);
 +    // libc is meant to be used as a flat list of names, but they're all actually defined in different
 +    // modules based on the target platform. Ignore everything but crate name and the item name.
 +    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
 +}
 +
 +/// Returns the list of condition expressions and the list of blocks in a
 +/// sequence of `if/else`.
 +/// E.g., this returns `([a, b], [c, d, e])` for the expression
 +/// `if a { c } else if b { d } else { e }`.
 +pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
 +    let mut conds = Vec::new();
 +    let mut blocks: Vec<&Block<'_>> = Vec::new();
 +
 +    while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
 +        conds.push(cond);
 +        if let ExprKind::Block(block, _) = then.kind {
 +            blocks.push(block);
 +        } else {
 +            panic!("ExprKind::If node is not an ExprKind::Block");
 +        }
 +
 +        if let Some(else_expr) = r#else {
 +            expr = else_expr;
 +        } else {
 +            break;
 +        }
 +    }
 +
 +    // final `else {..}`
 +    if !blocks.is_empty() {
 +        if let ExprKind::Block(block, _) = expr.kind {
 +            blocks.push(block);
 +        }
 +    }
 +
 +    (conds, blocks)
 +}
 +
 +/// Checks if the given function kind is an async function.
 +pub fn is_async_fn(kind: FnKind<'_>) -> bool {
 +    matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
 +}
 +
 +/// Peels away all the compiler generated code surrounding the body of an async function,
 +pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> {
 +    if let ExprKind::Call(
 +        _,
 +        &[
 +            Expr {
 +                kind: ExprKind::Closure(&Closure { body, .. }),
 +                ..
 +            },
 +        ],
 +    ) = body.value.kind
 +    {
 +        if let ExprKind::Block(
 +            Block {
 +                stmts: [],
 +                expr:
 +                    Some(Expr {
 +                        kind: ExprKind::DropTemps(expr),
 +                        ..
 +                    }),
 +                ..
 +            },
 +            _,
 +        ) = tcx.hir().body(body).value.kind
 +        {
 +            return Some(expr);
 +        }
 +    };
 +    None
 +}
 +
 +// check if expr is calling method or function with #[must_use] attribute
 +pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    let did = match expr.kind {
 +        ExprKind::Call(path, _) => if_chain! {
 +            if let ExprKind::Path(ref qpath) = path.kind;
 +            if let def::Res::Def(_, did) = cx.qpath_res(qpath, path.hir_id);
 +            then {
 +                Some(did)
 +            } else {
 +                None
 +            }
 +        },
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        _ => None,
 +    };
 +
 +    did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
 +}
 +
 +/// Checks if an expression represents the identity function
 +/// Only examines closures and `std::convert::identity`
 +pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 +    /// Checks if a function's body represents the identity function. Looks for bodies of the form:
 +    /// * `|x| x`
 +    /// * `|x| return x`
 +    /// * `|x| { return x }`
 +    /// * `|x| { return x; }`
 +    fn is_body_identity_function(cx: &LateContext<'_>, func: &Body<'_>) -> bool {
 +        let id = if_chain! {
 +            if let [param] = func.params;
 +            if let PatKind::Binding(_, id, _, _) = param.pat.kind;
 +            then {
 +                id
 +            } else {
 +                return false;
 +            }
 +        };
 +
 +        let mut expr = &func.value;
 +        loop {
 +            match expr.kind {
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [], expr: Some(e), .. }, _, )
 +                | ExprKind::Ret(Some(e)) => expr = e,
 +                #[rustfmt::skip]
 +                ExprKind::Block(&Block { stmts: [stmt], expr: None, .. }, _) => {
 +                    if_chain! {
 +                        if let StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind;
 +                        if let ExprKind::Ret(Some(ret_val)) = e.kind;
 +                        then {
 +                            expr = ret_val;
 +                        } else {
 +                            return false;
 +                        }
 +                    }
 +                },
 +                _ => return path_to_local_id(expr, id) && cx.typeck_results().expr_adjustments(expr).is_empty(),
 +            }
 +        }
 +    }
 +
 +    match expr.kind {
 +        ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
 +        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
 +    }
 +}
 +
 +/// Gets the node where an expression is either used, or it's type is unified with another branch.
 +/// Returns both the node and the `HirId` of the closest child node.
 +pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
 +    let mut child_id = expr.hir_id;
 +    let mut iter = tcx.hir().parent_iter(child_id);
 +    loop {
 +        match iter.next() {
 +            None => break None,
 +            Some((id, Node::Block(_))) => child_id = id,
 +            Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
 +            Some((_, Node::Expr(expr))) => match expr.kind {
 +                ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
 +                ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
 +                ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
 +                _ => break Some((Node::Expr(expr), child_id)),
 +            },
 +            Some((_, node)) => break Some((node, child_id)),
 +        }
 +    }
 +}
 +
 +/// Checks if the result of an expression is used, or it's type is unified with another branch.
 +pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    !matches!(
 +        get_expr_use_or_unification_node(tcx, expr),
 +        None | Some((
 +            Node::Stmt(Stmt {
 +                kind: StmtKind::Expr(_)
 +                    | StmtKind::Semi(_)
 +                    | StmtKind::Local(Local {
 +                        pat: Pat {
 +                            kind: PatKind::Wild,
 +                            ..
 +                        },
 +                        ..
 +                    }),
 +                ..
 +            }),
 +            _
 +        ))
 +    )
 +}
 +
 +/// Checks if the expression is the final expression returned from a block.
 +pub fn is_expr_final_block_expr(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 +    matches!(get_parent_node(tcx, expr.hir_id), Some(Node::Block(..)))
 +}
 +
 +pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
 +    if !is_no_std_crate(cx) {
 +        Some("std")
 +    } else if !is_no_core_crate(cx) {
 +        Some("core")
 +    } else {
 +        None
 +    }
 +}
 +
 +pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            normal.item.path == sym::no_std
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
 +    cx.tcx.hir().attrs(hir::CRATE_HIR_ID).iter().any(|attr| {
 +        if let ast::AttrKind::Normal(ref normal) = attr.kind {
 +            normal.item.path == sym::no_core
 +        } else {
 +            false
 +        }
 +    })
 +}
 +
 +/// Check if parent of a hir node is a trait implementation block.
 +/// For example, `f` in
 +/// ```rust
 +/// # struct S;
 +/// # trait Trait { fn f(); }
 +/// impl Trait for S {
 +///     fn f() {}
 +/// }
 +/// ```
 +pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
 +    if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
 +        matches!(item.kind, ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }))
 +    } else {
 +        false
 +    }
 +}
 +
 +/// Check if it's even possible to satisfy the `where` clause for the item.
 +///
 +/// `trivial_bounds` feature allows functions with unsatisfiable bounds, for example:
 +///
 +/// ```ignore
 +/// fn foo() where i32: Iterator {
 +///     for _ in 2i32 {}
 +/// }
 +/// ```
 +pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
 +    use rustc_trait_selection::traits;
 +    let predicates = cx
 +        .tcx
 +        .predicates_of(did)
 +        .predicates
 +        .iter()
 +        .filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
 +    traits::impossible_predicates(
 +        cx.tcx,
 +        traits::elaborate_predicates(cx.tcx, predicates)
 +            .map(|o| o.predicate)
 +            .collect::<Vec<_>>(),
 +    )
 +}
 +
 +/// Returns the `DefId` of the callee if the given expression is a function or method call.
 +pub fn fn_def_id(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<DefId> {
 +    match &expr.kind {
 +        ExprKind::MethodCall(..) => cx.typeck_results().type_dependent_def_id(expr.hir_id),
 +        ExprKind::Call(
 +            Expr {
 +                kind: ExprKind::Path(qpath),
 +                hir_id: path_hir_id,
 +                ..
 +            },
 +            ..,
 +        ) => {
 +            // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or
 +            // deref to fn pointers, dyn Fn, impl Fn - #8850
 +            if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) =
 +                cx.typeck_results().qpath_res(qpath, *path_hir_id)
 +            {
 +                Some(id)
 +            } else {
 +                None
 +            }
 +        },
 +        _ => None,
 +    }
 +}
 +
 +/// Returns Option<String> where String is a textual representation of the type encapsulated in the
 +/// slice iff the given expression is a slice of primitives (as defined in the
 +/// `is_recursively_primitive_type` function) and None otherwise.
 +pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
 +    let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
 +    let expr_kind = expr_type.kind();
 +    let is_primitive = match expr_kind {
 +        rustc_ty::Slice(element_type) => is_recursively_primitive_type(*element_type),
 +        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
 +            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
 +                is_recursively_primitive_type(*element_type)
 +            } else {
 +                unreachable!()
 +            }
 +        },
 +        _ => false,
 +    };
 +
 +    if is_primitive {
 +        // if we have wrappers like Array, Slice or Tuple, print these
 +        // and get the type enclosed in the slice ref
 +        match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
 +            rustc_ty::Slice(..) => return Some("slice".into()),
 +            rustc_ty::Array(..) => return Some("array".into()),
 +            rustc_ty::Tuple(..) => return Some("tuple".into()),
 +            _ => {
 +                // is_recursively_primitive_type() should have taken care
 +                // of the rest and we can rely on the type that is found
 +                let refs_peeled = expr_type.peel_refs();
 +                return Some(refs_peeled.walk().last().unwrap().to_string());
 +            },
 +        }
 +    }
 +    None
 +}
 +
 +/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
 +/// `hash` must be comformed with `eq`
 +pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
 +where
 +    Hash: Fn(&T) -> u64,
 +    Eq: Fn(&T, &T) -> bool,
 +{
 +    match exprs {
 +        [a, b] if eq(a, b) => return vec![(a, b)],
 +        _ if exprs.len() <= 2 => return vec![],
 +        _ => {},
 +    }
 +
 +    let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
 +
 +    let mut map: UnhashMap<u64, Vec<&_>> =
 +        UnhashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
 +
 +    for expr in exprs {
 +        match map.entry(hash(expr)) {
 +            Entry::Occupied(mut o) => {
 +                for o in o.get() {
 +                    if eq(o, expr) {
 +                        match_expr_list.push((o, expr));
 +                    }
 +                }
 +                o.get_mut().push(expr);
 +            },
 +            Entry::Vacant(v) => {
 +                v.insert(vec![expr]);
 +            },
 +        }
 +    }
 +
 +    match_expr_list
 +}
 +
 +/// Peels off all references on the pattern. Returns the underlying pattern and the number of
 +/// references removed.
 +pub fn peel_hir_pat_refs<'a>(pat: &'a Pat<'a>) -> (&'a Pat<'a>, usize) {
 +    fn peel<'a>(pat: &'a Pat<'a>, count: usize) -> (&'a Pat<'a>, usize) {
 +        if let PatKind::Ref(pat, _) = pat.kind {
 +            peel(pat, count + 1)
 +        } else {
 +            (pat, count)
 +        }
 +    }
 +    peel(pat, 0)
 +}
 +
 +/// Peels of expressions while the given closure returns `Some`.
 +pub fn peel_hir_expr_while<'tcx>(
 +    mut expr: &'tcx Expr<'tcx>,
 +    mut f: impl FnMut(&'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
 +) -> &'tcx Expr<'tcx> {
 +    while let Some(e) = f(expr) {
 +        expr = e;
 +    }
 +    expr
 +}
 +
 +/// Peels off up to the given number of references on the expression. Returns the underlying
 +/// expression and the number of references removed.
 +pub fn peel_n_hir_expr_refs<'a>(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
 +    let mut remaining = count;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) if remaining != 0 => {
 +            remaining -= 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count - remaining)
 +}
 +
 +/// Peels off all references on the expression. Returns the underlying expression and the number of
 +/// references removed.
 +pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
 +    let mut count = 0;
 +    let e = peel_hir_expr_while(expr, |e| match e.kind {
 +        ExprKind::AddrOf(ast::BorrowKind::Ref, _, e) => {
 +            count += 1;
 +            Some(e)
 +        },
 +        _ => None,
 +    });
 +    (e, count)
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
 +    let mut count = 0;
 +    loop {
 +        match &ty.kind {
 +            TyKind::Rptr(_, ref_ty) => {
 +                ty = ref_ty.ty;
 +                count += 1;
 +            },
 +            _ => break (ty, count),
 +        }
 +    }
 +}
 +
 +/// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 +/// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 +pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
 +    loop {
 +        match expr.kind {
 +            ExprKind::AddrOf(_, _, e) => expr = e,
 +            ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => expr = e,
 +            _ => break,
 +        }
 +    }
 +    expr
 +}
 +
 +pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 +    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +        if let Res::Def(_, def_id) = path.res {
 +            return cx.tcx.has_attr(def_id, sym::cfg) || cx.tcx.has_attr(def_id, sym::cfg_attr);
 +        }
 +    }
 +    false
 +}
 +
 +static TEST_ITEM_NAMES_CACHE: OnceLock<Mutex<FxHashMap<LocalDefId, Vec<Symbol>>>> = OnceLock::new();
 +
 +fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol]) -> bool) -> bool {
 +    let cache = TEST_ITEM_NAMES_CACHE.get_or_init(|| Mutex::new(FxHashMap::default()));
 +    let mut map: MutexGuard<'_, FxHashMap<LocalDefId, Vec<Symbol>>> = cache.lock().unwrap();
 +    let value = map.entry(module);
 +    match value {
 +        Entry::Occupied(entry) => f(entry.get()),
 +        Entry::Vacant(entry) => {
 +            let mut names = Vec::new();
 +            for id in tcx.hir().module_items(module) {
 +                if matches!(tcx.def_kind(id.def_id), DefKind::Const)
 +                    && let item = tcx.hir().item(id)
 +                    && let ItemKind::Const(ty, _body) = item.kind {
 +                    if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
 +                        // We could also check for the type name `test::TestDescAndFn`
 +                        if let Res::Def(DefKind::Struct, _) = path.res {
 +                            let has_test_marker = tcx
 +                                .hir()
 +                                .attrs(item.hir_id())
 +                                .iter()
 +                                .any(|a| a.has_name(sym::rustc_test_marker));
 +                            if has_test_marker {
 +                                names.push(item.ident.name);
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +            names.sort_unstable();
 +            f(entry.insert(names))
 +        },
 +    }
 +}
 +
 +/// Checks if the function containing the given `HirId` is a `#[test]` function
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    with_test_item_names(tcx, tcx.parent_module(id), |names| {
 +        tcx.hir()
 +            .parent_iter(id)
 +            // Since you can nest functions we need to collect all until we leave
 +            // function scope
 +            .any(|(_id, node)| {
 +                if let Node::Item(item) = node {
 +                    if let ItemKind::Fn(_, _, _) = item.kind {
 +                        // Note that we have sorted the item names in the visitor,
 +                        // so the binary_search gets the same as `contains`, but faster.
 +                        return names.binary_search(&item.ident.name).is_ok();
 +                    }
 +                }
 +                false
 +            })
 +    })
 +}
 +
 +/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
 +pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
 +    fn is_cfg_test(attr: &Attribute) -> bool {
 +        if attr.has_name(sym::cfg)
 +            && let Some(items) = attr.meta_item_list()
 +            && let [item] = &*items
 +            && item.has_name(sym::test)
 +        {
 +            true
 +        } else {
 +            false
 +        }
 +    }
 +    tcx.hir()
 +        .parent_iter(id)
 +        .flat_map(|(parent_id, _)| tcx.hir().attrs(parent_id))
 +        .any(is_cfg_test)
 +}
 +
 +/// Checks whether item either has `test` attribute applied, or
 +/// is a module with `test` in its name.
 +///
 +/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
 +pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
 +    is_in_test_function(tcx, item.hir_id())
 +        || matches!(item.kind, ItemKind::Mod(..))
 +            && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
 +}
 +
 +/// Walks the HIR tree from the given expression, up to the node where the value produced by the
 +/// expression is consumed. Calls the function for every node encountered this way until it returns
 +/// `Some`.
 +///
 +/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
 +/// produced by the expression is consumed.
 +pub fn walk_to_expr_usage<'tcx, T>(
 +    cx: &LateContext<'tcx>,
 +    e: &Expr<'tcx>,
 +    mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
 +) -> Option<T> {
 +    let map = cx.tcx.hir();
 +    let mut iter = map.parent_iter(e.hir_id);
 +    let mut child_id = e.hir_id;
 +
 +    while let Some((parent_id, parent)) = iter.next() {
 +        if let Some(x) = f(parent, child_id) {
 +            return Some(x);
 +        }
 +        let parent = match parent {
 +            Node::Expr(e) => e,
 +            Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            Node::Arm(a) if a.body.hir_id == child_id => {
 +                child_id = parent_id;
 +                continue;
 +            },
 +            _ => return None,
 +        };
 +        match parent.kind {
 +            ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
 +            ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
 +                child_id = id;
 +                iter = map.parent_iter(id);
 +            },
 +            ExprKind::Block(..) => child_id = parent_id,
 +            _ => return None,
 +        }
 +    }
 +    None
 +}
 +
++/// Checks whether a given span has any comment token
++/// This checks for all types of comment: line "//", block "/**", doc "///" "//!"
++pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
++    let Ok(snippet) = sm.span_to_snippet(span) else { return false };
++    return tokenize(&snippet).any(|token| {
++        matches!(
++            token.kind,
++            TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
++        )
++    });
++}
++
 +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 a268e339bb130df99138102b739e6def1d44c640,0000000000000000000000000000000000000000..e5ca35455404885139db2c6e5a7f27d8276a83e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,583 -1,0 +1,897 @@@
- use if_chain::if_chain;
 +#![allow(clippy::similar_names)] // `expr` and `expn`
 +
++use crate::is_path_diagnostic_item;
++use crate::source::snippet_opt;
 +use crate::visitors::expr_visitor_no_bodies;
 +
 +use arrayvec::ArrayVec;
- use rustc_span::{sym, ExpnData, ExpnId, ExpnKind, Span, Symbol};
++use itertools::{izip, Either, Itertools};
 +use rustc_ast::ast::LitKind;
 +use rustc_hir::intravisit::Visitor;
 +use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
++use rustc_lexer::unescape::unescape_literal;
++use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 +use rustc_lint::LateContext;
++use rustc_parse_format::{self as rpf, Alignment};
 +use rustc_span::def_id::DefId;
 +use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
- /// A parsed `format_args!` expansion
++use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Pos, Span, SpanData, Symbol};
 +use std::ops::ControlFlow;
 +
 +const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
 +    sym::assert_eq_macro,
 +    sym::assert_macro,
 +    sym::assert_ne_macro,
 +    sym::debug_assert_eq_macro,
 +    sym::debug_assert_macro,
 +    sym::debug_assert_ne_macro,
 +    sym::eprint_macro,
 +    sym::eprintln_macro,
 +    sym::format_args_macro,
 +    sym::format_macro,
 +    sym::print_macro,
 +    sym::println_macro,
 +    sym::std_panic_macro,
 +    sym::write_macro,
 +    sym::writeln_macro,
 +];
 +
 +/// Returns true if a given Macro `DefId` is a format macro (e.g. `println!`)
 +pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
 +    if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
 +        FORMAT_MACRO_DIAG_ITEMS.contains(&name)
 +    } else {
 +        false
 +    }
 +}
 +
 +/// A macro call, like `vec![1, 2, 3]`.
 +///
 +/// Use `tcx.item_name(macro_call.def_id)` to get the macro name.
 +/// Even better is to check if it is a diagnostic item.
 +///
 +/// This structure is similar to `ExpnData` but it precludes desugaring expansions.
 +#[derive(Debug)]
 +pub struct MacroCall {
 +    /// Macro `DefId`
 +    pub def_id: DefId,
 +    /// Kind of macro
 +    pub kind: MacroKind,
 +    /// The expansion produced by the macro call
 +    pub expn: ExpnId,
 +    /// Span of the macro call site
 +    pub span: Span,
 +}
 +
 +impl MacroCall {
 +    pub fn is_local(&self) -> bool {
 +        span_is_local(self.span)
 +    }
 +}
 +
 +/// Returns an iterator of expansions that created the given span
 +pub fn expn_backtrace(mut span: Span) -> impl Iterator<Item = (ExpnId, ExpnData)> {
 +    std::iter::from_fn(move || {
 +        let ctxt = span.ctxt();
 +        if ctxt == SyntaxContext::root() {
 +            return None;
 +        }
 +        let expn = ctxt.outer_expn();
 +        let data = expn.expn_data();
 +        span = data.call_site;
 +        Some((expn, data))
 +    })
 +}
 +
 +/// Checks whether the span is from the root expansion or a locally defined macro
 +pub fn span_is_local(span: Span) -> bool {
 +    !span.from_expansion() || expn_is_local(span.ctxt().outer_expn())
 +}
 +
 +/// Checks whether the expansion is the root expansion or a locally defined macro
 +pub fn expn_is_local(expn: ExpnId) -> bool {
 +    if expn == ExpnId::root() {
 +        return true;
 +    }
 +    let data = expn.expn_data();
 +    let backtrace = expn_backtrace(data.call_site);
 +    std::iter::once((expn, data))
 +        .chain(backtrace)
 +        .find_map(|(_, data)| data.macro_def_id)
 +        .map_or(true, DefId::is_local)
 +}
 +
 +/// Returns an iterator of macro expansions that created the given span.
 +/// Note that desugaring expansions are skipped.
 +pub fn macro_backtrace(span: Span) -> impl Iterator<Item = MacroCall> {
 +    expn_backtrace(span).filter_map(|(expn, data)| match data {
 +        ExpnData {
 +            kind: ExpnKind::Macro(kind, _),
 +            macro_def_id: Some(def_id),
 +            call_site: span,
 +            ..
 +        } => Some(MacroCall {
 +            def_id,
 +            kind,
 +            expn,
 +            span,
 +        }),
 +        _ => None,
 +    })
 +}
 +
 +/// If the macro backtrace of `span` has a macro call at the root expansion
 +/// (i.e. not a nested macro call), returns `Some` with the `MacroCall`
 +pub fn root_macro_call(span: Span) -> Option<MacroCall> {
 +    macro_backtrace(span).last()
 +}
 +
 +/// Like [`root_macro_call`], but only returns `Some` if `node` is the "first node"
 +/// produced by the macro call, as in [`first_node_in_macro`].
 +pub fn root_macro_call_first_node(cx: &LateContext<'_>, node: &impl HirNode) -> Option<MacroCall> {
 +    if first_node_in_macro(cx, node) != Some(ExpnId::root()) {
 +        return None;
 +    }
 +    root_macro_call(node.span())
 +}
 +
 +/// Like [`macro_backtrace`], but only returns macro calls where `node` is the "first node" of the
 +/// macro call, as in [`first_node_in_macro`].
 +pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) -> impl Iterator<Item = MacroCall> {
 +    let span = node.span();
 +    first_node_in_macro(cx, node)
 +        .into_iter()
 +        .flat_map(move |expn| macro_backtrace(span).take_while(move |macro_call| macro_call.expn != expn))
 +}
 +
 +/// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
 +/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
 +/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
 +/// This is useful for finding macro calls while visiting the HIR without processing the macro call
 +/// at every node within its expansion.
 +///
 +/// If you already have immediate access to the parent node, it is simpler to
 +/// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
 +///
 +/// If a macro call is in statement position, it expands to one or more statements.
 +/// In that case, each statement *and* their immediate descendants will all yield `Some`
 +/// with the `ExpnId` of the containing block.
 +///
 +/// A node may be the "first node" of multiple macro calls in a macro backtrace.
 +/// The expansion of the outermost macro call site is returned in such cases.
 +pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<ExpnId> {
 +    // get the macro expansion or return `None` if not found
 +    // `macro_backtrace` importantly ignores desugaring expansions
 +    let expn = macro_backtrace(node.span()).next()?.expn;
 +
 +    // get the parent node, possibly skipping over a statement
 +    // if the parent is not found, it is sensible to return `Some(root)`
 +    let hir = cx.tcx.hir();
 +    let mut parent_iter = hir.parent_iter(node.hir_id());
 +    let (parent_id, _) = match parent_iter.next() {
 +        None => return Some(ExpnId::root()),
 +        Some((_, Node::Stmt(_))) => match parent_iter.next() {
 +            None => return Some(ExpnId::root()),
 +            Some(next) => next,
 +        },
 +        Some(next) => next,
 +    };
 +
 +    // get the macro expansion of the parent node
 +    let parent_span = hir.span(parent_id);
 +    let Some(parent_macro_call) = macro_backtrace(parent_span).next() else {
 +        // the parent node is not in a macro
 +        return Some(ExpnId::root());
 +    };
 +
 +    if parent_macro_call.expn.is_descendant_of(expn) {
 +        // `node` is input to a macro call
 +        return None;
 +    }
 +
 +    Some(parent_macro_call.expn)
 +}
 +
 +/* Specific Macro Utils */
 +
 +/// Is `def_id` of `std::panic`, `core::panic` or any inner implementation macros
 +pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
 +    let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
 +    matches!(
 +        name.as_str(),
 +        "core_panic_macro"
 +            | "std_panic_macro"
 +            | "core_panic_2015_macro"
 +            | "std_panic_2015_macro"
 +            | "core_panic_2021_macro"
 +    )
 +}
 +
 +pub enum PanicExpn<'a> {
 +    /// No arguments - `panic!()`
 +    Empty,
 +    /// A string literal or any `&str` - `panic!("message")` or `panic!(message)`
 +    Str(&'a Expr<'a>),
 +    /// A single argument that implements `Display` - `panic!("{}", object)`
 +    Display(&'a Expr<'a>),
 +    /// Anything else - `panic!("error {}: {}", a, b)`
 +    Format(FormatArgsExpn<'a>),
 +}
 +
 +impl<'a> PanicExpn<'a> {
 +    pub fn parse(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Self> {
 +        if !macro_backtrace(expr.span).any(|macro_call| is_panic(cx, macro_call.def_id)) {
 +            return None;
 +        }
 +        let ExprKind::Call(callee, [arg]) = &expr.kind else { return None };
 +        let ExprKind::Path(QPath::Resolved(_, path)) = &callee.kind else { return None };
 +        let result = match path.segments.last().unwrap().ident.as_str() {
 +            "panic" if arg.span.ctxt() == expr.span.ctxt() => Self::Empty,
 +            "panic" | "panic_str" => Self::Str(arg),
 +            "panic_display" => {
 +                let ExprKind::AddrOf(_, _, e) = &arg.kind else { return None };
 +                Self::Display(e)
 +            },
 +            "panic_fmt" => Self::Format(FormatArgsExpn::parse(cx, arg)?),
 +            _ => return None,
 +        };
 +        Some(result)
 +    }
 +}
 +
 +/// Finds the arguments of an `assert!` or `debug_assert!` macro call within the macro expansion
 +pub fn find_assert_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([e], p)| (e, p))
 +}
 +
 +/// Finds the arguments of an `assert_eq!` or `debug_assert_eq!` macro call within the macro
 +/// expansion
 +pub fn find_assert_eq_args<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<(&'a Expr<'a>, &'a Expr<'a>, PanicExpn<'a>)> {
 +    find_assert_args_inner(cx, expr, expn).map(|([a, b], p)| (a, b, p))
 +}
 +
 +fn find_assert_args_inner<'a, const N: usize>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +) -> Option<([&'a Expr<'a>; N], PanicExpn<'a>)> {
 +    let macro_id = expn.expn_data().macro_def_id?;
 +    let (expr, expn) = match cx.tcx.item_name(macro_id).as_str().strip_prefix("debug_") {
 +        None => (expr, expn),
 +        Some(inner_name) => find_assert_within_debug_assert(cx, expr, expn, Symbol::intern(inner_name))?,
 +    };
 +    let mut args = ArrayVec::new();
 +    let mut panic_expn = None;
 +    expr_visitor_no_bodies(|e| {
 +        if args.is_full() {
 +            if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
 +                panic_expn = PanicExpn::parse(cx, e);
 +            }
 +            panic_expn.is_none()
 +        } else if is_assert_arg(cx, e, expn) {
 +            args.push(e);
 +            false
 +        } else {
 +            true
 +        }
 +    })
 +    .visit_expr(expr);
 +    let args = args.into_inner().ok()?;
 +    // if no `panic!(..)` is found, use `PanicExpn::Empty`
 +    // to indicate that the default assertion message is used
 +    let panic_expn = panic_expn.unwrap_or(PanicExpn::Empty);
 +    Some((args, panic_expn))
 +}
 +
 +fn find_assert_within_debug_assert<'a>(
 +    cx: &LateContext<'_>,
 +    expr: &'a Expr<'a>,
 +    expn: ExpnId,
 +    assert_name: Symbol,
 +) -> Option<(&'a Expr<'a>, ExpnId)> {
 +    let mut found = None;
 +    expr_visitor_no_bodies(|e| {
 +        if found.is_some() || !e.span.from_expansion() {
 +            return false;
 +        }
 +        let e_expn = e.span.ctxt().outer_expn();
 +        if e_expn == expn {
 +            return true;
 +        }
 +        if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
 +            found = Some((e, e_expn));
 +        }
 +        false
 +    })
 +    .visit_expr(expr);
 +    found
 +}
 +
 +fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
 +    if !expr.span.from_expansion() {
 +        return true;
 +    }
 +    let result = macro_backtrace(expr.span).try_for_each(|macro_call| {
 +        if macro_call.expn == assert_expn {
 +            ControlFlow::Break(false)
 +        } else {
 +            match cx.tcx.item_name(macro_call.def_id) {
 +                // `cfg!(debug_assertions)` in `debug_assert!`
 +                sym::cfg => ControlFlow::CONTINUE,
 +                // assert!(other_macro!(..))
 +                _ => ControlFlow::Break(true),
 +            }
 +        }
 +    });
 +    match result {
 +        ControlFlow::Break(is_assert_arg) => is_assert_arg,
 +        ControlFlow::Continue(()) => true,
 +    }
 +}
 +
- pub struct FormatArgsExpn<'tcx> {
-     /// Span of the first argument, the format string
-     pub format_string_span: Span,
-     /// The format string split by formatted args like `{..}`
-     pub format_string_parts: Vec<Symbol>,
-     /// Values passed after the format string
-     pub value_args: Vec<&'tcx Expr<'tcx>>,
-     /// Each element is a `value_args` index and a formatting trait (e.g. `sym::Debug`)
-     pub formatters: Vec<(usize, Symbol)>,
-     /// List of `fmt::v1::Argument { .. }` expressions. If this is empty,
-     /// then `formatters` represents the format args (`{..}`).
-     /// If this is non-empty, it represents the format args, and the `position`
-     /// parameters within the struct expressions are indexes of `formatters`.
-     pub specs: Vec<&'tcx Expr<'tcx>>,
++/// The format string doesn't exist in the HIR, so we reassemble it from source code
 +#[derive(Debug)]
- impl<'tcx> FormatArgsExpn<'tcx> {
-     /// Parses an expanded `format_args!` or `format_args_nl!` invocation
-     pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
-         macro_backtrace(expr.span).find(|macro_call| {
-             matches!(
-                 cx.tcx.item_name(macro_call.def_id),
-                 sym::const_format_args | sym::format_args | sym::format_args_nl
-             )
-         })?;
-         let mut format_string_span: Option<Span> = None;
-         let mut format_string_parts: Vec<Symbol> = Vec::new();
-         let mut value_args: Vec<&Expr<'_>> = Vec::new();
-         let mut formatters: Vec<(usize, Symbol)> = Vec::new();
-         let mut specs: Vec<&Expr<'_>> = Vec::new();
-         expr_visitor_no_bodies(|e| {
-             // if we're still inside of the macro definition...
-             if e.span.ctxt() == expr.span.ctxt() {
-                 // ArgumentV1::new_<format_trait>(<value>)
-                 if_chain! {
-                     if let ExprKind::Call(callee, [val]) = e.kind;
-                     if let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind;
-                     if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
-                     if path.segments.last().unwrap().ident.name == sym::ArgumentV1;
-                     if seg.ident.name.as_str().starts_with("new_");
-                     then {
-                         let val_idx = if_chain! {
-                             if val.span.ctxt() == expr.span.ctxt();
-                             if let ExprKind::Field(_, field) = val.kind;
-                             if let Ok(idx) = field.name.as_str().parse();
-                             then {
-                                 // tuple index
-                                 idx
-                             } else {
-                                 // assume the value expression is passed directly
-                                 formatters.len()
-                             }
-                         };
-                         let fmt_trait = match seg.ident.name.as_str() {
-                             "new_display" => "Display",
-                             "new_debug" => "Debug",
-                             "new_lower_exp" => "LowerExp",
-                             "new_upper_exp" => "UpperExp",
-                             "new_octal" => "Octal",
-                             "new_pointer" => "Pointer",
-                             "new_binary" => "Binary",
-                             "new_lower_hex" => "LowerHex",
-                             "new_upper_hex" => "UpperHex",
-                             _ => unreachable!(),
-                         };
-                         formatters.push((val_idx, Symbol::intern(fmt_trait)));
-                     }
-                 }
-                 if let ExprKind::Struct(QPath::Resolved(_, path), ..) = e.kind {
-                     if path.segments.last().unwrap().ident.name == sym::Argument {
-                         specs.push(e);
-                     }
++pub struct FormatString {
++    /// Span of the whole format string literal, including `[r#]"`.
++    pub span: Span,
++    /// Snippet of the whole format string literal, including `[r#]"`.
++    pub snippet: String,
++    /// If the string is raw `r"..."`/`r#""#`, how many `#`s does it have on each side.
++    pub style: Option<usize>,
++    /// The unescaped value of the format string, e.g. `"val – {}"` for the literal
++    /// `"val \u{2013} {}"`.
++    pub unescaped: String,
++    /// The format string split by format args like `{..}`.
++    pub parts: Vec<Symbol>,
 +}
 +
-                 // walk through the macro expansion
-                 return true;
++impl FormatString {
++    fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
++        // format_args!(r"a {} b \", 1);
++        //
++        // expands to
++        //
++        // ::core::fmt::Arguments::new_v1(&["a ", " b \\"],
++        //      &[::core::fmt::ArgumentV1::new_display(&1)]);
++        //
++        // where `pieces` is the expression `&["a ", " b \\"]`. It has the span of `r"a {} b \"`
++        let span = pieces.span;
++        let snippet = snippet_opt(cx, span)?;
++
++        let (inner, style) = match tokenize(&snippet).next()?.kind {
++            TokenKind::Literal { kind, .. } => {
++                let style = match kind {
++                    LiteralKind::Str { .. } => None,
++                    LiteralKind::RawStr { n_hashes: Some(n), .. } => Some(n.into()),
++                    _ => return None,
++                };
++
++                let start = style.map_or(1, |n| 2 + n);
++                let end = snippet.len() - style.map_or(1, |n| 1 + n);
++
++                (&snippet[start..end], style)
++            },
++            _ => return None,
++        };
++
++        let mode = if style.is_some() {
++            unescape::Mode::RawStr
++        } else {
++            unescape::Mode::Str
++        };
++
++        let mut unescaped = String::with_capacity(inner.len());
++        unescape_literal(inner, mode, &mut |_, ch| {
++            unescaped.push(ch.unwrap());
++        });
++
++        let mut parts = Vec::new();
++        expr_visitor_no_bodies(|expr| {
++            if let ExprKind::Lit(lit) = &expr.kind {
++                if let LitKind::Str(symbol, _) = lit.node {
++                    parts.push(symbol);
 +                }
-             // assume that the first expr with a differing context represents
-             // (and has the span of) the format string
-             if format_string_span.is_none() {
-                 format_string_span = Some(e.span);
-                 let span = e.span;
-                 // walk the expr and collect string literals which are format string parts
-                 expr_visitor_no_bodies(|e| {
-                     if e.span.ctxt() != span.ctxt() {
-                         // defensive check, probably doesn't happen
-                         return false;
-                     }
-                     if let ExprKind::Lit(lit) = &e.kind {
-                         if let LitKind::Str(symbol, _s) = lit.node {
-                             format_string_parts.push(symbol);
-                         }
-                     }
-                     true
-                 })
-                 .visit_expr(e);
 +            }
-                 // assume that any further exprs with a differing context are value args
-                 value_args.push(e);
++
++            true
++        })
++        .visit_expr(pieces);
++
++        Some(Self {
++            span,
++            snippet,
++            style,
++            unescaped,
++            parts,
++        })
++    }
++}
++
++struct FormatArgsValues<'tcx> {
++    /// See `FormatArgsExpn::value_args`
++    value_args: Vec<&'tcx Expr<'tcx>>,
++    /// Maps an `rt::v1::Argument::position` or an `rt::v1::Count::Param` to its index in
++    /// `value_args`
++    pos_to_value_index: Vec<usize>,
++    /// Used to check if a value is declared inline & to resolve `InnerSpan`s.
++    format_string_span: SpanData,
++}
++
++impl<'tcx> FormatArgsValues<'tcx> {
++    fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
++        let mut pos_to_value_index = Vec::new();
++        let mut value_args = Vec::new();
++        expr_visitor_no_bodies(|expr| {
++            if expr.span.ctxt() == args.span.ctxt() {
++                // ArgumentV1::new_<format_trait>(<val>)
++                // ArgumentV1::from_usize(<val>)
++                if let ExprKind::Call(callee, [val]) = expr.kind
++                    && let ExprKind::Path(QPath::TypeRelative(ty, _)) = callee.kind
++                    && let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind
++                    && path.segments.last().unwrap().ident.name == sym::ArgumentV1
++                {
++                    let val_idx = if val.span.ctxt() == expr.span.ctxt()
++                        && let ExprKind::Field(_, field) = val.kind
++                        && let Ok(idx) = field.name.as_str().parse()
++                    {
++                        // tuple index
++                        idx
++                    } else {
++                        // assume the value expression is passed directly
++                        pos_to_value_index.len()
++                    };
++
++                    pos_to_value_index.push(val_idx);
++                }
++
++                true
 +            } else {
-             // don't walk anything not from the macro expansion (e.a. inputs)
-             false
++                // assume that any expr with a differing span is a value
++                value_args.push(expr);
++
++                false
 +            }
-         .visit_expr(expr);
-         Some(FormatArgsExpn {
-             format_string_span: format_string_span?,
-             format_string_parts,
 +        })
-             formatters,
-             specs,
++        .visit_expr(args);
++
++        Self {
 +            value_args,
-     /// Finds a nested call to `format_args!` within a `format!`-like macro call
++            pos_to_value_index,
++            format_string_span,
++        }
++    }
++}
++
++/// The positions of a format argument's value, precision and width
++///
++/// A position is an index into the second argument of `Arguments::new_v1[_formatted]`
++#[derive(Debug, Default, Copy, Clone)]
++struct ParamPosition {
++    /// The position stored in `rt::v1::Argument::position`.
++    value: usize,
++    /// The position stored in `rt::v1::FormatSpec::width` if it is a `Count::Param`.
++    width: Option<usize>,
++    /// The position stored in `rt::v1::FormatSpec::precision` if it is a `Count::Param`.
++    precision: Option<usize>,
++}
++
++/// Parses the `fmt` arg of `Arguments::new_v1_formatted(pieces, args, fmt, _)`
++fn parse_rt_fmt<'tcx>(fmt_arg: &'tcx Expr<'tcx>) -> Option<impl Iterator<Item = ParamPosition> + 'tcx> {
++    fn parse_count(expr: &Expr<'_>) -> Option<usize> {
++        // ::core::fmt::rt::v1::Count::Param(1usize),
++        if let ExprKind::Call(ctor, [val]) = expr.kind
++            && let ExprKind::Path(QPath::Resolved(_, path)) = ctor.kind
++            && path.segments.last()?.ident.name == sym::Param
++            && let ExprKind::Lit(lit) = &val.kind
++            && let LitKind::Int(pos, _) = lit.node
++        {
++            Some(pos as usize)
++        } else {
++            None
++        }
++    }
++
++    if let ExprKind::AddrOf(.., array) = fmt_arg.kind
++        && let ExprKind::Array(specs) = array.kind
++    {
++        Some(specs.iter().map(|spec| {
++            let mut position = ParamPosition::default();
++
++            // ::core::fmt::rt::v1::Argument {
++            //     position: 0usize,
++            //     format: ::core::fmt::rt::v1::FormatSpec {
++            //         ..
++            //         precision: ::core::fmt::rt::v1::Count::Implied,
++            //         width: ::core::fmt::rt::v1::Count::Implied,
++            //     },
++            // }
++
++            // TODO: this can be made much nicer next sync with `Visitor::visit_expr_field`
++            if let ExprKind::Struct(_, fields, _) = spec.kind {
++                for field in fields {
++                    match (field.ident.name, &field.expr.kind) {
++                        (sym::position, ExprKind::Lit(lit)) => {
++                            if let LitKind::Int(pos, _) = lit.node {
++                                position.value = pos as usize;
++                            }
++                        },
++                        (sym::format, &ExprKind::Struct(_, spec_fields, _)) => {
++                            for spec_field in spec_fields {
++                                match spec_field.ident.name {
++                                    sym::precision => {
++                                        position.precision = parse_count(spec_field.expr);
++                                    },
++                                    sym::width => {
++                                        position.width = parse_count(spec_field.expr);
++                                    },
++                                    _ => {},
++                                }
++                            }
++                        },
++                        _ => {},
++                    }
++                }
++            }
++
++            position
++        }))
++    } else {
++        None
++    }
++}
++
++/// `Span::from_inner`, but for `rustc_parse_format`'s `InnerSpan`
++fn span_from_inner(base: SpanData, inner: rpf::InnerSpan) -> Span {
++    Span::new(
++        base.lo + BytePos::from_usize(inner.start),
++        base.lo + BytePos::from_usize(inner.end),
++        base.ctxt,
++        base.parent,
++    )
++}
++
++#[derive(Debug, Copy, Clone, PartialEq, Eq)]
++pub enum FormatParamKind {
++    /// An implicit parameter , such as `{}` or `{:?}`.
++    Implicit,
++    /// A parameter with an explicit number, or an asterisk precision. e.g. `{1}`, `{0:?}`,
++    /// `{:.0$}` or `{:.*}`.
++    Numbered,
++    /// A named parameter with a named `value_arg`, such as the `x` in `format!("{x}", x = 1)`.
++    Named(Symbol),
++    /// An implicit named parameter, such as the `y` in `format!("{y}")`.
++    NamedInline(Symbol),
++}
++
++/// A `FormatParam` is any place in a `FormatArgument` that refers to a supplied value, e.g.
++///
++/// ```
++/// let precision = 2;
++/// format!("{:.precision$}", 0.1234);
++/// ```
++///
++/// has two `FormatParam`s, a [`FormatParamKind::Implicit`] `.kind` with a `.value` of `0.1234`
++/// and a [`FormatParamKind::NamedInline("precision")`] `.kind` with a `.value` of `2`
++#[derive(Debug, Copy, Clone)]
++pub struct FormatParam<'tcx> {
++    /// The expression this parameter refers to.
++    pub value: &'tcx Expr<'tcx>,
++    /// How this parameter refers to its `value`.
++    pub kind: FormatParamKind,
++    /// Span of the parameter, may be zero width. Includes the whitespace of implicit parameters.
++    ///
++    /// ```text
++    /// format!("{}, {  }, {0}, {name}", ...);
++    ///          ^    ~~    ~    ~~~~
++    /// ```
++    pub span: Span,
++}
++
++impl<'tcx> FormatParam<'tcx> {
++    fn new(
++        mut kind: FormatParamKind,
++        position: usize,
++        inner: rpf::InnerSpan,
++        values: &FormatArgsValues<'tcx>,
++    ) -> Option<Self> {
++        let value_index = *values.pos_to_value_index.get(position)?;
++        let value = *values.value_args.get(value_index)?;
++        let span = span_from_inner(values.format_string_span, inner);
++
++        // if a param is declared inline, e.g. `format!("{x}")`, the generated expr's span points
++        // into the format string
++        if let FormatParamKind::Named(name) = kind && values.format_string_span.contains(value.span.data()) {
++            kind = FormatParamKind::NamedInline(name);
++        }
++
++        Some(Self { value, kind, span })
++    }
++}
++
++/// Used by [width](https://doc.rust-lang.org/std/fmt/#width) and
++/// [precision](https://doc.rust-lang.org/std/fmt/#precision) specifiers.
++#[derive(Debug, Copy, Clone)]
++pub enum Count<'tcx> {
++    /// Specified with a literal number, stores the value.
++    Is(usize, Span),
++    /// Specified using `$` and `*` syntaxes. The `*` format is still considered to be
++    /// `FormatParamKind::Numbered`.
++    Param(FormatParam<'tcx>),
++    /// Not specified.
++    Implied,
++}
++
++impl<'tcx> Count<'tcx> {
++    fn new(
++        count: rpf::Count<'_>,
++        position: Option<usize>,
++        inner: Option<rpf::InnerSpan>,
++        values: &FormatArgsValues<'tcx>,
++    ) -> Option<Self> {
++        Some(match count {
++            rpf::Count::CountIs(val) => Self::Is(val, span_from_inner(values.format_string_span, inner?)),
++            rpf::Count::CountIsName(name, span) => Self::Param(FormatParam::new(
++                FormatParamKind::Named(Symbol::intern(name)),
++                position?,
++                span,
++                values,
++            )?),
++            rpf::Count::CountIsParam(_) => {
++                Self::Param(FormatParam::new(FormatParamKind::Numbered, position?, inner?, values)?)
++            },
++            rpf::Count::CountImplied => Self::Implied,
++        })
++    }
++
++    pub fn is_implied(self) -> bool {
++        matches!(self, Count::Implied)
++    }
++
++    pub fn param(self) -> Option<FormatParam<'tcx>> {
++        match self {
++            Count::Param(param) => Some(param),
++            _ => None,
++        }
++    }
++}
++
++/// Specification for the formatting of an argument in the format string. See
++/// <https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters> for the precise meanings.
++#[derive(Debug)]
++pub struct FormatSpec<'tcx> {
++    /// Optionally specified character to fill alignment with.
++    pub fill: Option<char>,
++    /// Optionally specified alignment.
++    pub align: Alignment,
++    /// Packed version of various flags provided, see [`rustc_parse_format::Flag`].
++    pub flags: u32,
++    /// Represents either the maximum width or the integer precision.
++    pub precision: Count<'tcx>,
++    /// The minimum width, will be padded according to `width`/`align`
++    pub width: Count<'tcx>,
++    /// The formatting trait used by the argument, e.g. `sym::Display` for `{}`, `sym::Debug` for
++    /// `{:?}`.
++    pub r#trait: Symbol,
++    pub trait_span: Option<Span>,
++}
++
++impl<'tcx> FormatSpec<'tcx> {
++    fn new(spec: rpf::FormatSpec<'_>, positions: ParamPosition, values: &FormatArgsValues<'tcx>) -> Option<Self> {
++        Some(Self {
++            fill: spec.fill,
++            align: spec.align,
++            flags: spec.flags,
++            precision: Count::new(spec.precision, positions.precision, spec.precision_span, values)?,
++            width: Count::new(spec.width, positions.width, spec.width_span, values)?,
++            r#trait: match spec.ty {
++                "" => sym::Display,
++                "?" => sym::Debug,
++                "o" => sym!(Octal),
++                "x" => sym!(LowerHex),
++                "X" => sym!(UpperHex),
++                "p" => sym::Pointer,
++                "b" => sym!(Binary),
++                "e" => sym!(LowerExp),
++                "E" => sym!(UpperExp),
++                _ => return None,
++            },
++            trait_span: spec
++                .ty_span
++                .map(|span| span_from_inner(values.format_string_span, span)),
 +        })
 +    }
 +
-     /// Returns a vector of `FormatArgsArg`.
-     pub fn args(&self) -> Option<Vec<FormatArgsArg<'tcx>>> {
-         if self.specs.is_empty() {
-             let args = std::iter::zip(&self.value_args, &self.formatters)
-                 .map(|(value, &(_, format_trait))| FormatArgsArg {
-                     value,
-                     format_trait,
-                     spec: None,
-                 })
-                 .collect();
-             return Some(args);
-         }
-         self.specs
-             .iter()
-             .map(|spec| {
-                 if_chain! {
-                     // struct `core::fmt::rt::v1::Argument`
-                     if let ExprKind::Struct(_, fields, _) = spec.kind;
-                     if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position);
-                     if let ExprKind::Lit(lit) = &position_field.expr.kind;
-                     if let LitKind::Int(position, _) = lit.node;
-                     if let Ok(i) = usize::try_from(position);
-                     if let Some(&(j, format_trait)) = self.formatters.get(i);
-                     then {
-                         Some(FormatArgsArg {
-                             value: self.value_args[j],
-                             format_trait,
-                             spec: Some(spec),
-                         })
-                     } else {
-                         None
-                     }
-                 }
-             })
-             .collect()
-     }
++    /// Returns true if this format spec would change the contents of a string when formatted
++    pub fn has_string_formatting(&self) -> bool {
++        self.r#trait != sym::Display || !self.width.is_implied() || !self.precision.is_implied()
++    }
++}
++
++/// A format argument, such as `{}`, `{foo:?}`.
++#[derive(Debug)]
++pub struct FormatArg<'tcx> {
++    /// The parameter the argument refers to.
++    pub param: FormatParam<'tcx>,
++    /// How to format `param`.
++    pub format: FormatSpec<'tcx>,
++    /// span of the whole argument, `{..}`.
++    pub span: Span,
++}
++
++/// A parsed `format_args!` expansion.
++#[derive(Debug)]
++pub struct FormatArgsExpn<'tcx> {
++    /// The format string literal.
++    pub format_string: FormatString,
++    // The format arguments, such as `{:?}`.
++    pub args: Vec<FormatArg<'tcx>>,
++    /// Has an added newline due to `println!()`/`writeln!()`/etc. The last format string part will
++    /// include this added newline.
++    pub newline: bool,
++    /// Values passed after the format string and implicit captures. `[1, z + 2, x]` for
++    /// `format!("{x} {} {y}", 1, z + 2)`.
++    value_args: Vec<&'tcx Expr<'tcx>>,
++}
++
++impl<'tcx> FormatArgsExpn<'tcx> {
++    pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
++        let macro_name = macro_backtrace(expr.span)
++            .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
++            .find(|&name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))?;
++        let newline = macro_name == sym::format_args_nl;
++
++        // ::core::fmt::Arguments::new_v1(pieces, args)
++        // ::core::fmt::Arguments::new_v1_formatted(pieces, args, fmt, _unsafe_arg)
++        if let ExprKind::Call(callee, [pieces, args, rest @ ..]) = expr.kind
++            && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = callee.kind
++            && is_path_diagnostic_item(cx, ty, sym::Arguments)
++            && matches!(seg.ident.as_str(), "new_v1" | "new_v1_formatted")
++        {
++            let format_string = FormatString::new(cx, pieces)?;
++
++            let mut parser = rpf::Parser::new(
++                &format_string.unescaped,
++                format_string.style,
++                Some(format_string.snippet.clone()),
++                // `format_string.unescaped` does not contain the appended newline
++                false,
++                rpf::ParseMode::Format,
++            );
++
++            let parsed_args = parser
++                .by_ref()
++                .filter_map(|piece| match piece {
++                    rpf::Piece::NextArgument(a) => Some(a),
++                    rpf::Piece::String(_) => None,
++                })
++                .collect_vec();
++            if !parser.errors.is_empty() {
++                return None;
++            }
++
++            let positions = if let Some(fmt_arg) = rest.first() {
++                // If the argument contains format specs, `new_v1_formatted(_, _, fmt, _)`, parse
++                // them.
++
++                Either::Left(parse_rt_fmt(fmt_arg)?)
++            } else {
++                // If no format specs are given, the positions are in the given order and there are
++                // no `precision`/`width`s to consider.
++
++                Either::Right((0..).map(|n| ParamPosition {
++                    value: n,
++                    width: None,
++                    precision: None,
++                }))
++            };
++
++            let values = FormatArgsValues::new(args, format_string.span.data());
++
++            let args = izip!(positions, parsed_args, parser.arg_places)
++                .map(|(position, parsed_arg, arg_span)| {
++                    Some(FormatArg {
++                        param: FormatParam::new(
++                            match parsed_arg.position {
++                                rpf::Position::ArgumentImplicitlyIs(_) => FormatParamKind::Implicit,
++                                rpf::Position::ArgumentIs(_) => FormatParamKind::Numbered,
++                                // NamedInline is handled by `FormatParam::new()`
++                                rpf::Position::ArgumentNamed(name) => FormatParamKind::Named(Symbol::intern(name)),
++                            },
++                            position.value,
++                            parsed_arg.position_span,
++                            &values,
++                        )?,
++                        format: FormatSpec::new(parsed_arg.format, position, &values)?,
++                        span: span_from_inner(values.format_string_span, arg_span),
++                    })
++                })
++                .collect::<Option<Vec<_>>>()?;
++
++            Some(Self {
++                format_string,
++                args,
++                value_args: values.value_args,
++                newline,
++            })
++        } else {
++            None
++        }
++    }
++
 +    pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
 +        let mut format_args = None;
 +        expr_visitor_no_bodies(|e| {
 +            if format_args.is_some() {
 +                return false;
 +            }
 +            let e_ctxt = e.span.ctxt();
 +            if e_ctxt == expr.span.ctxt() {
 +                return true;
 +            }
 +            if e_ctxt.outer_expn().is_descendant_of(expn_id) {
 +                format_args = FormatArgsExpn::parse(cx, e);
 +            }
 +            false
 +        })
 +        .visit_expr(expr);
 +        format_args
 +    }
 +
-             [] => self.format_string_span,
 +    /// Source callsite span of all inputs
 +    pub fn inputs_span(&self) -> Span {
 +        match *self.value_args {
-                 .format_string_span
-                 .to(hygiene::walk_chain(last.span, self.format_string_span.ctxt())),
++            [] => self.format_string.span,
 +            [.., last] => self
- }
++                .format_string
++                .span
++                .to(hygiene::walk_chain(last.span, self.format_string.span.ctxt())),
 +        }
 +    }
- /// Type representing a `FormatArgsExpn`'s format arguments
- pub struct FormatArgsArg<'tcx> {
-     /// An element of `value_args` according to `position`
-     pub value: &'tcx Expr<'tcx>,
-     /// An element of `args` according to `position`
-     pub format_trait: Symbol,
-     /// An element of `specs`
-     pub spec: Option<&'tcx Expr<'tcx>>,
- }
- impl<'tcx> FormatArgsArg<'tcx> {
-     /// Returns true if any formatting parameters are used that would have an effect on strings,
-     /// like `{:+2}` instead of just `{}`.
-     pub fn has_string_formatting(&self) -> bool {
-         self.spec.map_or(false, |spec| {
-             // `!` because these conditions check that `self` is unformatted.
-             !if_chain! {
-                 // struct `core::fmt::rt::v1::Argument`
-                 if let ExprKind::Struct(_, fields, _) = spec.kind;
-                 if let Some(format_field) = fields.iter().find(|f| f.ident.name == sym::format);
-                 // struct `core::fmt::rt::v1::FormatSpec`
-                 if let ExprKind::Struct(_, subfields, _) = format_field.expr.kind;
-                 if subfields.iter().all(|field| match field.ident.name {
-                     sym::precision | sym::width => match field.expr.kind {
-                         ExprKind::Path(QPath::Resolved(_, path)) => {
-                             path.segments.last().unwrap().ident.name == sym::Implied
-                         }
-                         _ => false,
-                     }
-                     _ => true,
-                 });
-                 then { true } else { false }
-             }
-         })
 +
++    /// Iterator of all format params, both values and those referenced by `width`/`precision`s.
++    pub fn params(&'tcx self) -> impl Iterator<Item = FormatParam<'tcx>> {
++        self.args
++            .iter()
++            .flat_map(|arg| [Some(arg.param), arg.format.precision.param(), arg.format.width.param()])
++            .flatten()
 +    }
 +}
 +
 +/// A node with a `HirId` and a `Span`
 +pub trait HirNode {
 +    fn hir_id(&self) -> HirId;
 +    fn span(&self) -> Span;
 +}
 +
 +macro_rules! impl_hir_node {
 +    ($($t:ident),*) => {
 +        $(impl HirNode for hir::$t<'_> {
 +            fn hir_id(&self) -> HirId {
 +                self.hir_id
 +            }
 +            fn span(&self) -> Span {
 +                self.span
 +            }
 +        })*
 +    };
 +}
 +
 +impl_hir_node!(Expr, Pat);
 +
 +impl HirNode for hir::Item<'_> {
 +    fn hir_id(&self) -> HirId {
 +        self.hir_id()
 +    }
 +
 +    fn span(&self) -> Span {
 +        self.span
 +    }
 +}
index 9e238c6f1ac0ef84a8883d8d6c84d695ccd28cfd,0000000000000000000000000000000000000000..62020e21c81552a98e28c431396cdb18cde6c85d
mode 100644,000000..100644
--- /dev/null
@@@ -1,39 -1,0 +1,39 @@@
-     1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
 +use rustc_semver::RustcVersion;
 +
 +macro_rules! msrv_aliases {
 +    ($($major:literal,$minor:literal,$patch:literal {
 +        $($name:ident),* $(,)?
 +    })*) => {
 +        $($(
 +        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
 +        )*)*
 +    };
 +}
 +
 +// names may refer to stabilized feature flags or library items
 +msrv_aliases! {
 +    1,62,0 { BOOL_THEN_SOME }
-     1,24,0 { IS_ASCII_DIGIT }
++    1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
 +    1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
 +    1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
 +    1,50,0 { BOOL_THEN }
 +    1,47,0 { TAU }
 +    1,46,0 { CONST_IF_MATCH }
 +    1,45,0 { STR_STRIP_PREFIX }
 +    1,43,0 { LOG2_10, LOG10_2 }
 +    1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
 +    1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
 +    1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
 +    1,38,0 { POINTER_CAST, REM_EUCLID }
 +    1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
 +    1,36,0 { ITERATOR_COPIED }
 +    1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
 +    1,34,0 { TRY_FROM }
 +    1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
 +    1,28,0 { FROM_BOOL }
 +    1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
++    1,24,0 { IS_ASCII_DIGIT }
 +    1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
 +    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
 +    1,16,0 { STR_REPEAT }
 +}
index 8d697a301c444c354efd1ee383a5be24eae64099,0000000000000000000000000000000000000000..fb0d34e02eece6b1d67c0ff67f498c36ee137d74
mode 100644,000000..100644
--- /dev/null
@@@ -1,198 -1,0 +1,198 @@@
- #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +//! This module contains paths to types and functions Clippy needs to know
 +//! about.
 +//!
 +//! Whenever possible, please consider diagnostic items over hardcoded paths.
 +//! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 +
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
 +#[cfg(feature = "internal")]
 +pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
 +    ["rustc_lint_defs", "Applicability", "Unspecified"],
 +    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
 +    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
 +    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
 +];
 +#[cfg(feature = "internal")]
 +pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
 +pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
 +pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
 +pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 +pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 +pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 +pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 +pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
 +pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 +pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
 +pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
 +pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
 +pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
 +pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
 +pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"];
 +pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
 +pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 +pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
 +/// Preferably use the diagnostic item `sym::deref_method` where possible
 +pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
 +pub const DIR_BUILDER: [&str; 3] = ["std", "fs", "DirBuilder"];
 +pub const DISPLAY_TRAIT: [&str; 3] = ["core", "fmt", "Display"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
 +#[cfg(feature = "internal")]
 +pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
 +pub const EXIT: [&str; 3] = ["std", "process", "exit"];
 +pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
 +pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
 +pub const FILE: [&str; 3] = ["std", "fs", "File"];
 +pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
 +pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
 +pub const FROM_ITERATOR_METHOD: [&str; 6] = ["core", "iter", "traits", "collect", "FromIterator", "from_iter"];
 +pub const FROM_STR_METHOD: [&str; 5] = ["core", "str", "traits", "FromStr", "from_str"];
 +pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
 +pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 +pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 +pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
 +pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
 +#[cfg(feature = "internal")]
 +pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 +#[cfg(feature = "internal")]
 +pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
 +pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
 +pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
 +pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
 +pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
 +pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 +pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 +pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 +pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
 +pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
 +pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 +pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
 +#[cfg(feature = "internal")]
 +pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 +#[cfg(feature = "internal")]
 +pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
 +#[cfg(feature = "internal")]
 +pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
 +#[cfg(feature = "internal")]
 +pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
 +pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
 +pub const MUTEX_GUARD: [&str; 4] = ["std", "sync", "mutex", "MutexGuard"];
 +pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
 +/// Preferably use the diagnostic item `sym::Option` where possible
 +pub const OPTION: [&str; 3] = ["core", "option", "Option"];
 +pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
 +pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 +pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 +pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 +pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
 +pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 +pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 +pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
 +pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
 +pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
++pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
 +pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
 +#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
 +pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
 +pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
 +pub const POLL_PENDING: [&str; 5] = ["core", "task", "poll", "Poll", "Pending"];
 +pub const POLL_READY: [&str; 5] = ["core", "task", "poll", "Poll", "Ready"];
 +pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
 +pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
 +pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
 +pub const PTR_SLICE_FROM_RAW_PARTS: [&str; 3] = ["core", "ptr", "slice_from_raw_parts"];
 +pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_raw_parts_mut"];
 +pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
 +pub const PTR_READ: [&str; 3] = ["core", "ptr", "read"];
 +pub const PTR_READ_UNALIGNED: [&str; 3] = ["core", "ptr", "read_unaligned"];
 +pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
 +pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 +pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
 +pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
 +pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
 +pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 +pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
 +pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
 +pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
 +pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
 +pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
 +pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
 +pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
 +pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
 +/// Preferably use the diagnostic item `sym::Result` where possible
 +pub const RESULT: [&str; 3] = ["core", "result", "Result"];
 +pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
 +pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
 +#[cfg(feature = "internal")]
 +pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
 +pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
 +pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
 +pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
 +pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
 +pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
 +pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
 +pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
 +pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
 +pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
 +pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 +pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 +pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
 +pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
 +pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
 +pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
 +pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 +pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
 +pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
 +pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
 +pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 +pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 +pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
 +pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 +pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"];
 +#[cfg(feature = "internal")]
 +pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 +#[cfg(feature = "internal")]
 +pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 +#[cfg(feature = "internal")]
 +pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
 +pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
 +pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
 +#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 +pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
 +pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 +pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 +pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
 +pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
 +pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 +pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 +pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
 +pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
 +pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
 +pub const PTR_NON_NULL: [&str; 4] = ["core", "ptr", "non_null", "NonNull"];
 +pub const INSTANT_NOW: [&str; 4] = ["std", "time", "Instant", "now"];
 +pub const INSTANT: [&str; 3] = ["std", "time", "Instant"];
index e7d670766a050203793c22970b12c09bc26c2806,0000000000000000000000000000000000000000..5a7f9568441c90acb857177748168c1f763772ca
mode 100644,000000..100644
--- /dev/null
@@@ -1,841 -1,0 +1,883 @@@
- /// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
- pub fn contains_ty<'tcx>(ty: Ty<'tcx>, other_ty: Ty<'tcx>) -> bool {
-     ty.walk().any(|inner| match inner.unpack() {
-         GenericArgKind::Type(inner_ty) => other_ty == inner_ty,
-         GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-     })
- }
 +//! Util methods for [`rustc_middle::ty`]
 +
 +#![allow(clippy::module_name_repetitions)]
 +
 +use core::ops::ControlFlow;
 +use rustc_ast::ast::Mutability;
 +use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 +use rustc_hir as hir;
 +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 +use rustc_hir::def_id::DefId;
 +use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
 +use rustc_infer::infer::TyCtxtInferExt;
 +use rustc_lint::LateContext;
 +use rustc_middle::mir::interpret::{ConstValue, Scalar};
 +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 +use rustc_middle::ty::{
 +    self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy,
 +    Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
 +};
 +use rustc_span::symbol::Ident;
 +use rustc_span::{sym, Span, Symbol, DUMMY_SP};
 +use rustc_target::abi::{Size, VariantIdx};
 +use rustc_trait_selection::infer::InferCtxtExt;
 +use rustc_trait_selection::traits::query::normalize::AtExt;
 +use std::iter;
 +
 +use crate::{match_def_path, path_res, paths};
 +
 +// Checks if the given type implements copy.
 +pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
 +}
 +
 +/// Checks whether a type can be partially moved.
 +pub fn can_partially_move_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    if has_drop(cx, ty) || is_copy(cx, ty) {
 +        return false;
 +    }
 +    match ty.kind() {
 +        ty::Param(_) => false,
 +        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
 +        _ => true,
 +    }
 +}
 +
- /// Peels off all references on the type.Returns the underlying type, the number of references
 +/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
 +/// constructor.
 +pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
 +    ty.walk().any(|inner| match inner.unpack() {
 +        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
 +        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
 +    })
 +}
 +
 +/// Resolves `<T as Iterator>::Item` for `T`
 +/// Do not invoke without first verifying that the type implements `Iterator`
 +pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .get_diagnostic_item(sym::Iterator)
 +        .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item"))
 +}
 +
 +/// Returns the associated type `name` for `ty` as an implementation of `trait_id`.
 +/// Do not invoke without first verifying that the type implements the trait.
 +pub fn get_associated_type<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    name: &str,
 +) -> Option<Ty<'tcx>> {
 +    cx.tcx
 +        .associated_items(trait_id)
 +        .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
 +        .and_then(|assoc| {
 +            let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
 +            cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok()
 +        })
 +}
 +
 +/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
 +/// implements a trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.get_diagnostic_name(adt.did()),
 +        _ => None,
 +    }
 +}
 +
 +/// Returns true if ty has `iter` or `iter_mut` methods
 +pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
 +    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
 +    // exists and has the desired signature. Unfortunately FnCtxt is not exported
 +    // so we can't use its `lookup_method` method.
 +    let into_iter_collections: &[Symbol] = &[
 +        sym::Vec,
 +        sym::Option,
 +        sym::Result,
 +        sym::BTreeMap,
 +        sym::BTreeSet,
 +        sym::VecDeque,
 +        sym::LinkedList,
 +        sym::BinaryHeap,
 +        sym::HashSet,
 +        sym::HashMap,
 +        sym::PathBuf,
 +        sym::Path,
 +        sym::Receiver,
 +    ];
 +
 +    let ty_to_check = match probably_ref_ty.kind() {
 +        ty::Ref(_, ty_to_check, _) => *ty_to_check,
 +        _ => probably_ref_ty,
 +    };
 +
 +    let def_id = match ty_to_check.kind() {
 +        ty::Array(..) => return Some(sym::array),
 +        ty::Slice(..) => return Some(sym::slice),
 +        ty::Adt(adt, _) => adt.did(),
 +        _ => return None,
 +    };
 +
 +    for &name in into_iter_collections {
 +        if cx.tcx.is_diagnostic_item(name, def_id) {
 +            return Some(cx.tcx.item_name(def_id));
 +        }
 +    }
 +    None
 +}
 +
 +/// Checks whether a type implements a trait.
 +/// The function returns false in case the type contains an inference variable.
 +///
 +/// See:
 +/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
 +/// * [Common tools for writing lints] for an example how to use this function and other options.
 +///
 +/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 +pub fn implements_trait<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
 +    implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params)
 +}
 +
 +/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
 +pub fn implements_trait_with_env<'tcx>(
 +    tcx: TyCtxt<'tcx>,
 +    param_env: ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    trait_id: DefId,
 +    ty_params: &[GenericArg<'tcx>],
 +) -> bool {
 +    // Clippy shouldn't have infer types
 +    assert!(!ty.needs_infer());
 +
 +    let ty = tcx.erase_regions(ty);
 +    if ty.has_escaping_bound_vars() {
 +        return false;
 +    }
 +    let ty_params = tcx.mk_substs(ty_params.iter());
 +    tcx.infer_ctxt().enter(|infcx| {
 +        infcx
 +            .type_implements_trait(trait_id, ty, ty_params, param_env)
 +            .must_apply_modulo_regions()
 +    })
 +}
 +
 +/// Checks whether this type implements `Drop`.
 +pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.ty_adt_def() {
 +        Some(def) => def.has_dtor(cx.tcx),
 +        None => false,
 +    }
 +}
 +
 +// Returns whether the type has #[must_use] attribute
 +pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.has_attr(adt.did(), sym::must_use),
 +        ty::Foreign(did) => cx.tcx.has_attr(*did, sym::must_use),
 +        ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) | ty::Ref(_, ty, _) => {
 +            // for the Array case we don't need to care for the len == 0 case
 +            // because we don't want to lint functions returning empty arrays
 +            is_must_use_ty(cx, *ty)
 +        },
 +        ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
 +        ty::Opaque(def_id, _) => {
 +            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
 +                if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
 +                    if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        ty::Dynamic(binder, _) => {
 +            for predicate in binder.iter() {
 +                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
 +                    if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
 +                        return true;
 +                    }
 +                }
 +            }
 +            false
 +        },
 +        _ => false,
 +    }
 +}
 +
 +// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
 +// this function can be removed once the `normalize` method does not panic when normalization does
 +// not succeed
 +/// Checks if `Ty` is normalizable. This function is useful
 +/// to avoid crashes on `layout_of`.
 +pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
 +    is_normalizable_helper(cx, param_env, ty, &mut FxHashMap::default())
 +}
 +
 +fn is_normalizable_helper<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    param_env: ty::ParamEnv<'tcx>,
 +    ty: Ty<'tcx>,
 +    cache: &mut FxHashMap<Ty<'tcx>, bool>,
 +) -> bool {
 +    if let Some(&cached_result) = cache.get(&ty) {
 +        return cached_result;
 +    }
 +    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
 +    cache.insert(ty, false);
 +    let result = cx.tcx.infer_ctxt().enter(|infcx| {
 +        let cause = rustc_middle::traits::ObligationCause::dummy();
 +        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
 +            match ty.kind() {
 +                ty::Adt(def, substs) => def.variants().iter().all(|variant| {
 +                    variant
 +                        .fields
 +                        .iter()
 +                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
 +                }),
 +                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
 +                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
 +                        is_normalizable_helper(cx, param_env, inner_ty, cache)
 +                    },
 +                    _ => true, // if inner_ty == ty, we've already checked it
 +                }),
 +            }
 +        } else {
 +            false
 +        }
 +    });
 +    cache.insert(ty, result);
 +    result
 +}
 +
 +/// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
 +/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
 +/// tuples and slices of primitive type) see `is_recursively_primitive_type`
 +pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
 +    matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
 +}
 +
 +/// Returns `true` if the given type is a primitive (a `bool` or `char`, any integer or
 +/// floating-point number type, a `str`, or an array, slice, or tuple of those types).
 +pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
 +        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
 +        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
 +        ty::Tuple(inner_types) => inner_types.iter().all(is_recursively_primitive_type),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is a reference equals to a diagnostic item
 +pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
 +            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +            _ => false,
 +        },
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a diagnostic item. To check if a type implements a
 +/// trait marked with a diagnostic item use [`implements_trait`].
 +///
 +/// For a further exploitation what diagnostic items are see [diagnostic items] in
 +/// rustc-dev-guide.
 +///
 +/// ---
 +///
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +///
 +/// [Diagnostic Items]: https://rustc-dev-guide.rust-lang.org/diagnostics/diagnostic-items.html
 +pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the type is equal to a lang item.
 +///
 +/// Returns `false` if the `LangItem` is not defined.
 +pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => cx
 +            .tcx
 +            .lang_items()
 +            .require(lang_item)
 +            .map_or(false, |li| li == adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Return `true` if the passed `typ` is `isize` or `usize`.
 +pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
 +    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 +}
 +
 +/// Checks if type is struct, enum or union type with the given def path.
 +///
 +/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
 +/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
 +pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
 +    match ty.kind() {
 +        ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
 +        _ => false,
 +    }
 +}
 +
 +/// Checks if the drop order for a type matters. Some std types implement drop solely to
 +/// deallocate memory. For these types, and composites containing them, changing the drop order
 +/// won't result in any observable side effects.
 +pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
 +        if !seen.insert(ty) {
 +            return false;
 +        }
 +        if !ty.has_significant_drop(cx.tcx, cx.param_env) {
 +            false
 +        }
 +        // Check for std types which implement drop, but only for memory allocation.
 +        else if is_type_lang_item(cx, ty, LangItem::OwnedBox)
 +            || matches!(
 +                get_type_diagnostic_name(cx, ty),
 +                Some(sym::HashSet | sym::Rc | sym::Arc | sym::cstring_type)
 +            )
 +            || match_type(cx, ty, &paths::WEAK_RC)
 +            || match_type(cx, ty, &paths::WEAK_ARC)
 +        {
 +            // Check all of the generic arguments.
 +            if let ty::Adt(_, subs) = ty.kind() {
 +                subs.types().any(|ty| needs_ordered_drop_inner(cx, ty, seen))
 +            } else {
 +                true
 +            }
 +        } else if !cx
 +            .tcx
 +            .lang_items()
 +            .drop_trait()
 +            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
 +        {
 +            // This type doesn't implement drop, so no side effects here.
 +            // Check if any component type has any.
 +            match ty.kind() {
 +                ty::Tuple(fields) => fields.iter().any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
 +                ty::Array(ty, _) => needs_ordered_drop_inner(cx, *ty, seen),
 +                ty::Adt(adt, subs) => adt
 +                    .all_fields()
 +                    .map(|f| f.ty(cx.tcx, subs))
 +                    .any(|ty| needs_ordered_drop_inner(cx, ty, seen)),
 +                _ => true,
 +            }
 +        } else {
 +            true
 +        }
 +    }
 +
 +    needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 +}
 +
 +/// Peels off all references on the type. Returns the underlying type and the number of references
 +/// removed.
 +pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
 +        if let ty::Ref(_, ty, _) = ty.kind() {
 +            peel(*ty, count + 1)
 +        } else {
 +            (ty, count)
 +        }
 +    }
 +    peel(ty, 0)
 +}
 +
++/// Peels off all references on the type. Returns the underlying type, the number of references
 +/// removed, and whether the pointer is ultimately mutable or not.
 +pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
 +    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
 +            ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
 +            _ => (ty, count, mutability),
 +        }
 +    }
 +    f(ty, 0, Mutability::Mut)
 +}
 +
 +/// Returns `true` if the given type is an `unsafe` function.
 +pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
 +    match ty.kind() {
 +        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
 +        _ => false,
 +    }
 +}
 +
 +/// Returns the base type for HIR references and pointers.
 +pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
 +    match ty.kind {
 +        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
 +        _ => ty,
 +    }
 +}
 +
 +/// Returns the base type for references and raw pointers, and count reference
 +/// depth.
 +pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
 +    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
 +        match ty.kind() {
 +            ty::Ref(_, ty, _) => inner(*ty, depth + 1),
 +            _ => (ty, depth),
 +        }
 +    }
 +    inner(ty, 0)
 +}
 +
 +/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
 +/// otherwise returns `false`
 +pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 +    match (&a.kind(), &b.kind()) {
 +        (&ty::Adt(did_a, substs_a), &ty::Adt(did_b, substs_b)) => {
 +            if did_a != did_b {
 +                return false;
 +            }
 +
 +            substs_a
 +                .iter()
 +                .zip(substs_b.iter())
 +                .all(|(arg_a, arg_b)| match (arg_a.unpack(), arg_b.unpack()) {
 +                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
 +                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
 +                        same_type_and_consts(type_a, type_b)
 +                    },
 +                    _ => true,
 +                })
 +        },
 +        _ => a == b,
 +    }
 +}
 +
 +/// Checks if a given type looks safe to be uninitialized.
 +pub fn is_uninit_value_valid_for_ty(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    match *ty.kind() {
 +        ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component),
 +        ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)),
 +        ty::Adt(adt, _) => cx.tcx.lang_items().maybe_uninit() == Some(adt.did()),
 +        _ => false,
 +    }
 +}
 +
 +/// Gets an iterator over all predicates which apply to the given item.
 +pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
 +    let mut next_id = Some(id);
 +    iter::from_fn(move || {
 +        next_id.take().map(|id| {
 +            let preds = tcx.predicates_of(id);
 +            next_id = preds.parent;
 +            preds.predicates.iter()
 +        })
 +    })
 +    .flatten()
 +}
 +
 +/// A signature for a function like type.
 +#[derive(Clone, Copy)]
 +pub enum ExprFnSig<'tcx> {
 +    Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
 +    Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
 +    Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>, Option<DefId>),
 +}
 +impl<'tcx> ExprFnSig<'tcx> {
 +    /// Gets the argument type at the given offset. This will return `None` when the index is out of
 +    /// bounds only for variadic functions, otherwise this will panic.
 +    pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
 +            Self::Sig(sig, _) => {
 +                if sig.c_variadic() {
 +                    sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
 +                } else {
 +                    Some(sig.input(i))
 +                }
 +            },
 +            Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
 +            Self::Trait(inputs, _, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
 +        }
 +    }
 +
 +    /// Gets the argument type at the given offset. For closures this will also get the type as
 +    /// written. This will return `None` when the index is out of bounds only for variadic
 +    /// functions, otherwise this will panic.
 +    pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
 +        match self {
 +            Self::Sig(sig, _) => {
 +                if sig.c_variadic() {
 +                    sig.inputs()
 +                        .map_bound(|inputs| inputs.get(i).copied())
 +                        .transpose()
 +                        .map(|arg| (None, arg))
 +                } else {
 +                    Some((None, sig.input(i)))
 +                }
 +            },
 +            Self::Closure(decl, sig) => Some((
 +                decl.and_then(|decl| decl.inputs.get(i)),
 +                sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
 +            )),
 +            Self::Trait(inputs, _, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
 +        }
 +    }
 +
 +    /// Gets the result type, if one could be found. Note that the result type of a trait may not be
 +    /// specified.
 +    pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
 +        match self {
 +            Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
 +            Self::Trait(_, output, _) => output,
 +        }
 +    }
 +
 +    pub fn predicates_id(&self) -> Option<DefId> {
 +        if let ExprFnSig::Sig(_, id) | ExprFnSig::Trait(_, _, id) = *self {
 +            id
 +        } else {
 +            None
 +        }
 +    }
 +}
 +
 +/// If the expression is function like, get the signature for it.
 +pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
 +    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
 +        Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
 +    } else {
 +        ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
 +    }
 +}
 +
 +/// If the type is function like, get the signature for it.
 +pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
 +    if ty.is_box() {
 +        return ty_sig(cx, ty.boxed_ty());
 +    }
 +    match *ty.kind() {
 +        ty::Closure(id, subs) => {
 +            let decl = id
 +                .as_local()
 +                .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
 +            Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
 +        },
 +        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
 +        ty::Opaque(id, _) => sig_from_bounds(cx, ty, cx.tcx.item_bounds(id), cx.tcx.opt_parent(id)),
 +        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
 +        ty::Dynamic(bounds, _) => {
 +            let lang_items = cx.tcx.lang_items();
 +            match bounds.principal() {
 +                Some(bound)
 +                    if Some(bound.def_id()) == lang_items.fn_trait()
 +                        || Some(bound.def_id()) == lang_items.fn_once_trait()
 +                        || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
 +                {
 +                    let output = bounds
 +                        .projection_bounds()
 +                        .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
 +                        .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
 +                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output, None))
 +                },
 +                _ => None,
 +            }
 +        },
 +        ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
 +            Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
 +            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
 +        },
 +        ty::Param(_) => sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None),
 +        _ => None,
 +    }
 +}
 +
 +fn sig_from_bounds<'tcx>(
 +    cx: &LateContext<'tcx>,
 +    ty: Ty<'tcx>,
 +    predicates: &'tcx [Predicate<'tcx>],
 +    predicates_id: Option<DefId>,
 +) -> Option<ExprFnSig<'tcx>> {
 +    let mut inputs = None;
 +    let mut output = None;
 +    let lang_items = cx.tcx.lang_items();
 +
 +    for pred in predicates {
 +        match pred.kind().skip_binder() {
 +            PredicateKind::Trait(p)
 +                if (lang_items.fn_trait() == Some(p.def_id())
 +                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                    || lang_items.fn_once_trait() == Some(p.def_id()))
 +                    && p.self_ty() == ty =>
 +            {
 +                let i = pred.kind().rebind(p.trait_ref.substs.type_at(1));
 +                if inputs.map_or(false, |inputs| i != inputs) {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                inputs = Some(i);
 +            },
 +            PredicateKind::Projection(p)
 +                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
 +                    && p.projection_ty.self_ty() == ty =>
 +            {
 +                if output.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                output = Some(pred.kind().rebind(p.term.ty().unwrap()));
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    inputs.map(|ty| ExprFnSig::Trait(ty, output, predicates_id))
 +}
 +
 +fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
 +    let mut inputs = None;
 +    let mut output = None;
 +    let lang_items = cx.tcx.lang_items();
 +
 +    for pred in cx
 +        .tcx
 +        .bound_explicit_item_bounds(ty.item_def_id)
 +        .transpose_iter()
 +        .map(|x| x.map_bound(|(p, _)| p))
 +    {
 +        match pred.0.kind().skip_binder() {
 +            PredicateKind::Trait(p)
 +                if (lang_items.fn_trait() == Some(p.def_id())
 +                    || lang_items.fn_mut_trait() == Some(p.def_id())
 +                    || lang_items.fn_once_trait() == Some(p.def_id())) =>
 +            {
 +                let i = pred
 +                    .map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
 +                    .subst(cx.tcx, ty.substs);
 +
 +                if inputs.map_or(false, |inputs| inputs != i) {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                inputs = Some(i);
 +            },
 +            PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
 +                if output.is_some() {
 +                    // Multiple different fn trait impls. Is this even allowed?
 +                    return None;
 +                }
 +                output = Some(
 +                    pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
 +                        .subst(cx.tcx, ty.substs),
 +                );
 +            },
 +            _ => (),
 +        }
 +    }
 +
 +    inputs.map(|ty| ExprFnSig::Trait(ty, output, None))
 +}
 +
 +#[derive(Clone, Copy)]
 +pub enum EnumValue {
 +    Unsigned(u128),
 +    Signed(i128),
 +}
 +impl core::ops::Add<u32> for EnumValue {
 +    type Output = Self;
 +    fn add(self, n: u32) -> Self::Output {
 +        match self {
 +            Self::Unsigned(x) => Self::Unsigned(x + u128::from(n)),
 +            Self::Signed(x) => Self::Signed(x + i128::from(n)),
 +        }
 +    }
 +}
 +
 +/// Attempts to read the given constant as though it were an an enum value.
 +#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
 +pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
 +    if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
 +        match tcx.type_of(id).kind() {
 +            ty::Int(_) => Some(EnumValue::Signed(match value.size().bytes() {
 +                1 => i128::from(value.assert_bits(Size::from_bytes(1)) as u8 as i8),
 +                2 => i128::from(value.assert_bits(Size::from_bytes(2)) as u16 as i16),
 +                4 => i128::from(value.assert_bits(Size::from_bytes(4)) as u32 as i32),
 +                8 => i128::from(value.assert_bits(Size::from_bytes(8)) as u64 as i64),
 +                16 => value.assert_bits(Size::from_bytes(16)) as i128,
 +                _ => return None,
 +            })),
 +            ty::Uint(_) => Some(EnumValue::Unsigned(match value.size().bytes() {
 +                1 => value.assert_bits(Size::from_bytes(1)),
 +                2 => value.assert_bits(Size::from_bytes(2)),
 +                4 => value.assert_bits(Size::from_bytes(4)),
 +                8 => value.assert_bits(Size::from_bytes(8)),
 +                16 => value.assert_bits(Size::from_bytes(16)),
 +                _ => return None,
 +            })),
 +            _ => None,
 +        }
 +    } else {
 +        None
 +    }
 +}
 +
 +/// Gets the value of the given variant.
 +pub fn get_discriminant_value(tcx: TyCtxt<'_>, adt: AdtDef<'_>, i: VariantIdx) -> EnumValue {
 +    let variant = &adt.variant(i);
 +    match variant.discr {
 +        VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap(),
 +        VariantDiscr::Relative(x) => match adt.variant((i.as_usize() - x as usize).into()).discr {
 +            VariantDiscr::Explicit(id) => read_explicit_enum_value(tcx, id).unwrap() + x,
 +            VariantDiscr::Relative(_) => EnumValue::Unsigned(x.into()),
 +        },
 +    }
 +}
 +
 +/// Check if the given type is either `core::ffi::c_void`, `std::os::raw::c_void`, or one of the
 +/// platform specific `libc::<platform>::c_void` types in libc.
 +pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 +    if let ty::Adt(adt, _) = ty.kind()
 +        && let &[krate, .., name] = &*cx.get_def_path(adt.did())
 +        && let sym::libc | sym::core | sym::std = krate
 +        && name.as_str() == "c_void"
 +    {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +pub fn for_each_top_level_late_bound_region<B>(
 +    ty: Ty<'_>,
 +    f: impl FnMut(BoundRegion) -> ControlFlow<B>,
 +) -> ControlFlow<B> {
 +    struct V<F> {
 +        index: u32,
 +        f: F,
 +    }
 +    impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
 +        type BreakTy = B;
 +        fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
 +            if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
 +                (self.f)(bound)
 +            } else {
 +                ControlFlow::Continue(())
 +            }
 +        }
 +        fn visit_binder<T: TypeVisitable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
 +            self.index += 1;
 +            let res = t.super_visit_with(self);
 +            self.index -= 1;
 +            res
 +        }
 +    }
 +    ty.visit_with(&mut V { index: 0, f })
 +}
 +
 +/// Gets the struct or enum variant from the given `Res`
 +pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
 +    match res {
 +        Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
 +        Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
 +        Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
 +        Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
 +            let var_id = cx.tcx.parent(id);
 +            Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
 +        },
 +        Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
 +        _ => None,
 +    }
 +}
 +
 +/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
 +pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
 +    let ty::Param(ty) = *ty.kind() else {
 +        return false;
 +    };
 +    let lang = tcx.lang_items();
 +    let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
 +        = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
 +    else {
 +        return false;
 +    };
 +    predicates
 +        .iter()
 +        .try_fold(false, |found, p| {
 +            if let PredicateKind::Trait(p) = p.kind().skip_binder()
 +            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
 +            && ty.index == self_ty.index
 +        {
 +            // This should use `super_traits_of`, but that's a private function.
 +            if p.trait_ref.def_id == fn_once_id {
 +                return Some(true);
 +            } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
 +                return None;
 +            }
 +        }
 +            Some(found)
 +        })
 +        .unwrap_or(false)
 +}
++
++/// Comes up with an "at least" guesstimate for the type's size, not taking into
++/// account the layout of type parameters.
++pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
++    use rustc_middle::ty::layout::LayoutOf;
++    if !is_normalizable(cx, cx.param_env, ty) {
++        return 0;
++    }
++    match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
++        (Ok(size), _) => size,
++        (Err(_), ty::Tuple(list)) => list.as_substs().types().map(|t| approx_ty_size(cx, t)).sum(),
++        (Err(_), ty::Array(t, n)) => {
++            n.try_eval_usize(cx.tcx, cx.param_env).unwrap_or_default() * approx_ty_size(cx, *t)
++        },
++        (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
++            .variants()
++            .iter()
++            .map(|v| {
++                v.fields
++                    .iter()
++                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
++                    .sum::<u64>()
++            })
++            .sum(),
++        (Err(_), ty::Adt(def, subst)) if def.is_enum() => def
++            .variants()
++            .iter()
++            .map(|v| {
++                v.fields
++                    .iter()
++                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
++                    .sum::<u64>()
++            })
++            .max()
++            .unwrap_or_default(),
++        (Err(_), ty::Adt(def, subst)) if def.is_union() => def
++            .variants()
++            .iter()
++            .map(|v| {
++                v.fields
++                    .iter()
++                    .map(|field| approx_ty_size(cx, field.ty(cx.tcx, subst)))
++                    .max()
++                    .unwrap_or_default()
++            })
++            .max()
++            .unwrap_or_default(),
++        (Err(_), _) => 0,
++    }
++}
index 7e14df4feea66316953a53425dcde75eee8bf066,0000000000000000000000000000000000000000..85b60fefd60fc4ab84a271c50ebc510bd7449221
mode 100644,000000..100644
--- /dev/null
@@@ -1,3 -1,0 +1,3 @@@
- channel = "nightly-2022-08-11"
 +[toolchain]
++channel = "nightly-2022-08-27"
 +components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index 5f289918a7c1306e8d908c7456e0c922dd36ef37,0000000000000000000000000000000000000000..429dddc42ea9157f6830be6792489b3ab3d08f95
mode 100644,000000..100644
--- /dev/null
@@@ -1,162 -1,0 +1,162 @@@
-         .args(&["rev-parse", "--short", "HEAD"])
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +
 +use std::env;
 +
 +#[macro_export]
 +macro_rules! get_version_info {
 +    () => {{
 +        let major = env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
 +        let minor = env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
 +        let patch = env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
 +        let crate_name = String::from(env!("CARGO_PKG_NAME"));
 +
 +        let host_compiler = option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
 +        let commit_hash = option_env!("GIT_HASH").map(str::to_string);
 +        let commit_date = option_env!("COMMIT_DATE").map(str::to_string);
 +
 +        VersionInfo {
 +            major,
 +            minor,
 +            patch,
 +            host_compiler,
 +            commit_hash,
 +            commit_date,
 +            crate_name,
 +        }
 +    }};
 +}
 +
 +// some code taken and adapted from RLS and cargo
 +pub struct VersionInfo {
 +    pub major: u8,
 +    pub minor: u8,
 +    pub patch: u16,
 +    pub host_compiler: Option<String>,
 +    pub commit_hash: Option<String>,
 +    pub commit_date: Option<String>,
 +    pub crate_name: String,
 +}
 +
 +impl std::fmt::Display for VersionInfo {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        let hash = self.commit_hash.clone().unwrap_or_default();
 +        let hash_trimmed = hash.trim();
 +
 +        let date = self.commit_date.clone().unwrap_or_default();
 +        let date_trimmed = date.trim();
 +
 +        if (hash_trimmed.len() + date_trimmed.len()) > 0 {
 +            write!(
 +                f,
 +                "{} {}.{}.{} ({} {})",
 +                self.crate_name, self.major, self.minor, self.patch, hash_trimmed, date_trimmed,
 +            )?;
 +        } else {
 +            write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
 +        }
 +
 +        Ok(())
 +    }
 +}
 +
 +impl std::fmt::Debug for VersionInfo {
 +    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 +        write!(
 +            f,
 +            "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
 +            self.crate_name, self.major, self.minor, self.patch,
 +        )?;
 +        if self.commit_hash.is_some() {
 +            write!(
 +                f,
 +                ", commit_hash: \"{}\", commit_date: \"{}\" }}",
 +                self.commit_hash.clone().unwrap_or_default().trim(),
 +                self.commit_date.clone().unwrap_or_default().trim()
 +            )?;
 +        } else {
 +            write!(f, " }}")?;
 +        }
 +
 +        Ok(())
 +    }
 +}
 +
 +#[must_use]
 +pub fn get_commit_hash() -> Option<String> {
 +    std::process::Command::new("git")
-         .args(&["log", "-1", "--date=short", "--pretty=format:%cd"])
++        .args(["rev-parse", "--short", "HEAD"])
 +        .output()
 +        .ok()
 +        .and_then(|r| String::from_utf8(r.stdout).ok())
 +}
 +
 +#[must_use]
 +pub fn get_commit_date() -> Option<String> {
 +    std::process::Command::new("git")
++        .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
 +        .output()
 +        .ok()
 +        .and_then(|r| String::from_utf8(r.stdout).ok())
 +}
 +
 +#[must_use]
 +pub fn get_channel() -> String {
 +    match env::var("CFG_RELEASE_CHANNEL") {
 +        Ok(channel) => channel,
 +        Err(_) => {
 +            // if that failed, try to ask rustc -V, do some parsing and find out
 +            match std::process::Command::new("rustc")
 +                .arg("-V")
 +                .output()
 +                .ok()
 +                .and_then(|r| String::from_utf8(r.stdout).ok())
 +            {
 +                Some(rustc_output) => {
 +                    if rustc_output.contains("beta") {
 +                        String::from("beta")
 +                    } else if rustc_output.contains("stable") {
 +                        String::from("stable")
 +                    } else {
 +                        // default to nightly if we fail to parse
 +                        String::from("nightly")
 +                    }
 +                },
 +                // default to nightly
 +                None => String::from("nightly"),
 +            }
 +        },
 +    }
 +}
 +
 +#[cfg(test)]
 +mod test {
 +    use super::*;
 +
 +    #[test]
 +    fn test_struct_local() {
 +        let vi = get_version_info!();
 +        assert_eq!(vi.major, 0);
 +        assert_eq!(vi.minor, 2);
 +        assert_eq!(vi.patch, 0);
 +        assert_eq!(vi.crate_name, "rustc_tools_util");
 +        // hard to make positive tests for these since they will always change
 +        assert!(vi.commit_hash.is_none());
 +        assert!(vi.commit_date.is_none());
 +    }
 +
 +    #[test]
 +    fn test_display_local() {
 +        let vi = get_version_info!();
 +        assert_eq!(vi.to_string(), "rustc_tools_util 0.2.0");
 +    }
 +
 +    #[test]
 +    fn test_debug_local() {
 +        let vi = get_version_info!();
 +        let s = format!("{:?}", vi);
 +        assert_eq!(
 +            s,
 +            "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 2, patch: 0 }"
 +        );
 +    }
 +}
index 0defd45b68b064745537b02321f5bb2b56c688aa,0000000000000000000000000000000000000000..e106583de4a2eb0a099d145cafe71b60453c2ef2
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,28 @@@
-         .args(&["dev", "fmt", "--check"])
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::path::PathBuf;
 +use std::process::Command;
 +
 +#[test]
 +fn fmt() {
 +    if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() {
 +        return;
 +    }
 +
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +    let output = Command::new("cargo")
 +        .current_dir(root_dir)
++        .args(["dev", "fmt", "--check"])
 +        .output()
 +        .unwrap();
 +
 +    println!("status: {}", output.status);
 +    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +    assert!(
 +        output.status.success(),
 +        "Formatting check failed. Run `cargo dev fmt` to update formatting."
 +    );
 +}
index 92ac1a2be56142e896c4ea0a9226055bbda56ea3,0000000000000000000000000000000000000000..ba6186e599e9637f0481df911ab18085ee3a441f
mode 100644,000000..100644
--- /dev/null
@@@ -1,509 -1,0 +1,516 @@@
-     "trait_duplication_in_bounds.rs",
 +#![feature(test)] // compiletest_rs requires this attribute
 +#![feature(once_cell)]
 +#![feature(is_sorted)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use compiletest_rs as compiletest;
 +use compiletest_rs::common::Mode as TestMode;
 +
 +use std::collections::HashMap;
 +use std::env::{self, remove_var, set_var, var_os};
 +use std::ffi::{OsStr, OsString};
 +use std::fs;
 +use std::io;
 +use std::path::{Path, PathBuf};
 +use std::sync::LazyLock;
 +use test_utils::IS_RUSTC_TEST_SUITE;
 +
 +mod test_utils;
 +
 +// whether to run internal tests or not
 +const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 +
 +/// All crates used in UI tests are listed here
 +static TEST_DEPENDENCIES: &[&str] = &[
 +    "clippy_lints",
 +    "clippy_utils",
 +    "derive_new",
 +    "futures",
 +    "if_chain",
 +    "itertools",
 +    "quote",
 +    "regex",
 +    "serde",
 +    "serde_derive",
 +    "syn",
 +    "tokio",
 +    "parking_lot",
 +    "rustc_semver",
 +];
 +
 +// Test dependencies may need an `extern crate` here to ensure that they show up
 +// in the depinfo file (otherwise cargo thinks they are unused)
 +#[allow(unused_extern_crates)]
 +extern crate clippy_lints;
 +#[allow(unused_extern_crates)]
 +extern crate clippy_utils;
 +#[allow(unused_extern_crates)]
 +extern crate derive_new;
 +#[allow(unused_extern_crates)]
 +extern crate futures;
 +#[allow(unused_extern_crates)]
 +extern crate if_chain;
 +#[allow(unused_extern_crates)]
 +extern crate itertools;
 +#[allow(unused_extern_crates)]
 +extern crate parking_lot;
 +#[allow(unused_extern_crates)]
 +extern crate quote;
 +#[allow(unused_extern_crates)]
 +extern crate rustc_semver;
 +#[allow(unused_extern_crates)]
 +extern crate syn;
 +#[allow(unused_extern_crates)]
 +extern crate tokio;
 +
 +/// Produces a string with an `--extern` flag for all UI test crate
 +/// dependencies.
 +///
 +/// The dependency files are located by parsing the depinfo file for this test
 +/// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
 +/// dependencies must be added to Cargo.toml at the project root. Test
 +/// dependencies that are not *directly* used by this test module require an
 +/// `extern crate` declaration.
 +static EXTERN_FLAGS: LazyLock<String> = LazyLock::new(|| {
 +    let current_exe_depinfo = {
 +        let mut path = env::current_exe().unwrap();
 +        path.set_extension("d");
 +        fs::read_to_string(path).unwrap()
 +    };
 +    let mut crates: HashMap<&str, &str> = HashMap::with_capacity(TEST_DEPENDENCIES.len());
 +    for line in current_exe_depinfo.lines() {
 +        // each dependency is expected to have a Makefile rule like `/path/to/crate-hash.rlib:`
 +        let parse_name_path = || {
 +            if line.starts_with(char::is_whitespace) {
 +                return None;
 +            }
 +            let path_str = line.strip_suffix(':')?;
 +            let path = Path::new(path_str);
 +            if !matches!(path.extension()?.to_str()?, "rlib" | "so" | "dylib" | "dll") {
 +                return None;
 +            }
 +            let (name, _hash) = path.file_stem()?.to_str()?.rsplit_once('-')?;
 +            // the "lib" prefix is not present for dll files
 +            let name = name.strip_prefix("lib").unwrap_or(name);
 +            Some((name, path_str))
 +        };
 +        if let Some((name, path)) = parse_name_path() {
 +            if TEST_DEPENDENCIES.contains(&name) {
 +                // A dependency may be listed twice if it is available in sysroot,
 +                // and the sysroot dependencies are listed first. As of the writing,
 +                // this only seems to apply to if_chain.
 +                crates.insert(name, path);
 +            }
 +        }
 +    }
 +    let not_found: Vec<&str> = TEST_DEPENDENCIES
 +        .iter()
 +        .copied()
 +        .filter(|n| !crates.contains_key(n))
 +        .collect();
 +    assert!(
 +        not_found.is_empty(),
 +        "dependencies not found in depinfo: {:?}\n\
 +        help: Make sure the `-Z binary-dep-depinfo` rust flag is enabled\n\
 +        help: Try adding to dev-dependencies in Cargo.toml\n\
 +        help: Be sure to also add `extern crate ...;` to tests/compile-test.rs",
 +        not_found,
 +    );
 +    crates
 +        .into_iter()
 +        .map(|(name, path)| format!(" --extern {}={}", name, path))
 +        .collect()
 +});
 +
 +fn base_config(test_dir: &str) -> compiletest::Config {
 +    let mut config = compiletest::Config {
 +        edition: Some("2021".into()),
 +        mode: TestMode::Ui,
 +        ..Default::default()
 +    };
 +
 +    if let Ok(filters) = env::var("TESTNAME") {
 +        config.filters = filters.split(',').map(ToString::to_string).collect();
 +    }
 +
 +    if let Some(path) = option_env!("RUSTC_LIB_PATH") {
 +        let path = PathBuf::from(path);
 +        config.run_lib_path = path.clone();
 +        config.compile_lib_path = path;
 +    }
 +    let current_exe_path = env::current_exe().unwrap();
 +    let deps_path = current_exe_path.parent().unwrap();
 +    let profile_path = deps_path.parent().unwrap();
 +
 +    // Using `-L dependency={}` enforces that external dependencies are added with `--extern`.
 +    // This is valuable because a) it allows us to monitor what external dependencies are used
 +    // and b) it ensures that conflicting rlibs are resolved properly.
 +    let host_libs = option_env!("HOST_LIBS")
 +        .map(|p| format!(" -L dependency={}", Path::new(p).join("deps").display()))
 +        .unwrap_or_default();
 +    config.target_rustcflags = Some(format!(
 +        "--emit=metadata -Dwarnings -Zui-testing -L dependency={}{}{}",
 +        deps_path.display(),
 +        host_libs,
 +        &*EXTERN_FLAGS,
 +    ));
 +
 +    config.src_base = Path::new("tests").join(test_dir);
 +    config.build_base = profile_path.join("test").join(test_dir);
 +    config.rustc_path = profile_path.join(if cfg!(windows) {
 +        "clippy-driver.exe"
 +    } else {
 +        "clippy-driver"
 +    });
 +    config
 +}
 +
 +fn run_ui() {
 +    let mut config = base_config("ui");
 +    config.rustfix_coverage = true;
 +    // use tests/clippy.toml
 +    let _g = VarGuard::set("CARGO_MANIFEST_DIR", fs::canonicalize("tests").unwrap());
 +    let _threads = VarGuard::set(
 +        "RUST_TEST_THREADS",
 +        // if RUST_TEST_THREADS is set, adhere to it, otherwise override it
 +        env::var("RUST_TEST_THREADS").unwrap_or_else(|_| {
 +            std::thread::available_parallelism()
 +                .map_or(1, std::num::NonZeroUsize::get)
 +                .to_string()
 +        }),
 +    );
 +    compiletest::run_tests(&config);
 +    check_rustfix_coverage();
 +}
 +
 +fn run_internal_tests() {
 +    // only run internal tests with the internal-tests feature
 +    if !RUN_INTERNAL_TESTS {
 +        return;
 +    }
 +    let config = base_config("ui-internal");
 +    compiletest::run_tests(&config);
 +}
 +
 +fn run_ui_toml() {
 +    fn run_tests(config: &compiletest::Config, mut tests: Vec<tester::TestDescAndFn>) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +            let dir_path = dir.path();
 +            let _g = VarGuard::set("CARGO_MANIFEST_DIR", &dir_path);
 +            for file in fs::read_dir(&dir_path)? {
 +                let file = file?;
 +                let file_path = file.path();
 +                if file.file_type()?.is_dir() {
 +                    continue;
 +                }
 +                if file_path.extension() != Some(OsStr::new("rs")) {
 +                    continue;
 +                }
 +                let paths = compiletest::common::TestPaths {
 +                    file: file_path,
 +                    base: config.src_base.clone(),
 +                    relative_dir: dir_path.file_name().unwrap().into(),
 +                };
 +                let test_name = compiletest::make_test_name(config, &paths);
 +                let index = tests
 +                    .iter()
 +                    .position(|test| test.desc.name == test_name)
 +                    .expect("The test should be in there");
 +                result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    let mut config = base_config("ui-toml");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let res = run_tests(&config, tests);
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +fn run_ui_cargo() {
 +    fn run_tests(
 +        config: &compiletest::Config,
 +        filters: &[String],
 +        mut tests: Vec<tester::TestDescAndFn>,
 +    ) -> Result<bool, io::Error> {
 +        let mut result = true;
 +        let opts = compiletest::test_opts(config);
 +
 +        for dir in fs::read_dir(&config.src_base)? {
 +            let dir = dir?;
 +            if !dir.file_type()?.is_dir() {
 +                continue;
 +            }
 +
 +            // Use the filter if provided
 +            let dir_path = dir.path();
 +            for filter in filters {
 +                if !dir_path.ends_with(filter) {
 +                    continue;
 +                }
 +            }
 +
 +            for case in fs::read_dir(&dir_path)? {
 +                let case = case?;
 +                if !case.file_type()?.is_dir() {
 +                    continue;
 +                }
 +
 +                let src_path = case.path().join("src");
 +
 +                // When switching between branches, if the previous branch had a test
 +                // that the current branch does not have, the directory is not removed
 +                // because an ignored Cargo.lock file exists.
 +                if !src_path.exists() {
 +                    continue;
 +                }
 +
 +                env::set_current_dir(&src_path)?;
 +
 +                let cargo_toml_path = case.path().join("Cargo.toml");
 +                let cargo_content = fs::read(&cargo_toml_path)?;
 +                let cargo_parsed: toml::Value = toml::from_str(
 +                    std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"),
 +                )
 +                .expect("Can't parse `Cargo.toml`");
 +
 +                let _g = VarGuard::set("CARGO_MANIFEST_DIR", case.path());
 +                let _h = VarGuard::set(
 +                    "CARGO_PKG_RUST_VERSION",
 +                    cargo_parsed
 +                        .get("package")
 +                        .and_then(|p| p.get("rust-version"))
 +                        .and_then(toml::Value::as_str)
 +                        .unwrap_or(""),
 +                );
 +
 +                for file in fs::read_dir(&src_path)? {
 +                    let file = file?;
 +                    if file.file_type()?.is_dir() {
 +                        continue;
 +                    }
 +
 +                    // Search for the main file to avoid running a test for each file in the project
 +                    let file_path = file.path();
 +                    match file_path.file_name().and_then(OsStr::to_str) {
 +                        Some("main.rs") => {},
 +                        _ => continue,
 +                    }
 +                    let _g = VarGuard::set("CLIPPY_CONF_DIR", case.path());
 +                    let paths = compiletest::common::TestPaths {
 +                        file: file_path,
 +                        base: config.src_base.clone(),
 +                        relative_dir: src_path.strip_prefix(&config.src_base).unwrap().into(),
 +                    };
 +                    let test_name = compiletest::make_test_name(config, &paths);
 +                    let index = tests
 +                        .iter()
 +                        .position(|test| test.desc.name == test_name)
 +                        .expect("The test should be in there");
 +                    result &= tester::run_tests_console(&opts, vec![tests.swap_remove(index)])?;
 +                }
 +            }
 +        }
 +        Ok(result)
 +    }
 +
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +
 +    let mut config = base_config("ui-cargo");
 +    config.src_base = config.src_base.canonicalize().unwrap();
 +
 +    let tests = compiletest::make_tests(&config);
 +
 +    let current_dir = env::current_dir().unwrap();
 +    let res = run_tests(&config, &config.filters, tests);
 +    env::set_current_dir(current_dir).unwrap();
 +
 +    match res {
 +        Ok(true) => {},
 +        Ok(false) => panic!("Some tests failed"),
 +        Err(e) => {
 +            panic!("I/O failure during tests: {:?}", e);
 +        },
 +    }
 +}
 +
 +#[test]
 +fn compile_test() {
 +    set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
 +    run_ui();
 +    run_ui_toml();
 +    run_ui_cargo();
 +    run_internal_tests();
 +}
 +
 +const RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS: &[&str] = &[
 +    "assign_ops2.rs",
 +    "borrow_deref_ref_unfixable.rs",
 +    "cast_size_32bit.rs",
 +    "char_lit_as_u8.rs",
 +    "cmp_owned/without_suggestion.rs",
 +    "dbg_macro.rs",
 +    "deref_addrof_double_trigger.rs",
 +    "doc/unbalanced_ticks.rs",
 +    "eprint_with_newline.rs",
 +    "explicit_counter_loop.rs",
 +    "iter_skip_next_unfixable.rs",
 +    "let_and_return.rs",
 +    "literals.rs",
 +    "map_flatten.rs",
 +    "map_unwrap_or.rs",
 +    "match_bool.rs",
 +    "mem_replace_macro.rs",
 +    "needless_arbitrary_self_type_unfixable.rs",
 +    "needless_borrow_pat.rs",
 +    "needless_for_each_unfixable.rs",
 +    "nonminimal_bool.rs",
 +    "print_literal.rs",
 +    "print_with_newline.rs",
 +    "redundant_static_lifetimes_multiple.rs",
 +    "ref_binding_to_reference.rs",
 +    "repl_uninit.rs",
 +    "result_map_unit_fn_unfixable.rs",
 +    "search_is_some.rs",
 +    "single_component_path_imports_nested_first.rs",
 +    "string_add.rs",
++    "suspicious_to_owned.rs",
 +    "toplevel_ref_arg_non_rustfix.rs",
-     let missing_coverage_path = Path::new("target/debug/test/ui/rustfix_missing_coverage.txt");
 +    "unit_arg.rs",
 +    "unnecessary_clone.rs",
 +    "unnecessary_lazy_eval_unfixable.rs",
 +    "write_literal.rs",
 +    "write_literal_2.rs",
 +    "write_with_newline.rs",
 +];
 +
 +fn check_rustfix_coverage() {
-         for rs_path in missing_coverage_contents.lines() {
-             if Path::new(rs_path).starts_with("tests/ui/crashes") {
++    let missing_coverage_path = Path::new("debug/test/ui/rustfix_missing_coverage.txt");
++    let missing_coverage_path = if let Ok(target_dir) = std::env::var("CARGO_TARGET_DIR") {
++        PathBuf::from(target_dir).join(missing_coverage_path)
++    } else {
++        missing_coverage_path.to_path_buf()
++    };
 +
 +    if let Ok(missing_coverage_contents) = std::fs::read_to_string(missing_coverage_path) {
 +        assert!(RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS.iter().is_sorted_by_key(Path::new));
 +
-             let filename = Path::new(rs_path).strip_prefix("tests/ui/").unwrap();
++        for rs_file in missing_coverage_contents.lines() {
++            let rs_path = Path::new(rs_file);
++            if rs_path.starts_with("tests/ui/crashes") {
 +                continue;
 +            }
-                 rs_path,
++            assert!(rs_path.starts_with("tests/ui/"), "{:?}", rs_file);
++            let filename = rs_path.strip_prefix("tests/ui/").unwrap();
 +            assert!(
 +                RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS
 +                    .binary_search_by_key(&filename, Path::new)
 +                    .is_ok(),
 +                "`{}` runs `MachineApplicable` diagnostics but is missing a `run-rustfix` annotation. \
 +                Please either add `// run-rustfix` at the top of the file or add the file to \
 +                `RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS` in `tests/compile-test.rs`.",
++                rs_file,
 +            );
 +        }
 +    }
 +}
 +
 +#[test]
 +fn rustfix_coverage_known_exceptions_accuracy() {
 +    for filename in RUSTFIX_COVERAGE_KNOWN_EXCEPTIONS {
 +        let rs_path = Path::new("tests/ui").join(filename);
 +        assert!(
 +            rs_path.exists(),
 +            "`{}` does not exist",
 +            rs_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
 +        );
 +        let fixed_path = rs_path.with_extension("fixed");
 +        assert!(
 +            !fixed_path.exists(),
 +            "`{}` exists",
 +            fixed_path.strip_prefix(env!("CARGO_MANIFEST_DIR")).unwrap().display()
 +        );
 +    }
 +}
 +
 +#[test]
 +fn ui_cargo_toml_metadata() {
 +    let ui_cargo_path = Path::new("tests/ui-cargo");
 +    let cargo_common_metadata_path = ui_cargo_path.join("cargo_common_metadata");
 +    let publish_exceptions =
 +        ["fail_publish", "fail_publish_true", "pass_publish_empty"].map(|path| cargo_common_metadata_path.join(path));
 +
 +    for entry in walkdir::WalkDir::new(ui_cargo_path) {
 +        let entry = entry.unwrap();
 +        let path = entry.path();
 +        if path.file_name() != Some(OsStr::new("Cargo.toml")) {
 +            continue;
 +        }
 +
 +        let toml = fs::read_to_string(path).unwrap().parse::<toml::Value>().unwrap();
 +
 +        let package = toml.as_table().unwrap().get("package").unwrap().as_table().unwrap();
 +
 +        let name = package.get("name").unwrap().as_str().unwrap().replace('-', "_");
 +        assert!(
 +            path.parent()
 +                .unwrap()
 +                .components()
 +                .map(|component| component.as_os_str().to_string_lossy().replace('-', "_"))
 +                .any(|s| *s == name)
 +                || path.starts_with(&cargo_common_metadata_path),
 +            "{:?} has incorrect package name",
 +            path
 +        );
 +
 +        let publish = package.get("publish").and_then(toml::Value::as_bool).unwrap_or(true);
 +        assert!(
 +            !publish || publish_exceptions.contains(&path.parent().unwrap().to_path_buf()),
 +            "{:?} lacks `publish = false`",
 +            path
 +        );
 +    }
 +}
 +
 +/// 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 5697e8680cd6f86779a303edfd5b63d940e8da14,0000000000000000000000000000000000000000..961525bbd9101dfbc6759f69ba3f05d7aa94e170
mode 100644,000000..100644
--- /dev/null
@@@ -1,104 -1,0 +1,104 @@@
-         command.args(&["-D", "clippy::internal"]);
 +//! This test is a part of quality control and makes clippy eat what it produces. Awesome lints and
 +//! long error messages
 +//!
 +//! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
 +
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::path::PathBuf;
 +use std::process::Command;
 +use test_utils::IS_RUSTC_TEST_SUITE;
 +
 +mod test_utils;
 +
 +#[test]
 +fn dogfood_clippy() {
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +
 +    // "" is the root package
 +    for package in &["", "clippy_dev", "clippy_lints", "clippy_utils", "rustc_tools_util"] {
 +        run_clippy_for_package(package, &["-D", "clippy::all", "-D", "clippy::pedantic"]);
 +    }
 +}
 +
 +#[test]
 +#[ignore]
 +#[cfg(feature = "internal")]
 +fn run_metadata_collection_lint() {
 +    use std::fs::File;
 +    use std::time::SystemTime;
 +
 +    // Setup for validation
 +    let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json");
 +    let start_time = SystemTime::now();
 +
 +    // Run collection as is
 +    std::env::set_var("ENABLE_METADATA_COLLECTION", "1");
 +    run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 +
 +    // Check if cargo caching got in the way
 +    if let Ok(file) = File::open(metadata_output_path) {
 +        if let Ok(metadata) = file.metadata() {
 +            if let Ok(last_modification) = metadata.modified() {
 +                if last_modification > start_time {
 +                    // The output file has been modified. Most likely by a hungry
 +                    // metadata collection monster. So We'll return.
 +                    return;
 +                }
 +            }
 +        }
 +    }
 +
 +    // Force cargo to invalidate the caches
 +    filetime::set_file_mtime(
 +        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"),
 +        filetime::FileTime::now(),
 +    )
 +    .unwrap();
 +
 +    // Running the collection again
 +    run_clippy_for_package("clippy_lints", &["-A", "unfulfilled_lint_expectations"]);
 +}
 +
 +fn run_clippy_for_package(project: &str, args: &[&str]) {
 +    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +
 +    let mut command = Command::new(&*test_utils::CARGO_CLIPPY_PATH);
 +
 +    command
 +        .current_dir(root_dir.join(project))
 +        .env("CARGO_INCREMENTAL", "0")
 +        .arg("clippy")
 +        .arg("--all-targets")
 +        .arg("--all-features");
 +
 +    if let Ok(dogfood_args) = std::env::var("__CLIPPY_DOGFOOD_ARGS") {
 +        for arg in dogfood_args.split_whitespace() {
 +            command.arg(arg);
 +        }
 +    }
 +
 +    command.arg("--").args(args);
 +    command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 +
 +    if cfg!(feature = "internal") {
 +        // internal lints only exist if we build with the internal feature
-         command.args(&["-A", "unknown_lints"]);
++        command.args(["-D", "clippy::internal"]);
 +    } else {
 +        // running a clippy built without internal lints on the clippy source
 +        // that contains e.g. `allow(clippy::invalid_paths)`
++        command.args(["-A", "unknown_lints"]);
 +    }
 +
 +    let output = command.output().unwrap();
 +
 +    println!("status: {}", output.status);
 +    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +    assert!(output.status.success());
 +}
index c64425fa01a42a36b8351e934ae81523d75b7129,0000000000000000000000000000000000000000..23a9bef3ccceaee4b9df52ecb40afb848e8f1873
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,89 @@@
-         .args(&[
 +#![cfg(feature = "integration")]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::env;
 +use std::ffi::OsStr;
 +use std::process::Command;
 +
 +#[cfg_attr(feature = "integration", test)]
 +fn integration_test() {
 +    let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
 +    let repo_url = format!("https://github.com/{}", repo_name);
 +    let crate_name = repo_name
 +        .split('/')
 +        .nth(1)
 +        .expect("repo name should have format `<org>/<name>`");
 +
 +    let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path();
 +    repo_dir.push(crate_name);
 +
 +    let st = Command::new("git")
-         .args(&[
++        .args([
 +            OsStr::new("clone"),
 +            OsStr::new("--depth=1"),
 +            OsStr::new(&repo_url),
 +            OsStr::new(&repo_dir),
 +        ])
 +        .status()
 +        .expect("unable to run git");
 +    assert!(st.success());
 +
 +    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +    let target_dir = std::path::Path::new(&root_dir).join("target");
 +    let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");
 +
 +    let output = Command::new(clippy_binary)
 +        .current_dir(repo_dir)
 +        .env("RUST_BACKTRACE", "full")
 +        .env("CARGO_TARGET_DIR", target_dir)
++        .args([
 +            "clippy",
 +            "--all-targets",
 +            "--all-features",
 +            "--",
 +            "--cap-lints",
 +            "warn",
 +            "-Wclippy::pedantic",
 +            "-Wclippy::nursery",
 +        ])
 +        .output()
 +        .expect("unable to run clippy");
 +
 +    let stderr = String::from_utf8_lossy(&output.stderr);
 +    if stderr.contains("internal compiler error") {
 +        let backtrace_start = stderr
 +            .find("thread 'rustc' panicked at")
 +            .expect("start of backtrace not found");
 +        let backtrace_end = stderr
 +            .rfind("error: internal compiler error")
 +            .expect("end of backtrace not found");
 +
 +        panic!(
 +            "internal compiler error\nBacktrace:\n\n{}",
 +            &stderr[backtrace_start..backtrace_end]
 +        );
 +    } else if stderr.contains("query stack during panic") {
 +        panic!("query stack during panic in the output");
 +    } else if stderr.contains("E0463") {
 +        // Encountering E0463 (can't find crate for `x`) did _not_ cause the build to fail in the
 +        // past. Even though it should have. That's why we explicitly panic here.
 +        // See PR #3552 and issue #3523 for more background.
 +        panic!("error: E0463");
 +    } else if stderr.contains("E0514") {
 +        panic!("incompatible crate versions");
 +    } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
 +        panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
 +    } else {
 +        assert!(
 +            !stderr.contains("toolchain") || !stderr.contains("is not installed"),
 +            "missing required toolchain"
 +        );
 +    }
 +
 +    match output.status.code() {
 +        Some(0) => println!("Compilation successful"),
 +        Some(code) => eprintln!("Compilation failed. Exit code: {}", code),
 +        None => panic!("Process terminated by signal"),
 +    }
 +}
index c3aae1a9aa2d01992ee0c00b6c847bc869b033d0,0000000000000000000000000000000000000000..2e0f4e76075b38a1eaec8e6380001dcd5109886a
mode 100644,000000..100644
--- /dev/null
@@@ -1,116 -1,0 +1,116 @@@
-             RegexSet::new(&[
 +#![feature(once_cell)]
 +#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 +#![warn(rust_2018_idioms, unused_lifetimes)]
 +
 +use std::ffi::OsStr;
 +use std::path::PathBuf;
 +use std::sync::LazyLock;
 +
 +use regex::RegexSet;
 +
 +#[derive(Debug)]
 +struct Message {
 +    path: PathBuf,
 +    bad_lines: Vec<String>,
 +}
 +
 +impl Message {
 +    fn new(path: PathBuf) -> Self {
 +        // we don't want the first letter after "error: ", "help: " ... to be capitalized
 +        // also no punctuation (except for "?" ?) at the end of a line
 +        static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
-             RegexSet::new(&[
++            RegexSet::new([
 +                r"error: [A-Z]",
 +                r"help: [A-Z]",
 +                r"warning: [A-Z]",
 +                r"note: [A-Z]",
 +                r"try this: [A-Z]",
 +                r"error: .*[.!]$",
 +                r"help: .*[.!]$",
 +                r"warning: .*[.!]$",
 +                r"note: .*[.!]$",
 +                r"try this: .*[.!]$",
 +            ])
 +            .unwrap()
 +        });
 +
 +        // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
 +        // we want to ask a question ending in "?"
 +        static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
++            RegexSet::new([
 +                r"\.\.\.$",
 +                r".*C-like enum variant discriminant is not portable to 32-bit targets",
 +                r".*Intel x86 assembly syntax used",
 +                r".*AT&T x86 assembly syntax used",
 +                r"note: Clippy version: .*",
 +                r"the compiler unexpectedly panicked. this is a bug.",
 +            ])
 +            .unwrap()
 +        });
 +
 +        let content: String = std::fs::read_to_string(&path).unwrap();
 +
 +        let bad_lines = content
 +            .lines()
 +            .filter(|line| REGEX_SET.matches(line).matched_any())
 +            // ignore exceptions
 +            .filter(|line| !EXCEPTIONS_SET.matches(line).matched_any())
 +            .map(ToOwned::to_owned)
 +            .collect::<Vec<String>>();
 +
 +        Message { path, bad_lines }
 +    }
 +}
 +
 +#[test]
 +fn lint_message_convention() {
 +    // disable the test inside the rustc test suite
 +    if option_env!("RUSTC_TEST_SUITE").is_some() {
 +        return;
 +    }
 +
 +    // make sure that lint messages:
 +    // * are not capitalized
 +    // * don't have punctuation at the end of the last sentence
 +
 +    // these directories have interesting tests
 +    let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"]
 +        .iter()
 +        .map(PathBuf::from)
 +        .map(|p| {
 +            let base = PathBuf::from("tests");
 +            base.join(p)
 +        });
 +
 +    // gather all .stderr files
 +    let tests = test_dirs
 +        .flat_map(|dir| {
 +            std::fs::read_dir(dir)
 +                .expect("failed to read dir")
 +                .map(|direntry| direntry.unwrap().path())
 +        })
 +        .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr"))));
 +
 +    // get all files that have any "bad lines" in them
 +    let bad_tests: Vec<Message> = tests
 +        .map(Message::new)
 +        .filter(|message| !message.bad_lines.is_empty())
 +        .collect();
 +
 +    for message in &bad_tests {
 +        eprintln!(
 +            "error: the test '{}' contained the following nonconforming lines :",
 +            message.path.display()
 +        );
 +        message.bad_lines.iter().for_each(|line| eprintln!("{}", line));
 +        eprintln!("\n\n");
 +    }
 +
 +    eprintln!(
 +        "\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed."
 +    );
 +    eprintln!("Check out the rustc-dev-guide for more information:");
 +    eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n");
 +
 +    assert!(bad_tests.is_empty());
 +}
index 9f8e778b3b9d1d36b526de62226c03462ab1dff4,0000000000000000000000000000000000000000..a52a0b5289fe4839f22bc68f9ec1bbfb206fc6e2
mode 100644,000000..100644
--- /dev/null
@@@ -1,46 -1,0 +1,47 @@@
 +error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of
 +           allow-dbg-in-tests
 +           allow-expect-in-tests
 +           allow-unwrap-in-tests
 +           allowed-scripts
 +           arithmetic-allowed
 +           array-size-threshold
 +           avoid-breaking-exported-api
 +           await-holding-invalid-types
 +           blacklisted-names
 +           cargo-ignore-publish
 +           cognitive-complexity-threshold
 +           cyclomatic-complexity-threshold
 +           disallowed-methods
 +           disallowed-names
 +           disallowed-types
 +           doc-valid-idents
 +           enable-raw-pointer-heuristic-for-send
 +           enforced-import-renames
 +           enum-variant-name-threshold
 +           enum-variant-size-threshold
++           large-error-threshold
 +           literal-representation-threshold
 +           max-fn-params-bools
 +           max-include-file-size
 +           max-struct-bools
 +           max-suggested-slice-pattern-length
 +           max-trait-bounds
 +           msrv
 +           pass-by-value-size-limit
 +           single-char-binding-names-threshold
 +           standard-macro-braces
 +           third-party
 +           too-large-for-stack
 +           too-many-arguments-threshold
 +           too-many-lines-threshold
 +           trivial-copy-size-limit
 +           type-complexity-threshold
 +           unreadable-literal-lint-fractions
 +           upper-case-acronyms-aggressive
 +           vec-box-size-threshold
 +           verbose-bit-mask-threshold
 +           warn-on-all-wildcard-imports
 +       at line 5 column 1
 +
 +error: aborting due to previous error
 +
index 0d65071af15ed1c4694daec5b20dac9baa96e56a,0000000000000000000000000000000000000000..6f0485b5279b1112c173c8a5d07cfcbb872bd0d3
mode 100644,000000..100644
--- /dev/null
@@@ -1,44 -1,0 +1,44 @@@
-     let _ = String::from("").ends_with(".ext12");
 +#![warn(clippy::case_sensitive_file_extension_comparisons)]
 +
 +use std::string::String;
 +
 +struct TestStruct;
 +
 +impl TestStruct {
 +    fn ends_with(self, arg: &str) {}
 +}
 +
 +fn is_rust_file(filename: &str) -> bool {
 +    filename.ends_with(".rs")
 +}
 +
 +fn main() {
 +    // std::string::String and &str should trigger the lint failure with .ext12
-     let _ = String::from("").ends_with(".EXT12");
++    let _ = String::new().ends_with(".ext12");
 +    let _ = "str".ends_with(".ext12");
 +
 +    // The test struct should not trigger the lint failure with .ext12
 +    TestStruct {}.ends_with(".ext12");
 +
 +    // std::string::String and &str should trigger the lint failure with .EXT12
-     let _ = String::from("").ends_with(".eXT12");
++    let _ = String::new().ends_with(".EXT12");
 +    let _ = "str".ends_with(".EXT12");
 +
 +    // The test struct should not trigger the lint failure with .EXT12
 +    TestStruct {}.ends_with(".EXT12");
 +
 +    // Should not trigger the lint failure with .eXT12
-     let _ = String::from("").ends_with(".EXT123");
++    let _ = String::new().ends_with(".eXT12");
 +    let _ = "str".ends_with(".eXT12");
 +    TestStruct {}.ends_with(".eXT12");
 +
 +    // Should not trigger the lint failure with .EXT123 (too long)
-     let _ = String::from("").ends_with("a.ext");
++    let _ = String::new().ends_with(".EXT123");
 +    let _ = "str".ends_with(".EXT123");
 +    TestStruct {}.ends_with(".EXT123");
 +
 +    // Shouldn't fail if it doesn't start with a dot
++    let _ = String::new().ends_with("a.ext");
 +    let _ = "str".ends_with("a.extA");
 +    TestStruct {}.ends_with("a.ext");
 +}
index 05b98169f2d17b0dd1be7812b07acd5236fb566b,0000000000000000000000000000000000000000..5d9a043edb9a5357dbce51e67c236a5f838901a2
mode 100644,000000..100644
--- /dev/null
@@@ -1,43 -1,0 +1,43 @@@
-   --> $DIR/case_sensitive_file_extension_comparisons.rs:17:30
 +error: case-sensitive file extension comparison
 +  --> $DIR/case_sensitive_file_extension_comparisons.rs:12:14
 +   |
 +LL |     filename.ends_with(".rs")
 +   |              ^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::case-sensitive-file-extension-comparisons` implied by `-D warnings`
 +   = help: consider using a case-insensitive comparison instead
 +
 +error: case-sensitive file extension comparison
- LL |     let _ = String::from("").ends_with(".ext12");
-    |                              ^^^^^^^^^^^^^^^^^^^
++  --> $DIR/case_sensitive_file_extension_comparisons.rs:17:27
 +   |
-   --> $DIR/case_sensitive_file_extension_comparisons.rs:24:30
++LL |     let _ = String::new().ends_with(".ext12");
++   |                           ^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using a case-insensitive comparison instead
 +
 +error: case-sensitive file extension comparison
 +  --> $DIR/case_sensitive_file_extension_comparisons.rs:18:19
 +   |
 +LL |     let _ = "str".ends_with(".ext12");
 +   |                   ^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using a case-insensitive comparison instead
 +
 +error: case-sensitive file extension comparison
- LL |     let _ = String::from("").ends_with(".EXT12");
-    |                              ^^^^^^^^^^^^^^^^^^^
++  --> $DIR/case_sensitive_file_extension_comparisons.rs:24:27
 +   |
++LL |     let _ = String::new().ends_with(".EXT12");
++   |                           ^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using a case-insensitive comparison instead
 +
 +error: case-sensitive file extension comparison
 +  --> $DIR/case_sensitive_file_extension_comparisons.rs:25:19
 +   |
 +LL |     let _ = "str".ends_with(".EXT12");
 +   |                   ^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider using a case-insensitive comparison instead
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b70c1912951164cd911d6896ceedd2a9c2cd9cff
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++#![warn(clippy::cast_slice_from_raw_parts)]
++
++#[allow(unused_imports, unused_unsafe)]
++fn main() {
++    let mut vec = vec![0u8; 1];
++    let ptr: *const u8 = vec.as_ptr();
++    let mptr = vec.as_mut_ptr();
++    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
++    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
++    let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
++    {
++        use core::slice;
++        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
++        use slice as one;
++        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
++    }
++    {
++        use std::slice;
++        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
++        use slice as one;
++        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c1b316765c967748ab163213d492e5c1d12a47f1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,24 @@@
++// run-rustfix
++#![warn(clippy::cast_slice_from_raw_parts)]
++
++#[allow(unused_imports, unused_unsafe)]
++fn main() {
++    let mut vec = vec![0u8; 1];
++    let ptr: *const u8 = vec.as_ptr();
++    let mptr = vec.as_mut_ptr();
++    let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
++    let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
++    let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
++    {
++        use core::slice;
++        let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
++        use slice as one;
++        let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
++    }
++    {
++        use std::slice;
++        let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
++        use slice as one;
++        let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f07801c197cccfe73f1dd4727b2bd15770b11f2c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,46 @@@
++error: casting the result of `from_raw_parts` to *const [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:9:35
++   |
++LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
++   |
++   = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings`
++
++error: casting the result of `from_raw_parts_mut` to *mut [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:10:35
++   |
++LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
++   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
++
++error: casting the result of `from_raw_parts` to *const [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:11:26
++   |
++LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
++
++error: casting the result of `from_raw_parts` to *const [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:14:30
++   |
++LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
++
++error: casting the result of `from_raw_parts` to *const [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:16:30
++   |
++LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
++
++error: casting the result of `from_raw_parts` to *const [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:20:30
++   |
++LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
++
++error: casting the result of `from_raw_parts` to *const [u8]
++  --> $DIR/cast_raw_slice_pointer_cast.rs:22:30
++   |
++LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
++   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
++
++error: aborting due to 7 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..49fc9a9629e25415a714b6e3616602c77367f239
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,73 @@@
++// run-rustfix
++
++#![warn(clippy::collapsible_str_replace)]
++
++fn get_filter() -> char {
++    'u'
++}
++
++fn main() {
++    let d = 'd';
++    let p = 'p';
++    let s = 's';
++    let u = 'u';
++    let l = "l";
++
++    let mut iter = ["l", "z"].iter();
++
++    // LINT CASES
++    let _ = "hesuo worpd".replace(['s', 'u'], "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u'], l);
++
++    let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l");
++
++    let _ = "hesuo worpd"
++        .replace(['s', 'u', 'p', 'd'], "l");
++
++    let _ = "hesuo world".replace([s, 'u'], "l");
++
++    let _ = "hesuo worpd".replace([s, 'u', 'p'], "l");
++
++    let _ = "hesuo worpd".replace([s, u, 'p'], "l");
++
++    let _ = "hesuo worpd".replace([s, u, p], "l");
++
++    let _ = "hesuo worlp".replace(['s', 'u'], "l").replace('p', "d");
++
++    let _ = "hesuo worpd".replace('s', "x").replace(['u', 'p'], "l");
++
++    // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")`
++    let _ = "hesudo worpd".replace("su", "l").replace(['d', 'p'], "l");
++
++    let _ = "hesudo worpd".replace([d, 'p'], "l").replace("su", "l");
++
++    let _ = "hesuo world".replace([get_filter(), 's'], "l");
++
++    // NO LINT CASES
++    let _ = "hesuo world".replace('s', "l").replace('u', "p");
++
++    let _ = "hesuo worpd".replace('s', "l").replace('p', l);
++
++    let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l");
++
++    // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]`
++    let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l");
++
++    let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l);
++
++    let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l");
++
++    let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l");
++
++    // Regression test
++    let _ = "hesuo worpd"
++        .replace('u', iter.next().unwrap())
++        .replace('s', iter.next().unwrap());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e3e25c4146ffa6bdd7a71bcd2067ba9a6ec6b68e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,76 @@@
++// run-rustfix
++
++#![warn(clippy::collapsible_str_replace)]
++
++fn get_filter() -> char {
++    'u'
++}
++
++fn main() {
++    let d = 'd';
++    let p = 'p';
++    let s = 's';
++    let u = 'u';
++    let l = "l";
++
++    let mut iter = ["l", "z"].iter();
++
++    // LINT CASES
++    let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
++
++    let _ = "hesuo worpd".replace('s', l).replace('u', l);
++
++    let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
++
++    let _ = "hesuo worpd"
++        .replace('s', "l")
++        .replace('u', "l")
++        .replace('p', "l")
++        .replace('d', "l");
++
++    let _ = "hesuo world".replace(s, "l").replace('u', "l");
++
++    let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
++
++    let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
++
++    let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
++
++    let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
++
++    let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
++
++    // Note: Future iterations could lint `replace(|c| matches!(c, "su" | 'd' | 'p'), "l")`
++    let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
++
++    let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
++
++    let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
++
++    // NO LINT CASES
++    let _ = "hesuo world".replace('s', "l").replace('u', "p");
++
++    let _ = "hesuo worpd".replace('s', "l").replace('p', l);
++
++    let _ = "hesudo worpd".replace('d', "l").replace("su", "l").replace('p', "l");
++
++    // Note: Future iterations of `collapsible_str_replace` might lint this and combine to `[s, u, p]`
++    let _ = "hesuo worpd".replace([s, u], "l").replace([u, p], "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u'], "l").replace(['u', 'p'], "l");
++
++    let _ = "hesuo worpd".replace('s', "l").replace(['u', 'p'], "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u', 'p'], "l").replace('r', "l");
++
++    let _ = "hesuo worpd".replace(['s', 'u', 'p'], l).replace('r', l);
++
++    let _ = "hesuo worpd".replace(['s', u, 'p'], "l").replace('r', "l");
++
++    let _ = "hesuo worpd".replace([s, u], "l").replace(p, "l");
++
++    // Regression test
++    let _ = "hesuo worpd"
++        .replace('u', iter.next().unwrap())
++        .replace('s', iter.next().unwrap());
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8e3daf3b898a3328e6f5f9de77f42e24f2729b79
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,86 @@@
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:19:27
++   |
++LL |     let _ = "hesuo worpd".replace('s', "l").replace('u', "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
++   |
++   = note: `-D clippy::collapsible-str-replace` implied by `-D warnings`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:21:27
++   |
++LL |     let _ = "hesuo worpd".replace('s', l).replace('u', l);
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], l)`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:23:27
++   |
++LL |     let _ = "hesuo worpd".replace('s', "l").replace('u', "l").replace('p', "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u', 'p'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:26:10
++   |
++LL |           .replace('s', "l")
++   |  __________^
++LL | |         .replace('u', "l")
++LL | |         .replace('p', "l")
++LL | |         .replace('d', "l");
++   | |__________________________^ help: replace with: `replace(['s', 'u', 'p', 'd'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:31:27
++   |
++LL |     let _ = "hesuo world".replace(s, "l").replace('u', "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:33:27
++   |
++LL |     let _ = "hesuo worpd".replace(s, "l").replace('u', "l").replace('p', "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, 'u', 'p'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:35:27
++   |
++LL |     let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace('p', "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, 'p'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:37:27
++   |
++LL |     let _ = "hesuo worpd".replace(s, "l").replace(u, "l").replace(p, "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([s, u, p], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:39:27
++   |
++LL |     let _ = "hesuo worlp".replace('s', "l").replace('u', "l").replace('p', "d");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['s', 'u'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:41:45
++   |
++LL |     let _ = "hesuo worpd".replace('s', "x").replace('u', "l").replace('p', "l");
++   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['u', 'p'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:44:47
++   |
++LL |     let _ = "hesudo worpd".replace("su", "l").replace('d', "l").replace('p', "l");
++   |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace(['d', 'p'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:46:28
++   |
++LL |     let _ = "hesudo worpd".replace(d, "l").replace('p', "l").replace("su", "l");
++   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([d, 'p'], "l")`
++
++error: used consecutive `str::replace` call
++  --> $DIR/collapsible_str_replace.rs:48:27
++   |
++LL |     let _ = "hesuo world".replace(get_filter(), "l").replace('s', "l");
++   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `replace([get_filter(), 's'], "l")`
++
++error: aborting due to 13 previous errors
++
index 1073acf6f0cd66b51c33588bfa7296e1a9364a57,0000000000000000000000000000000000000000..d742595e14d4c17dfd987a6dbaab4aca98916da5
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
-     let res: Result<u8, ()> = Ok(0);
 +#![warn(clippy::expect_used)]
 +
 +fn expect_option() {
 +    let opt = Some(0);
 +    let _ = opt.expect("");
 +}
 +
 +fn expect_result() {
++    let res: Result<u8, u8> = Ok(0);
 +    let _ = res.expect("");
++    let _ = res.expect_err("");
 +}
 +
 +fn main() {
 +    expect_option();
 +    expect_result();
 +}
index ab28aac45563b1cd7bfd3192ad5b192f86638451,0000000000000000000000000000000000000000..904c090464523c17dce9196547b56f306115afdb
mode 100644,000000..100644
--- /dev/null
@@@ -1,19 -1,0 +1,27 @@@
- error: aborting due to 2 previous errors
 +error: used `expect()` on `an Option` value
 +  --> $DIR/expect.rs:5:13
 +   |
 +LL |     let _ = opt.expect("");
 +   |             ^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::expect-used` implied by `-D warnings`
 +   = help: if this value is `None`, it will panic
 +
 +error: used `expect()` on `a Result` value
 +  --> $DIR/expect.rs:10:13
 +   |
 +LL |     let _ = res.expect("");
 +   |             ^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
++error: used `expect_err()` on `a Result` value
++  --> $DIR/expect.rs:11:13
++   |
++LL |     let _ = res.expect_err("");
++   |             ^^^^^^^^^^^^^^^^^^
++   |
++   = help: if this value is an `Ok`, it will panic
++
++error: aborting due to 3 previous errors
 +
index ae7805fdf018eea7228cecc02adcb4781173768e,0000000000000000000000000000000000000000..c86a502d15f0967fcf2e448d62f35f0886bdeff9
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,19 @@@
 +// run-rustfix
 +#![warn(clippy::imprecise_flops)]
 +
 +fn main() {
 +    let x = 2f32;
 +    let _ = x.exp_m1();
 +    let _ = x.exp_m1() + 2.0;
++    let _ = (x as f32).exp_m1() + 2.0;
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.exp() - 2.0;
 +    let _ = x.exp() - 1.0 * 2.0;
 +
 +    let x = 2f64;
 +    let _ = x.exp_m1();
 +    let _ = x.exp_m1() + 2.0;
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.exp() - 2.0;
 +    let _ = x.exp() - 1.0 * 2.0;
 +}
index 27e0b9bcbc937a829e9e1d9ce7d7fb286c0c63d6,0000000000000000000000000000000000000000..e59589f912a21accd00d9d9306a70b5df937197a
mode 100644,000000..100644
--- /dev/null
@@@ -1,18 -1,0 +1,19 @@@
 +// run-rustfix
 +#![warn(clippy::imprecise_flops)]
 +
 +fn main() {
 +    let x = 2f32;
 +    let _ = x.exp() - 1.0;
 +    let _ = x.exp() - 1.0 + 2.0;
++    let _ = (x as f32).exp() - 1.0 + 2.0;
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.exp() - 2.0;
 +    let _ = x.exp() - 1.0 * 2.0;
 +
 +    let x = 2f64;
 +    let _ = x.exp() - 1.0;
 +    let _ = x.exp() - 1.0 + 2.0;
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.exp() - 2.0;
 +    let _ = x.exp() - 1.0 * 2.0;
 +}
index 5cd999ad47cdd0d768138c901b1a9df01d891d8b,0000000000000000000000000000000000000000..f84eede19872a2923b409a5d32c3fddb5f03e8b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,34 @@@
-   --> $DIR/floating_point_exp.rs:13:13
 +error: (e.pow(x) - 1) can be computed more accurately
 +  --> $DIR/floating_point_exp.rs:6:13
 +   |
 +LL |     let _ = x.exp() - 1.0;
 +   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 +   |
 +   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
 +
 +error: (e.pow(x) - 1) can be computed more accurately
 +  --> $DIR/floating_point_exp.rs:7:13
 +   |
 +LL |     let _ = x.exp() - 1.0 + 2.0;
 +   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 +
 +error: (e.pow(x) - 1) can be computed more accurately
-   --> $DIR/floating_point_exp.rs:14:13
++  --> $DIR/floating_point_exp.rs:8:13
++   |
++LL |     let _ = (x as f32).exp() - 1.0 + 2.0;
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).exp_m1()`
++
++error: (e.pow(x) - 1) can be computed more accurately
++  --> $DIR/floating_point_exp.rs:14:13
 +   |
 +LL |     let _ = x.exp() - 1.0;
 +   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 +
 +error: (e.pow(x) - 1) can be computed more accurately
- error: aborting due to 4 previous errors
++  --> $DIR/floating_point_exp.rs:15:13
 +   |
 +LL |     let _ = x.exp() - 1.0 + 2.0;
 +   |             ^^^^^^^^^^^^^ help: consider using: `x.exp_m1()`
 +
++error: aborting due to 5 previous errors
 +
index 5b487bb8fcf790cae0bb771ae964327b04036cc5,0000000000000000000000000000000000000000..4def9300bb7d2f70dae72beb47af6be5586fd03c
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,59 @@@
 +// run-rustfix
 +#![allow(dead_code, clippy::double_parens)]
 +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
 +
 +const TWO: f32 = 2.0;
 +const E: f32 = std::f32::consts::E;
 +
 +fn check_log_base() {
 +    let x = 1f32;
 +    let _ = x.log2();
 +    let _ = x.log10();
 +    let _ = x.ln();
 +    let _ = x.log2();
 +    let _ = x.ln();
++    let _ = (x as f32).log2();
 +
 +    let x = 1f64;
 +    let _ = x.log2();
 +    let _ = x.log10();
 +    let _ = x.ln();
 +}
 +
 +fn check_ln1p() {
 +    let x = 1f32;
 +    let _ = 2.0f32.ln_1p();
 +    let _ = 2.0f32.ln_1p();
 +    let _ = x.ln_1p();
 +    let _ = (x / 2.0).ln_1p();
 +    let _ = x.powi(3).ln_1p();
 +    let _ = (x.powi(3) / 2.0).ln_1p();
 +    let _ = (std::f32::consts::E - 1.0).ln_1p();
 +    let _ = x.ln_1p();
 +    let _ = x.powi(3).ln_1p();
 +    let _ = (x + 2.0).ln_1p();
 +    let _ = (x / 2.0).ln_1p();
 +    // Cases where the lint shouldn't be applied
 +    let _ = (1.0 + x + 2.0).ln();
 +    let _ = (x + 1.0 + 2.0).ln();
 +    let _ = (x + 1.0 / 2.0).ln();
 +    let _ = (1.0 + x - 2.0).ln();
 +
 +    let x = 1f64;
 +    let _ = 2.0f64.ln_1p();
 +    let _ = 2.0f64.ln_1p();
 +    let _ = x.ln_1p();
 +    let _ = (x / 2.0).ln_1p();
 +    let _ = x.powi(3).ln_1p();
 +    let _ = x.ln_1p();
 +    let _ = x.powi(3).ln_1p();
 +    let _ = (x + 2.0).ln_1p();
 +    let _ = (x / 2.0).ln_1p();
 +    // Cases where the lint shouldn't be applied
 +    let _ = (1.0 + x + 2.0).ln();
 +    let _ = (x + 1.0 + 2.0).ln();
 +    let _ = (x + 1.0 / 2.0).ln();
 +    let _ = (1.0 + x - 2.0).ln();
 +}
 +
 +fn main() {}
index 01181484e7dee290c0973d60a38a93bd71062905,0000000000000000000000000000000000000000..1e04caa7d2a865070f83c44e465dfe672ea1514a
mode 100644,000000..100644
--- /dev/null
@@@ -1,58 -1,0 +1,59 @@@
 +// run-rustfix
 +#![allow(dead_code, clippy::double_parens)]
 +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
 +
 +const TWO: f32 = 2.0;
 +const E: f32 = std::f32::consts::E;
 +
 +fn check_log_base() {
 +    let x = 1f32;
 +    let _ = x.log(2f32);
 +    let _ = x.log(10f32);
 +    let _ = x.log(std::f32::consts::E);
 +    let _ = x.log(TWO);
 +    let _ = x.log(E);
++    let _ = (x as f32).log(2f32);
 +
 +    let x = 1f64;
 +    let _ = x.log(2f64);
 +    let _ = x.log(10f64);
 +    let _ = x.log(std::f64::consts::E);
 +}
 +
 +fn check_ln1p() {
 +    let x = 1f32;
 +    let _ = (1f32 + 2.).ln();
 +    let _ = (1f32 + 2.0).ln();
 +    let _ = (1.0 + x).ln();
 +    let _ = (1.0 + x / 2.0).ln();
 +    let _ = (1.0 + x.powi(3)).ln();
 +    let _ = (1.0 + x.powi(3) / 2.0).ln();
 +    let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
 +    let _ = (x + 1.0).ln();
 +    let _ = (x.powi(3) + 1.0).ln();
 +    let _ = (x + 2.0 + 1.0).ln();
 +    let _ = (x / 2.0 + 1.0).ln();
 +    // Cases where the lint shouldn't be applied
 +    let _ = (1.0 + x + 2.0).ln();
 +    let _ = (x + 1.0 + 2.0).ln();
 +    let _ = (x + 1.0 / 2.0).ln();
 +    let _ = (1.0 + x - 2.0).ln();
 +
 +    let x = 1f64;
 +    let _ = (1f64 + 2.).ln();
 +    let _ = (1f64 + 2.0).ln();
 +    let _ = (1.0 + x).ln();
 +    let _ = (1.0 + x / 2.0).ln();
 +    let _ = (1.0 + x.powi(3)).ln();
 +    let _ = (x + 1.0).ln();
 +    let _ = (x.powi(3) + 1.0).ln();
 +    let _ = (x + 2.0 + 1.0).ln();
 +    let _ = (x / 2.0 + 1.0).ln();
 +    // Cases where the lint shouldn't be applied
 +    let _ = (1.0 + x + 2.0).ln();
 +    let _ = (x + 1.0 + 2.0).ln();
 +    let _ = (x + 1.0 / 2.0).ln();
 +    let _ = (1.0 + x - 2.0).ln();
 +}
 +
 +fn main() {}
index 96e5a15444170f4fd2d9453ac15774af596f86d7,0000000000000000000000000000000000000000..89800a13a6ecc75ed966bc41d57b090427b0fc50
mode 100644,000000..100644
--- /dev/null
@@@ -1,174 -1,0 +1,180 @@@
-   --> $DIR/floating_point_log.rs:17:13
 +error: logarithm for bases 2, 10 and e can be computed more accurately
 +  --> $DIR/floating_point_log.rs:10:13
 +   |
 +LL |     let _ = x.log(2f32);
 +   |             ^^^^^^^^^^^ help: consider using: `x.log2()`
 +   |
 +   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
 +  --> $DIR/floating_point_log.rs:11:13
 +   |
 +LL |     let _ = x.log(10f32);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
 +  --> $DIR/floating_point_log.rs:12:13
 +   |
 +LL |     let _ = x.log(std::f32::consts::E);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
 +  --> $DIR/floating_point_log.rs:13:13
 +   |
 +LL |     let _ = x.log(TWO);
 +   |             ^^^^^^^^^^ help: consider using: `x.log2()`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
 +  --> $DIR/floating_point_log.rs:14:13
 +   |
 +LL |     let _ = x.log(E);
 +   |             ^^^^^^^^ help: consider using: `x.ln()`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
-   --> $DIR/floating_point_log.rs:18:13
++  --> $DIR/floating_point_log.rs:15:13
++   |
++LL |     let _ = (x as f32).log(2f32);
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log2()`
++
++error: logarithm for bases 2, 10 and e can be computed more accurately
++  --> $DIR/floating_point_log.rs:18:13
 +   |
 +LL |     let _ = x.log(2f64);
 +   |             ^^^^^^^^^^^ help: consider using: `x.log2()`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
-   --> $DIR/floating_point_log.rs:19:13
++  --> $DIR/floating_point_log.rs:19:13
 +   |
 +LL |     let _ = x.log(10f64);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.log10()`
 +
 +error: logarithm for bases 2, 10 and e can be computed more accurately
-   --> $DIR/floating_point_log.rs:24:13
++  --> $DIR/floating_point_log.rs:20:13
 +   |
 +LL |     let _ = x.log(std::f64::consts::E);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.ln()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:25:13
++  --> $DIR/floating_point_log.rs:25:13
 +   |
 +LL |     let _ = (1f32 + 2.).ln();
 +   |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
 +   |
 +   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:26:13
++  --> $DIR/floating_point_log.rs:26:13
 +   |
 +LL |     let _ = (1f32 + 2.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f32.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:27:13
++  --> $DIR/floating_point_log.rs:27:13
 +   |
 +LL |     let _ = (1.0 + x).ln();
 +   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:28:13
++  --> $DIR/floating_point_log.rs:28:13
 +   |
 +LL |     let _ = (1.0 + x / 2.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:29:13
++  --> $DIR/floating_point_log.rs:29:13
 +   |
 +LL |     let _ = (1.0 + x.powi(3)).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:30:13
++  --> $DIR/floating_point_log.rs:30:13
 +   |
 +LL |     let _ = (1.0 + x.powi(3) / 2.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x.powi(3) / 2.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:31:13
++  --> $DIR/floating_point_log.rs:31:13
 +   |
 +LL |     let _ = (1.0 + (std::f32::consts::E - 1.0)).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(std::f32::consts::E - 1.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:32:13
++  --> $DIR/floating_point_log.rs:32:13
 +   |
 +LL |     let _ = (x + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:33:13
++  --> $DIR/floating_point_log.rs:33:13
 +   |
 +LL |     let _ = (x.powi(3) + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:34:13
++  --> $DIR/floating_point_log.rs:34:13
 +   |
 +LL |     let _ = (x + 2.0 + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:42:13
++  --> $DIR/floating_point_log.rs:35:13
 +   |
 +LL |     let _ = (x / 2.0 + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:43:13
++  --> $DIR/floating_point_log.rs:43:13
 +   |
 +LL |     let _ = (1f64 + 2.).ln();
 +   |             ^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:44:13
++  --> $DIR/floating_point_log.rs:44:13
 +   |
 +LL |     let _ = (1f64 + 2.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `2.0f64.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:45:13
++  --> $DIR/floating_point_log.rs:45:13
 +   |
 +LL |     let _ = (1.0 + x).ln();
 +   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:46:13
++  --> $DIR/floating_point_log.rs:46:13
 +   |
 +LL |     let _ = (1.0 + x / 2.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:47:13
++  --> $DIR/floating_point_log.rs:47:13
 +   |
 +LL |     let _ = (1.0 + x.powi(3)).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:48:13
++  --> $DIR/floating_point_log.rs:48:13
 +   |
 +LL |     let _ = (x + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^ help: consider using: `x.ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:49:13
++  --> $DIR/floating_point_log.rs:49:13
 +   |
 +LL |     let _ = (x.powi(3) + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(3).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
-   --> $DIR/floating_point_log.rs:50:13
++  --> $DIR/floating_point_log.rs:50:13
 +   |
 +LL |     let _ = (x + 2.0 + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x + 2.0).ln_1p()`
 +
 +error: ln(1 + x) can be computed more accurately
- error: aborting due to 28 previous errors
++  --> $DIR/floating_point_log.rs:51:13
 +   |
 +LL |     let _ = (x / 2.0 + 1.0).ln();
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x / 2.0).ln_1p()`
 +
++error: aborting due to 29 previous errors
 +
index 13962a272d4552b6ccce46a9ff908890e0add9cf,0000000000000000000000000000000000000000..936462f94066f681284506f22daacd4a6706dc84
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops)]
 +
 +fn main() {
 +    let x = 3f32;
 +    let y = 5f32;
 +    let _ = x.log(y);
++    let _ = (x as f32).log(y);
 +    let _ = x.log(y);
 +    let _ = x.log(y);
 +    let _ = x.log(y);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.ln() / y.powf(3.2);
 +    let _ = x.powf(3.2) / y.powf(3.2);
 +    let _ = x.powf(3.2) / y.ln();
 +    let _ = x.log(5f32) / y.log(7f32);
 +}
index 26bc20d5370b1f0a6f678d1b7d54b6fe447487d8,0000000000000000000000000000000000000000..0b56fa8fa41fa3112f63adfeac48176a2909c79e
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops)]
 +
 +fn main() {
 +    let x = 3f32;
 +    let y = 5f32;
 +    let _ = x.ln() / y.ln();
++    let _ = (x as f32).ln() / y.ln();
 +    let _ = x.log2() / y.log2();
 +    let _ = x.log10() / y.log10();
 +    let _ = x.log(5f32) / y.log(5f32);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.ln() / y.powf(3.2);
 +    let _ = x.powf(3.2) / y.powf(3.2);
 +    let _ = x.powf(3.2) / y.ln();
 +    let _ = x.log(5f32) / y.log(7f32);
 +}
index 78354c2f62d432eaf0e9c7a9cd6670bfc3bbc4cf,0000000000000000000000000000000000000000..384e3554cbbe1335f3707ebc03eb52b1015bbdee
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,34 @@@
-   --> $DIR/floating_point_logbase.rs:9:13
 +error: log base can be expressed more clearly
 +  --> $DIR/floating_point_logbase.rs:7:13
 +   |
 +LL |     let _ = x.ln() / y.ln();
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 +   |
 +   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 +
 +error: log base can be expressed more clearly
 +  --> $DIR/floating_point_logbase.rs:8:13
 +   |
++LL |     let _ = (x as f32).ln() / y.ln();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).log(y)`
++
++error: log base can be expressed more clearly
++  --> $DIR/floating_point_logbase.rs:9:13
++   |
 +LL |     let _ = x.log2() / y.log2();
 +   |             ^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 +
 +error: log base can be expressed more clearly
-   --> $DIR/floating_point_logbase.rs:10:13
++  --> $DIR/floating_point_logbase.rs:10:13
 +   |
 +LL |     let _ = x.log10() / y.log10();
 +   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 +
 +error: log base can be expressed more clearly
- error: aborting due to 4 previous errors
++  --> $DIR/floating_point_logbase.rs:11:13
 +   |
 +LL |     let _ = x.log(5f32) / y.log(5f32);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.log(y)`
 +
++error: aborting due to 5 previous errors
 +
index b0641a100cdc810b4a3c7d3d045928970dfe1272,0000000000000000000000000000000000000000..7efe10a10f9e9809db283ef8bf73378a16bbe31a
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,45 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
 +
 +fn main() {
 +    let x = 3f32;
 +    let _ = x.exp2();
 +    let _ = 3.1f32.exp2();
 +    let _ = (-3.1f32).exp2();
 +    let _ = x.exp();
 +    let _ = 3.1f32.exp();
 +    let _ = (-3.1f32).exp();
 +    let _ = x.sqrt();
 +    let _ = x.cbrt();
++    let _ = (x as f32).cbrt();
 +    let _ = x.powi(3);
 +    let _ = x.powi(-2);
 +    let _ = x.powi(16_777_215);
 +    let _ = x.powi(-16_777_215);
++    let _ = (x as f32).powi(-16_777_215);
++    let _ = (x as f32).powi(3);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(16_777_216.0);
 +    let _ = x.powf(-16_777_216.0);
 +
 +    let x = 3f64;
 +    let _ = x.exp2();
 +    let _ = 3.1f64.exp2();
 +    let _ = (-3.1f64).exp2();
 +    let _ = x.exp();
 +    let _ = 3.1f64.exp();
 +    let _ = (-3.1f64).exp();
 +    let _ = x.sqrt();
 +    let _ = x.cbrt();
 +    let _ = x.powi(3);
 +    let _ = x.powi(-2);
 +    let _ = x.powi(-2_147_483_648);
 +    let _ = x.powi(2_147_483_647);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(-2_147_483_649.0);
 +    let _ = x.powf(2_147_483_648.0);
 +}
index a0a2c973900f4b7ba943f29a7bb90dcecaf6a8ce,0000000000000000000000000000000000000000..445080417f2ed8c2a0e5b7f1f1a799286077c2f8
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,45 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops, clippy::imprecise_flops)]
 +
 +fn main() {
 +    let x = 3f32;
 +    let _ = 2f32.powf(x);
 +    let _ = 2f32.powf(3.1);
 +    let _ = 2f32.powf(-3.1);
 +    let _ = std::f32::consts::E.powf(x);
 +    let _ = std::f32::consts::E.powf(3.1);
 +    let _ = std::f32::consts::E.powf(-3.1);
 +    let _ = x.powf(1.0 / 2.0);
 +    let _ = x.powf(1.0 / 3.0);
++    let _ = (x as f32).powf(1.0 / 3.0);
 +    let _ = x.powf(3.0);
 +    let _ = x.powf(-2.0);
 +    let _ = x.powf(16_777_215.0);
 +    let _ = x.powf(-16_777_215.0);
++    let _ = (x as f32).powf(-16_777_215.0);
++    let _ = (x as f32).powf(3.0);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(16_777_216.0);
 +    let _ = x.powf(-16_777_216.0);
 +
 +    let x = 3f64;
 +    let _ = 2f64.powf(x);
 +    let _ = 2f64.powf(3.1);
 +    let _ = 2f64.powf(-3.1);
 +    let _ = std::f64::consts::E.powf(x);
 +    let _ = std::f64::consts::E.powf(3.1);
 +    let _ = std::f64::consts::E.powf(-3.1);
 +    let _ = x.powf(1.0 / 2.0);
 +    let _ = x.powf(1.0 / 3.0);
 +    let _ = x.powf(3.0);
 +    let _ = x.powf(-2.0);
 +    let _ = x.powf(-2_147_483_648.0);
 +    let _ = x.powf(2_147_483_647.0);
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powf(2.1);
 +    let _ = x.powf(-2.1);
 +    let _ = x.powf(-2_147_483_649.0);
 +    let _ = x.powf(2_147_483_648.0);
 +}
index 2422eb911e90a7041dedbecccf2fa2677a12710d,0000000000000000000000000000000000000000..6ee696e6ada5ffda8d5580258d2ec7074bcfee3a
mode 100644,000000..100644
--- /dev/null
@@@ -1,150 -1,0 +1,168 @@@
- error: exponentiation with integer powers can be computed more efficiently
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:6:13
 +   |
 +LL |     let _ = 2f32.powf(x);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
 +   |
 +   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:7:13
 +   |
 +LL |     let _ = 2f32.powf(3.1);
 +   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:8:13
 +   |
 +LL |     let _ = 2f32.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:9:13
 +   |
 +LL |     let _ = std::f32::consts::E.powf(x);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:10:13
 +   |
 +LL |     let _ = std::f32::consts::E.powf(3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f32.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:11:13
 +   |
 +LL |     let _ = std::f32::consts::E.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f32).exp()`
 +
 +error: square-root of a number can be computed more efficiently and accurately
 +  --> $DIR/floating_point_powf.rs:12:13
 +   |
 +LL |     let _ = x.powf(1.0 / 2.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
 +
 +error: cube-root of a number can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:13:13
 +   |
 +LL |     let _ = x.powf(1.0 / 3.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 +   |
 +   = note: `-D clippy::imprecise-flops` implied by `-D warnings`
 +
-   --> $DIR/floating_point_powf.rs:15:13
++error: cube-root of a number can be computed more accurately
 +  --> $DIR/floating_point_powf.rs:14:13
 +   |
++LL |     let _ = (x as f32).powf(1.0 / 3.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).cbrt()`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:15:13
++   |
 +LL |     let _ = x.powf(3.0);
 +   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:16:13
++  --> $DIR/floating_point_powf.rs:16:13
 +   |
 +LL |     let _ = x.powf(-2.0);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:17:13
++  --> $DIR/floating_point_powf.rs:17:13
 +   |
 +LL |     let _ = x.powf(16_777_215.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(16_777_215)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:25:13
++  --> $DIR/floating_point_powf.rs:18:13
 +   |
 +LL |     let _ = x.powf(-16_777_215.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-16_777_215)`
 +
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:19:13
++   |
++LL |     let _ = (x as f32).powf(-16_777_215.0);
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(-16_777_215)`
++
++error: exponentiation with integer powers can be computed more efficiently
++  --> $DIR/floating_point_powf.rs:20:13
++   |
++LL |     let _ = (x as f32).powf(3.0);
++   |             ^^^^^^^^^^^^^^^^^^^^ help: consider using: `(x as f32).powi(3)`
++
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:26:13
++  --> $DIR/floating_point_powf.rs:28:13
 +   |
 +LL |     let _ = 2f64.powf(x);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:27:13
++  --> $DIR/floating_point_powf.rs:29:13
 +   |
 +LL |     let _ = 2f64.powf(3.1);
 +   |             ^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:28:13
++  --> $DIR/floating_point_powf.rs:30:13
 +   |
 +LL |     let _ = 2f64.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp2()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:29:13
++  --> $DIR/floating_point_powf.rs:31:13
 +   |
 +LL |     let _ = std::f64::consts::E.powf(x);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:30:13
++  --> $DIR/floating_point_powf.rs:32:13
 +   |
 +LL |     let _ = std::f64::consts::E.powf(3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `3.1f64.exp()`
 +
 +error: exponent for bases 2 and e can be computed more accurately
-   --> $DIR/floating_point_powf.rs:31:13
++  --> $DIR/floating_point_powf.rs:33:13
 +   |
 +LL |     let _ = std::f64::consts::E.powf(-3.1);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-3.1f64).exp()`
 +
 +error: square-root of a number can be computed more efficiently and accurately
-   --> $DIR/floating_point_powf.rs:32:13
++  --> $DIR/floating_point_powf.rs:34:13
 +   |
 +LL |     let _ = x.powf(1.0 / 2.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.sqrt()`
 +
 +error: cube-root of a number can be computed more accurately
-   --> $DIR/floating_point_powf.rs:33:13
++  --> $DIR/floating_point_powf.rs:35:13
 +   |
 +LL |     let _ = x.powf(1.0 / 3.0);
 +   |             ^^^^^^^^^^^^^^^^^ help: consider using: `x.cbrt()`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:34:13
++  --> $DIR/floating_point_powf.rs:36:13
 +   |
 +LL |     let _ = x.powf(3.0);
 +   |             ^^^^^^^^^^^ help: consider using: `x.powi(3)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:35:13
++  --> $DIR/floating_point_powf.rs:37:13
 +   |
 +LL |     let _ = x.powf(-2.0);
 +   |             ^^^^^^^^^^^^ help: consider using: `x.powi(-2)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
-   --> $DIR/floating_point_powf.rs:36:13
++  --> $DIR/floating_point_powf.rs:38:13
 +   |
 +LL |     let _ = x.powf(-2_147_483_648.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(-2_147_483_648)`
 +
 +error: exponentiation with integer powers can be computed more efficiently
- error: aborting due to 24 previous errors
++  --> $DIR/floating_point_powf.rs:39:13
 +   |
 +LL |     let _ = x.powf(2_147_483_647.0);
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.powi(2_147_483_647)`
 +
++error: aborting due to 27 previous errors
 +
index 85f7c531e398ae28af7c4056f87c9e1bf4ebd89e,0000000000000000000000000000000000000000..5758db7c6c82d90506cc76a561f5d3ac18dfe021
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,21 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops)]
 +
 +fn main() {
 +    let one = 1;
 +    let x = 3f32;
 +
 +    let y = 4f32;
 +    let _ = x.mul_add(x, y);
 +    let _ = y.mul_add(y, x);
++    let _ = (y as f32).mul_add(y as f32, x);
 +    let _ = x.mul_add(x, y).sqrt();
 +    let _ = y.mul_add(y, x).sqrt();
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powi(2);
 +    let _ = x.powi(1 + 1);
 +    let _ = x.powi(3);
 +    let _ = x.powi(4) + y;
 +    let _ = x.powi(one + 1);
 +    let _ = (x.powi(2) + y.powi(2)).sqrt();
 +}
index ece61d1bec42d4bb78b2d9bdc0f419369c11805c,0000000000000000000000000000000000000000..5926bf1b0004265c96a82b53a9e8abaefcdedcc2
mode 100644,000000..100644
--- /dev/null
@@@ -1,20 -1,0 +1,21 @@@
 +// run-rustfix
 +#![warn(clippy::suboptimal_flops)]
 +
 +fn main() {
 +    let one = 1;
 +    let x = 3f32;
 +
 +    let y = 4f32;
 +    let _ = x.powi(2) + y;
 +    let _ = x + y.powi(2);
++    let _ = x + (y as f32).powi(2);
 +    let _ = (x.powi(2) + y).sqrt();
 +    let _ = (x + y.powi(2)).sqrt();
 +    // Cases where the lint shouldn't be applied
 +    let _ = x.powi(2);
 +    let _ = x.powi(1 + 1);
 +    let _ = x.powi(3);
 +    let _ = x.powi(4) + y;
 +    let _ = x.powi(one + 1);
 +    let _ = (x.powi(2) + y.powi(2)).sqrt();
 +}
index 37d840988bb23b3b422895d73c09fd6670109bcd,0000000000000000000000000000000000000000..a3c74544212b22e1f082e7d4938b14c60775f062
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,34 @@@
-   --> $DIR/floating_point_powi.rs:12:13
 +error: multiply and add expressions can be calculated more efficiently and accurately
 +  --> $DIR/floating_point_powi.rs:9:13
 +   |
 +LL |     let _ = x.powi(2) + y;
 +   |             ^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
 +   |
 +   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
 +
 +error: multiply and add expressions can be calculated more efficiently and accurately
 +  --> $DIR/floating_point_powi.rs:10:13
 +   |
 +LL |     let _ = x + y.powi(2);
 +   |             ^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
 +
 +error: multiply and add expressions can be calculated more efficiently and accurately
 +  --> $DIR/floating_point_powi.rs:11:13
 +   |
++LL |     let _ = x + (y as f32).powi(2);
++   |             ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(y as f32).mul_add(y as f32, x)`
++
++error: multiply and add expressions can be calculated more efficiently and accurately
++  --> $DIR/floating_point_powi.rs:12:13
++   |
 +LL |     let _ = (x.powi(2) + y).sqrt();
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `x.mul_add(x, y)`
 +
 +error: multiply and add expressions can be calculated more efficiently and accurately
- error: aborting due to 4 previous errors
++  --> $DIR/floating_point_powi.rs:13:13
 +   |
 +LL |     let _ = (x + y.powi(2)).sqrt();
 +   |             ^^^^^^^^^^^^^^^ help: consider using: `y.mul_add(y, x)`
 +
++error: aborting due to 5 previous errors
 +
index ce91fe176c6fccec4fee57bc3339f7091e0e8827,0000000000000000000000000000000000000000..27674b8a455b099a30d9f7be60cceb729628e077
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,30 @@@
 +// run-rustfix
 +#![feature(const_fn_floating_point_arithmetic)]
 +#![warn(clippy::suboptimal_flops)]
 +
 +/// Allow suboptimal_flops in constant context
 +pub const fn const_context() {
 +    let x = 3f32;
 +    let _ = x * 180f32 / std::f32::consts::PI;
 +}
 +
++pub fn issue9391(degrees: i64) {
++    let _ = (degrees as f64).to_radians();
++    let _ = (degrees as f64).to_degrees();
++}
++
 +fn main() {
 +    let x = 3f32;
 +    let _ = x.to_degrees();
 +    let _ = 90.0_f64.to_degrees();
 +    let _ = 90.5_f64.to_degrees();
 +    let _ = x.to_radians();
 +    let _ = 90.0_f64.to_radians();
 +    let _ = 90.5_f64.to_radians();
 +    // let _ = 90.5 * 80. * std::f32::consts::PI / 180f32;
 +    // Cases where the lint shouldn't be applied
 +    let _ = x * 90f32 / std::f32::consts::PI;
 +    let _ = x * std::f32::consts::PI / 90f32;
 +    let _ = x * 180f32 / std::f32::consts::E;
 +    let _ = x * std::f32::consts::E / 180f32;
 +}
index 8f3234986148b70d302dd0f5ac04852feec89c16,0000000000000000000000000000000000000000..f1ea73df39845d988b5437125ee05a82057eb8bc
mode 100644,000000..100644
--- /dev/null
@@@ -1,25 -1,0 +1,30 @@@
 +// run-rustfix
 +#![feature(const_fn_floating_point_arithmetic)]
 +#![warn(clippy::suboptimal_flops)]
 +
 +/// Allow suboptimal_flops in constant context
 +pub const fn const_context() {
 +    let x = 3f32;
 +    let _ = x * 180f32 / std::f32::consts::PI;
 +}
 +
++pub fn issue9391(degrees: i64) {
++    let _ = degrees as f64 * std::f64::consts::PI / 180.0;
++    let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
++}
++
 +fn main() {
 +    let x = 3f32;
 +    let _ = x * 180f32 / std::f32::consts::PI;
 +    let _ = 90. * 180f64 / std::f64::consts::PI;
 +    let _ = 90.5 * 180f64 / std::f64::consts::PI;
 +    let _ = x * std::f32::consts::PI / 180f32;
 +    let _ = 90. * std::f32::consts::PI / 180f32;
 +    let _ = 90.5 * std::f32::consts::PI / 180f32;
 +    // let _ = 90.5 * 80. * std::f32::consts::PI / 180f32;
 +    // Cases where the lint shouldn't be applied
 +    let _ = x * 90f32 / std::f32::consts::PI;
 +    let _ = x * std::f32::consts::PI / 90f32;
 +    let _ = x * 180f32 / std::f32::consts::E;
 +    let _ = x * std::f32::consts::E / 180f32;
 +}
index f12d3d23f3ab93fa38f53d5fc595857872e1d3a2,0000000000000000000000000000000000000000..979442f2c24a371d57d38aa7546e790ef7b5c638
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,52 @@@
-    |
-    = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++error: conversion to radians can be done more accurately
++  --> $DIR/floating_point_rad.rs:12:13
++   |
++LL |     let _ = degrees as f64 * std::f64::consts::PI / 180.0;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_radians()`
++   |
++   = note: `-D clippy::suboptimal-flops` implied by `-D warnings`
++
 +error: conversion to degrees can be done more accurately
 +  --> $DIR/floating_point_rad.rs:13:13
 +   |
++LL |     let _ = degrees as f64 * 180.0 / std::f64::consts::PI;
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(degrees as f64).to_degrees()`
++
++error: conversion to degrees can be done more accurately
++  --> $DIR/floating_point_rad.rs:18:13
++   |
 +LL |     let _ = x * 180f32 / std::f32::consts::PI;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_degrees()`
-   --> $DIR/floating_point_rad.rs:14:13
 +
 +error: conversion to degrees can be done more accurately
-   --> $DIR/floating_point_rad.rs:15:13
++  --> $DIR/floating_point_rad.rs:19:13
 +   |
 +LL |     let _ = 90. * 180f64 / std::f64::consts::PI;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_degrees()`
 +
 +error: conversion to degrees can be done more accurately
-   --> $DIR/floating_point_rad.rs:16:13
++  --> $DIR/floating_point_rad.rs:20:13
 +   |
 +LL |     let _ = 90.5 * 180f64 / std::f64::consts::PI;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_degrees()`
 +
 +error: conversion to radians can be done more accurately
-   --> $DIR/floating_point_rad.rs:17:13
++  --> $DIR/floating_point_rad.rs:21:13
 +   |
 +LL |     let _ = x * std::f32::consts::PI / 180f32;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `x.to_radians()`
 +
 +error: conversion to radians can be done more accurately
-   --> $DIR/floating_point_rad.rs:18:13
++  --> $DIR/floating_point_rad.rs:22:13
 +   |
 +LL |     let _ = 90. * std::f32::consts::PI / 180f32;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.0_f64.to_radians()`
 +
 +error: conversion to radians can be done more accurately
- error: aborting due to 6 previous errors
++  --> $DIR/floating_point_rad.rs:23:13
 +   |
 +LL |     let _ = 90.5 * std::f32::consts::PI / 180f32;
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `90.5_f64.to_radians()`
 +
++error: aborting due to 8 previous errors
 +
index 6b754f3bd7103bb3f6ff068ab637acc368f85c7c,0000000000000000000000000000000000000000..b56d6aec508d38bda3189d67fd5610f971166d4e
mode 100644,000000..100644
--- /dev/null
@@@ -1,94 -1,0 +1,94 @@@
-     let arg: String = "".to_owned();
 +// run-rustfix
 +
 +#![allow(
 +    unused_tuple_struct_fields,
 +    clippy::print_literal,
 +    clippy::redundant_clone,
 +    clippy::to_string_in_format_args,
 +    clippy::needless_borrow
 +)]
 +#![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();
 +
 +    let _ = String::new();
 +
 +    "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::new();
 +    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");
 +
 +    // Issue #8290
 +    let x = "foo";
 +    let _ = x.to_string();
 +    let _ = format!("{x:?}"); // Don't lint on debug
 +    let _ = x.to_string();
 +
 +    // Issue #9234
 +    let abc = "abc";
 +    let _ = abc.to_string();
 +    let xx = "xx";
 +    let _ = xx.to_string();
 +}
index ca9826b356ec8aea1642450c6f3789244323f071,0000000000000000000000000000000000000000..4c1a3a840ed96721f5cd87d7496c3e1edec99f94
mode 100644,000000..100644
--- /dev/null
@@@ -1,96 -1,0 +1,96 @@@
-     let arg: String = "".to_owned();
 +// run-rustfix
 +
 +#![allow(
 +    unused_tuple_struct_fields,
 +    clippy::print_literal,
 +    clippy::redundant_clone,
 +    clippy::to_string_in_format_args,
 +    clippy::needless_borrow
 +)]
 +#![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"##
 +    );
 +
 +    let _ = format!("");
 +
 +    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::new();
 +    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");
 +
 +    // Issue #8290
 +    let x = "foo";
 +    let _ = format!("{x}");
 +    let _ = format!("{x:?}"); // Don't lint on debug
 +    let _ = format!("{y}", y = x);
 +
 +    // Issue #9234
 +    let abc = "abc";
 +    let _ = format!("{abc}");
 +    let xx = "xx";
 +    let _ = format!("{xx}");
 +}
index 69b5e1c722e0320df446f0fb6a01d74d20fead7f,0000000000000000000000000000000000000000..e1c2d4d70be4f65effd89e0d84edfa3b77fcf3bd
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,162 @@@
- #![allow(unreachable_code)]
- #![allow(unused_macros)]
- #![allow(unused_variables)]
 +// run-rustfix
 +
++#![allow(unused)]
 +#![allow(clippy::assertions_on_constants)]
 +#![allow(clippy::eq_op)]
 +#![allow(clippy::print_literal)]
 +#![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);
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{bar}", foo = "foo", bar = "bar");
 +    println!("{foo}{bar}", foo = "foo", bar = "bar");
 +    println!("{foo}{bar}", bar = "bar", foo = "foo");
 +    println!("{foo}{bar}", bar = "bar", foo = "foo");
 +
 +    // negative tests
 +    println!("error: something failed at {}", Somewhere.to_string());
 +    // The next two tests are negative because caching the string might be faster than calling `<X as
 +    // Display>::fmt` twice.
 +    println!("{} and again {0}", x.to_string());
 +    println!("{foo}{foo}", foo = "foo".to_string());
 +    my_macro!();
 +    println!("error: something failed at {}", my_other_macro!());
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{foo:?}", foo = "foo".to_string());
 +}
++
++fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
++    println!(
++        "{:<9}  {:<10}  {}",
++        format!("0x{:x}", vendor_id),
++        format!("0x{:x}", product_id),
++        name
++    );
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/8855
++mod issue_8855 {
++    #![allow(dead_code)]
++
++    struct A {}
++
++    impl std::fmt::Display for A {
++        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
++            write!(f, "test")
++        }
++    }
++
++    fn main() {
++        let a = A {};
++        let b = A {};
++
++        let x = format!("{} {}", a, b);
++        dbg!(x);
++
++        let x = format!("{:>6} {:>6}", a, b.to_string());
++        dbg!(x);
++    }
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/9256
++mod issue_9256 {
++    #![allow(dead_code)]
++
++    fn print_substring(original: &str) {
++        assert!(original.len() > 10);
++        println!("{}", &original[..10]);
++    }
++
++    fn main() {
++        print_substring("Hello, world!");
++    }
++}
index 3a434c5bf002a3350a08a0da2988747de925bcc9,0000000000000000000000000000000000000000..b9a4d66c28ad9f4f85390dc427dca8d08e66bcdc
mode 100644,000000..100644
--- /dev/null
@@@ -1,117 -1,0 +1,162 @@@
- #![allow(unreachable_code)]
- #![allow(unused_macros)]
- #![allow(unused_variables)]
 +// run-rustfix
 +
++#![allow(unused)]
 +#![allow(clippy::assertions_on_constants)]
 +#![allow(clippy::eq_op)]
 +#![allow(clippy::print_literal)]
 +#![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());
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
 +    println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
 +    println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
 +    println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
 +
 +    // negative tests
 +    println!("error: something failed at {}", Somewhere.to_string());
 +    // The next two tests are negative because caching the string might be faster than calling `<X as
 +    // Display>::fmt` twice.
 +    println!("{} and again {0}", x.to_string());
 +    println!("{foo}{foo}", foo = "foo".to_string());
 +    my_macro!();
 +    println!("error: something failed at {}", my_other_macro!());
 +    // https://github.com/rust-lang/rust-clippy/issues/7903
 +    println!("{foo}{foo:?}", foo = "foo".to_string());
 +}
++
++fn issue8643(vendor_id: usize, product_id: usize, name: &str) {
++    println!(
++        "{:<9}  {:<10}  {}",
++        format!("0x{:x}", vendor_id),
++        format!("0x{:x}", product_id),
++        name
++    );
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/8855
++mod issue_8855 {
++    #![allow(dead_code)]
++
++    struct A {}
++
++    impl std::fmt::Display for A {
++        fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
++            write!(f, "test")
++        }
++    }
++
++    fn main() {
++        let a = A {};
++        let b = A {};
++
++        let x = format!("{} {}", a, b.to_string());
++        dbg!(x);
++
++        let x = format!("{:>6} {:>6}", a, b.to_string());
++        dbg!(x);
++    }
++}
++
++// https://github.com/rust-lang/rust-clippy/issues/9256
++mod issue_9256 {
++    #![allow(dead_code)]
++
++    fn print_substring(original: &str) {
++        assert!(original.len() > 10);
++        println!("{}", original[..10].to_string());
++    }
++
++    fn main() {
++        print_substring("Hello, world!");
++    }
++}
index c0cbca507958d1306cfbf008fb3b1d16e2f3959d,0000000000000000000000000000000000000000..aa6e3659b43b519d19f14fa7ecd395a7d7763bda
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,142 @@@
-   --> $DIR/format_args.rs:76:72
 +error: `to_string` applied to a type that implements `Display` in `format!` args
-   --> $DIR/format_args.rs:80:27
++  --> $DIR/format_args.rs:74: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:85:27
++  --> $DIR/format_args.rs:78: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:87:63
++  --> $DIR/format_args.rs:83: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:88:65
++  --> $DIR/format_args.rs:85: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:89:64
++  --> $DIR/format_args.rs:86: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:90:66
++  --> $DIR/format_args.rs:87: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:91:77
++  --> $DIR/format_args.rs:88: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:92:70
++  --> $DIR/format_args.rs:89: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:93:73
++  --> $DIR/format_args.rs:90: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:94:73
++  --> $DIR/format_args.rs:91: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:95:63
++  --> $DIR/format_args.rs:92: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:96:20
++  --> $DIR/format_args.rs:93: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:97:20
++  --> $DIR/format_args.rs:94: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:98:24
++  --> $DIR/format_args.rs:95: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:99:20
++  --> $DIR/format_args.rs:96: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:100:20
++  --> $DIR/format_args.rs:97: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:102:39
++  --> $DIR/format_args.rs:98:20
 +   |
 +LL |     println!("{}", x_ref.to_string());
 +   |                    ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:103:52
++  --> $DIR/format_args.rs:100:39
 +   |
 +LL |     println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
 +   |                                       ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:104:39
++  --> $DIR/format_args.rs:101:52
 +   |
 +LL |     println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
 +   |                                                    ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
-   --> $DIR/format_args.rs:105:52
++  --> $DIR/format_args.rs:102:39
 +   |
 +LL |     println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
 +   |                                       ^^^^^^^^^^^^ help: remove this
 +
 +error: `to_string` applied to a type that implements `Display` in `println!` args
- error: aborting due to 21 previous errors
++  --> $DIR/format_args.rs:103:52
 +   |
 +LL |     println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
 +   |                                                    ^^^^^^^^^^^^ help: remove this
 +
++error: `to_string` applied to a type that implements `Display` in `format!` args
++  --> $DIR/format_args.rs:142:38
++   |
++LL |         let x = format!("{} {}", a, b.to_string());
++   |                                      ^^^^^^^^^^^^ help: remove this
++
++error: `to_string` applied to a type that implements `Display` in `println!` args
++  --> $DIR/format_args.rs:156:24
++   |
++LL |         println!("{}", original[..10].to_string());
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]`
++
++error: aborting due to 23 previous errors
 +
index 5f9cebe212abd89844733156582e3c64771c0524,0000000000000000000000000000000000000000..fa564e23cd275cb270a64ae9510504e91c81657d
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,119 @@@
-     let mut a = A("".into());
 +// run-rustfix
 +
 +#![warn(clippy::identity_op)]
 +#![allow(
 +    clippy::eq_op,
 +    clippy::no_effect,
 +    clippy::unnecessary_operation,
 +    clippy::op_ref,
 +    clippy::double_parens,
 +    unused
 +)]
 +
 +use std::fmt::Write as _;
 +
 +const ONE: i64 = 1;
 +const NEG_ONE: i64 = -1;
 +const ZERO: i64 = 0;
 +
 +struct A(String);
 +
 +impl std::ops::Shl<i32> for A {
 +    type Output = A;
 +    fn shl(mut self, other: i32) -> Self {
 +        let _ = write!(self.0, "{}", other);
 +        self
 +    }
 +}
 +
 +struct Length(u8);
 +struct Meter;
 +
 +impl core::ops::Mul<Meter> for u8 {
 +    type Output = Length;
 +    fn mul(self, _: Meter) -> Length {
 +        Length(self)
 +    }
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    let x = 0;
 +
 +    x;
 +    x;
 +    x + 1;
 +    x;
 +    1 + x;
 +    x - ZERO; //no error, as we skip lookups (for now)
 +    x;
 +    ((ZERO)) | x; //no error, as we skip lookups (for now)
 +
 +    x;
 +    x;
 +    x / ONE; //no error, as we skip lookups (for now)
 +
 +    x / 2; //no false positive
 +
 +    x & NEG_ONE; //no error, as we skip lookups (for now)
 +    x;
 +
 +    let u: u8 = 0;
 +    u;
 +
 +    1 << 0; // no error, this case is allowed, see issue 3430
 +    42;
 +    1;
 +    42;
 +    &x;
 +    x;
 +
++    let mut a = A(String::new());
 +    let b = a << 0; // no error: non-integer
 +
 +    1 * Meter; // no error: non-integer
 +
 +    2;
 +    -2;
 +    2 + x;
 +    -2 + x;
 +    x + 1;
 +    (x + 1) % 3; // no error
 +    4 % 3; // no error
 +    4 % -3; // no error
 +
 +    // See #8724
 +    let a = 0;
 +    let b = true;
 +    (if b { 1 } else { 2 });
 +    (if b { 1 } else { 2 }) + if b { 3 } else { 4 };
 +    (match a { 0 => 10, _ => 20 });
 +    (match a { 0 => 10, _ => 20 }) + match a { 0 => 30, _ => 40 };
 +    (if b { 1 } else { 2 }) + match a { 0 => 30, _ => 40 };
 +    (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
 +    (if b { 1 } else { 2 });
 +
 +    ({ a }) + 3;
 +    ({ a } * 2);
 +    (loop { let mut c = 0; if c == 10 { break c; } c += 1; }) + { a * 2 };
 +
 +    fn f(_: i32) {
 +        todo!();
 +    }
 +    f(a + { 8 * 5 });
 +    f(if b { 1 } else { 2 } + 3);
 +    const _: i32 = { 2 * 4 } + 3;
 +    const _: i32 = { 1 + 2 * 3 } + 3;
 +
 +    a as usize;
 +    let _ = a as usize;
 +    ({ a } as usize);
 +
 +    2 * { a };
 +    (({ a } + 4));
 +    1;
 +}
 +
 +pub fn decide(a: bool, b: bool) -> u32 {
 +    (if a { 1 } else { 2 }) + if b { 3 } else { 5 }
 +}
index ca799c9cfac0f8a4c7e82c24d2e605d770fec561,0000000000000000000000000000000000000000..3d06d2a73b628d7b760d3a8c3684c8d15c4ebde7
mode 100644,000000..100644
--- /dev/null
@@@ -1,119 -1,0 +1,119 @@@
-     let mut a = A("".into());
 +// run-rustfix
 +
 +#![warn(clippy::identity_op)]
 +#![allow(
 +    clippy::eq_op,
 +    clippy::no_effect,
 +    clippy::unnecessary_operation,
 +    clippy::op_ref,
 +    clippy::double_parens,
 +    unused
 +)]
 +
 +use std::fmt::Write as _;
 +
 +const ONE: i64 = 1;
 +const NEG_ONE: i64 = -1;
 +const ZERO: i64 = 0;
 +
 +struct A(String);
 +
 +impl std::ops::Shl<i32> for A {
 +    type Output = A;
 +    fn shl(mut self, other: i32) -> Self {
 +        let _ = write!(self.0, "{}", other);
 +        self
 +    }
 +}
 +
 +struct Length(u8);
 +struct Meter;
 +
 +impl core::ops::Mul<Meter> for u8 {
 +    type Output = Length;
 +    fn mul(self, _: Meter) -> Length {
 +        Length(self)
 +    }
 +}
 +
 +#[rustfmt::skip]
 +fn main() {
 +    let x = 0;
 +
 +    x + 0;
 +    x + (1 - 1);
 +    x + 1;
 +    0 + x;
 +    1 + x;
 +    x - ZERO; //no error, as we skip lookups (for now)
 +    x | (0);
 +    ((ZERO)) | x; //no error, as we skip lookups (for now)
 +
 +    x * 1;
 +    1 * x;
 +    x / ONE; //no error, as we skip lookups (for now)
 +
 +    x / 2; //no false positive
 +
 +    x & NEG_ONE; //no error, as we skip lookups (for now)
 +    -1 & x;
 +
 +    let u: u8 = 0;
 +    u & 255;
 +
 +    1 << 0; // no error, this case is allowed, see issue 3430
 +    42 << 0;
 +    1 >> 0;
 +    42 >> 0;
 +    &x >> 0;
 +    x >> &0;
 +
++    let mut a = A(String::new());
 +    let b = a << 0; // no error: non-integer
 +
 +    1 * Meter; // no error: non-integer
 +
 +    2 % 3;
 +    -2 % 3;
 +    2 % -3 + x;
 +    -2 % -3 + x;
 +    x + 1 % 3;
 +    (x + 1) % 3; // no error
 +    4 % 3; // no error
 +    4 % -3; // no error
 +
 +    // See #8724
 +    let a = 0;
 +    let b = true;
 +    0 + if b { 1 } else { 2 };
 +    0 + if b { 1 } else { 2 } + if b { 3 } else { 4 };
 +    0 + match a { 0 => 10, _ => 20 };
 +    0 + match a { 0 => 10, _ => 20 } + match a { 0 => 30, _ => 40 };
 +    0 + if b { 1 } else { 2 } + match a { 0 => 30, _ => 40 };
 +    0 + match a { 0 => 10, _ => 20 } + if b { 3 } else { 4 };
 +    (if b { 1 } else { 2 }) + 0;
 +
 +    0 + { a } + 3;
 +    0 + { a } * 2;
 +    0 + loop { let mut c = 0; if c == 10 { break c; } c += 1; } + { a * 2 };
 +
 +    fn f(_: i32) {
 +        todo!();
 +    }
 +    f(1 * a + { 8 * 5 });
 +    f(0 + if b { 1 } else { 2 } + 3);
 +    const _: i32 = { 2 * 4 } + 0 + 3;
 +    const _: i32 = 0 + { 1 + 2 * 3 } + 3;
 +
 +    0 + a as usize;
 +    let _ = 0 + a as usize;
 +    0 + { a } as usize;
 +
 +    2 * (0 + { a });
 +    1 * ({ a } + 4);
 +    1 * 1;
 +}
 +
 +pub fn decide(a: bool, b: bool) -> u32 {
 +    0 + if a { 1 } else { 2 } + if b { 3 } else { 5 }
 +}
index 6cbfafbb38b9913e7ec71de26f6206fce73ae1cf,0000000000000000000000000000000000000000..321feb0224ed11a97d111f645b8c77b7af073b89
mode 100644,000000..100644
--- /dev/null
@@@ -1,42 -1,0 +1,50 @@@
 +#![warn(clippy::if_let_mutex)]
 +
 +use std::ops::Deref;
 +use std::sync::Mutex;
 +
 +fn do_stuff<T>(_: T) {}
 +
 +fn if_let() {
 +    let m = Mutex::new(1_u8);
 +    if let Err(locked) = m.lock() {
 +        do_stuff(locked);
 +    } else {
 +        let lock = m.lock().unwrap();
 +        do_stuff(lock);
 +    };
 +}
 +
 +// This is the most common case as the above case is pretty
 +// contrived.
 +fn if_let_option() {
 +    let m = Mutex::new(Some(0_u8));
 +    if let Some(locked) = m.lock().unwrap().deref() {
 +        do_stuff(locked);
 +    } else {
 +        let lock = m.lock().unwrap();
 +        do_stuff(lock);
 +    };
 +}
 +
 +// When mutexes are different don't warn
 +fn if_let_different_mutex() {
 +    let m = Mutex::new(Some(0_u8));
 +    let other = Mutex::new(None::<u8>);
 +    if let Some(locked) = m.lock().unwrap().deref() {
 +        do_stuff(locked);
 +    } else {
 +        let lock = other.lock().unwrap();
 +        do_stuff(lock);
 +    };
 +}
 +
++fn mutex_ref(mutex: &Mutex<i32>) {
++    if let Ok(i) = mutex.lock() {
++        do_stuff(i);
++    } else {
++        let _x = mutex.lock();
++    };
++}
++
 +fn main() {}
index e9c4d9163328f3066e9c67f244a8ce233c35555a,0000000000000000000000000000000000000000..8a4d5dbac592b33d12a0d3b0061413a719e17248
mode 100644,000000..100644
--- /dev/null
@@@ -1,29 -1,0 +1,53 @@@
- LL | /     if let Err(locked) = m.lock() {
 +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
 +  --> $DIR/if_let_mutex.rs:10:5
 +   |
- LL | /     if let Some(locked) = m.lock().unwrap().deref() {
++LL |       if let Err(locked) = m.lock() {
++   |       ^                    - this Mutex will remain locked for the entire `if let`-block...
++   |  _____|
++   | |
 +LL | |         do_stuff(locked);
 +LL | |     } else {
 +LL | |         let lock = m.lock().unwrap();
++   | |                    - ... and is tried to lock again here, which will always deadlock.
 +LL | |         do_stuff(lock);
 +LL | |     };
 +   | |_____^
 +   |
 +   = note: `-D clippy::if-let-mutex` implied by `-D warnings`
 +   = help: move the lock call outside of the `if let ...` expression
 +
 +error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
 +  --> $DIR/if_let_mutex.rs:22:5
 +   |
- error: aborting due to 2 previous errors
++LL |       if let Some(locked) = m.lock().unwrap().deref() {
++   |       ^                     - this Mutex will remain locked for the entire `if let`-block...
++   |  _____|
++   | |
 +LL | |         do_stuff(locked);
 +LL | |     } else {
 +LL | |         let lock = m.lock().unwrap();
++   | |                    - ... and is tried to lock again here, which will always deadlock.
 +LL | |         do_stuff(lock);
 +LL | |     };
 +   | |_____^
 +   |
 +   = help: move the lock call outside of the `if let ...` expression
 +
++error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
++  --> $DIR/if_let_mutex.rs:43:5
++   |
++LL |       if let Ok(i) = mutex.lock() {
++   |       ^              ----- this Mutex will remain locked for the entire `if let`-block...
++   |  _____|
++   | |
++LL | |         do_stuff(i);
++LL | |     } else {
++LL | |         let _x = mutex.lock();
++   | |                  ----- ... and is tried to lock again here, which will always deadlock.
++LL | |     };
++   | |_____^
++   |
++   = help: move the lock call outside of the `if let ...` expression
++
++error: aborting due to 3 previous errors
 +
index 8cb22d569f4cc90bca59bf68e2971bb5e8415458,0000000000000000000000000000000000000000..c22ace30d2dc21defe9cefa3108599f228860f27
mode 100644,000000..100644
--- /dev/null
@@@ -1,61 -1,0 +1,61 @@@
- error: this could be simplified with `bool::then`
 +error: this could be simplified with `bool::then`
 +  --> $DIR/if_then_some_else_none.rs:6:13
 +   |
 +LL |       let _ = if foo() {
 +   |  _____________^
 +LL | |         println!("true!");
 +LL | |         Some("foo")
 +LL | |     } else {
 +LL | |         None
 +LL | |     };
 +   | |_____^
 +   |
 +   = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
 +   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })`
 +
 +error: this could be simplified with `bool::then`
 +  --> $DIR/if_then_some_else_none.rs:14:13
 +   |
 +LL |       let _ = if matches!(true, true) {
 +   |  _____________^
 +LL | |         println!("true!");
 +LL | |         Some(matches!(true, false))
 +LL | |     } else {
 +LL | |         None
 +LL | |     };
 +   | |_____^
 +   |
 +   = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
 +
-    = help: consider using `bool::then` like: `(o < 32).then(|| o)`
++error: this could be simplified with `bool::then_some`
 +  --> $DIR/if_then_some_else_none.rs:23:28
 +   |
 +LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
 +   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
- error: this could be simplified with `bool::then`
++   = help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
 +
-    = help: consider using `bool::then` like: `(!x).then(|| 0)`
++error: this could be simplified with `bool::then_some`
 +  --> $DIR/if_then_some_else_none.rs:27:13
 +   |
 +LL |     let _ = if !x { Some(0) } else { None };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
++   = help: consider using `bool::then_some` like: `(!x).then_some(0)`
 +
 +error: this could be simplified with `bool::then`
 +  --> $DIR/if_then_some_else_none.rs:82:13
 +   |
 +LL |       let _ = if foo() {
 +   |  _____________^
 +LL | |         println!("true!");
 +LL | |         Some(150)
 +LL | |     } else {
 +LL | |         None
 +LL | |     };
 +   | |_____^
 +   |
 +   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })`
 +
 +error: aborting due to 5 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bd9b07aefbfb85afd0a64ea4d96f1436a7f9d87b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++#![warn(clippy::iter_on_empty_collections)]
++#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
++
++fn array() {
++    assert_eq!(std::iter::empty().next(), Option::<i32>::None);
++    assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None);
++    assert_eq!(std::iter::empty().next(), Option::<&i32>::None);
++    assert_eq!(std::iter::empty().next(), Option::<i32>::None);
++    assert_eq!(std::iter::empty().next(), Option::<&mut i32>::None);
++    assert_eq!(std::iter::empty().next(), Option::<&i32>::None);
++
++    // Don't trigger on non-iter methods
++    let _: Option<String> = None.clone();
++    let _: [String; 0] = [].clone();
++
++    // Don't trigger on match or if branches
++    let _ = match 123 {
++        123 => [].iter(),
++        _ => ["test"].iter(),
++    };
++
++    let _ = if false { ["test"].iter() } else { [].iter() };
++}
++
++macro_rules! in_macros {
++    () => {
++        assert_eq!([].into_iter().next(), Option::<i32>::None);
++        assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
++        assert_eq!([].iter().next(), Option::<&i32>::None);
++        assert_eq!(None.into_iter().next(), Option::<i32>::None);
++        assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
++        assert_eq!(None.iter().next(), Option::<&i32>::None);
++    };
++}
++
++// Don't trigger on a `None` that isn't std's option
++mod custom_option {
++    #[allow(unused)]
++    enum CustomOption {
++        Some(i32),
++        None,
++    }
++
++    impl CustomOption {
++        fn iter(&self) {}
++        fn iter_mut(&mut self) {}
++        fn into_iter(self) {}
++    }
++    use CustomOption::*;
++
++    pub fn custom_option() {
++        None.iter();
++        None.iter_mut();
++        None.into_iter();
++    }
++}
++
++fn main() {
++    array();
++    custom_option::custom_option();
++    in_macros!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e15ba94bd465528d9147ef1f83a66f0b2bf9d6b3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++#![warn(clippy::iter_on_empty_collections)]
++#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
++
++fn array() {
++    assert_eq!([].into_iter().next(), Option::<i32>::None);
++    assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
++    assert_eq!([].iter().next(), Option::<&i32>::None);
++    assert_eq!(None.into_iter().next(), Option::<i32>::None);
++    assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
++    assert_eq!(None.iter().next(), Option::<&i32>::None);
++
++    // Don't trigger on non-iter methods
++    let _: Option<String> = None.clone();
++    let _: [String; 0] = [].clone();
++
++    // Don't trigger on match or if branches
++    let _ = match 123 {
++        123 => [].iter(),
++        _ => ["test"].iter(),
++    };
++
++    let _ = if false { ["test"].iter() } else { [].iter() };
++}
++
++macro_rules! in_macros {
++    () => {
++        assert_eq!([].into_iter().next(), Option::<i32>::None);
++        assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
++        assert_eq!([].iter().next(), Option::<&i32>::None);
++        assert_eq!(None.into_iter().next(), Option::<i32>::None);
++        assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
++        assert_eq!(None.iter().next(), Option::<&i32>::None);
++    };
++}
++
++// Don't trigger on a `None` that isn't std's option
++mod custom_option {
++    #[allow(unused)]
++    enum CustomOption {
++        Some(i32),
++        None,
++    }
++
++    impl CustomOption {
++        fn iter(&self) {}
++        fn iter_mut(&mut self) {}
++        fn into_iter(self) {}
++    }
++    use CustomOption::*;
++
++    pub fn custom_option() {
++        None.iter();
++        None.iter_mut();
++        None.into_iter();
++    }
++}
++
++fn main() {
++    array();
++    custom_option::custom_option();
++    in_macros!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cbd611769569670ab06ce18c3fbde8bba2308ddb
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: `into_iter` call on an empty collection
++  --> $DIR/iter_on_empty_collections.rs:6:16
++   |
++LL |     assert_eq!([].into_iter().next(), Option::<i32>::None);
++   |                ^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
++   |
++   = note: `-D clippy::iter-on-empty-collections` implied by `-D warnings`
++
++error: `iter_mut` call on an empty collection
++  --> $DIR/iter_on_empty_collections.rs:7:16
++   |
++LL |     assert_eq!([].iter_mut().next(), Option::<&mut i32>::None);
++   |                ^^^^^^^^^^^^^ help: try: `std::iter::empty()`
++
++error: `iter` call on an empty collection
++  --> $DIR/iter_on_empty_collections.rs:8:16
++   |
++LL |     assert_eq!([].iter().next(), Option::<&i32>::None);
++   |                ^^^^^^^^^ help: try: `std::iter::empty()`
++
++error: `into_iter` call on an empty collection
++  --> $DIR/iter_on_empty_collections.rs:9:16
++   |
++LL |     assert_eq!(None.into_iter().next(), Option::<i32>::None);
++   |                ^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
++
++error: `iter_mut` call on an empty collection
++  --> $DIR/iter_on_empty_collections.rs:10:16
++   |
++LL |     assert_eq!(None.iter_mut().next(), Option::<&mut i32>::None);
++   |                ^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
++
++error: `iter` call on an empty collection
++  --> $DIR/iter_on_empty_collections.rs:11:16
++   |
++LL |     assert_eq!(None.iter().next(), Option::<&i32>::None);
++   |                ^^^^^^^^^^^ help: try: `std::iter::empty()`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1fa4b03641bc73334438a452644037a410b6cbcc
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++#![warn(clippy::iter_on_single_items)]
++#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
++
++fn array() {
++    assert_eq!(std::iter::once(123).next(), Some(123));
++    assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123));
++    assert_eq!(std::iter::once(&123).next(), Some(&123));
++    assert_eq!(std::iter::once(123).next(), Some(123));
++    assert_eq!(std::iter::once(&mut 123).next(), Some(&mut 123));
++    assert_eq!(std::iter::once(&123).next(), Some(&123));
++
++    // Don't trigger on non-iter methods
++    let _: Option<String> = Some("test".to_string()).clone();
++    let _: [String; 1] = ["test".to_string()].clone();
++
++    // Don't trigger on match or if branches
++    let _ = match 123 {
++        123 => [].iter(),
++        _ => ["test"].iter(),
++    };
++
++    let _ = if false { ["test"].iter() } else { [].iter() };
++}
++
++macro_rules! in_macros {
++    () => {
++        assert_eq!([123].into_iter().next(), Some(123));
++        assert_eq!([123].iter_mut().next(), Some(&mut 123));
++        assert_eq!([123].iter().next(), Some(&123));
++        assert_eq!(Some(123).into_iter().next(), Some(123));
++        assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
++        assert_eq!(Some(123).iter().next(), Some(&123));
++    };
++}
++
++// Don't trigger on a `Some` that isn't std's option
++mod custom_option {
++    #[allow(unused)]
++    enum CustomOption {
++        Some(i32),
++        None,
++    }
++
++    impl CustomOption {
++        fn iter(&self) {}
++        fn iter_mut(&mut self) {}
++        fn into_iter(self) {}
++    }
++    use CustomOption::*;
++
++    pub fn custom_option() {
++        Some(3).iter();
++        Some(3).iter_mut();
++        Some(3).into_iter();
++    }
++}
++
++fn main() {
++    array();
++    custom_option::custom_option();
++    in_macros!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ea96d8066c568588ce493bb1394705e5b313a4dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++#![warn(clippy::iter_on_single_items)]
++#![allow(clippy::iter_next_slice, clippy::redundant_clone)]
++
++fn array() {
++    assert_eq!([123].into_iter().next(), Some(123));
++    assert_eq!([123].iter_mut().next(), Some(&mut 123));
++    assert_eq!([123].iter().next(), Some(&123));
++    assert_eq!(Some(123).into_iter().next(), Some(123));
++    assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
++    assert_eq!(Some(123).iter().next(), Some(&123));
++
++    // Don't trigger on non-iter methods
++    let _: Option<String> = Some("test".to_string()).clone();
++    let _: [String; 1] = ["test".to_string()].clone();
++
++    // Don't trigger on match or if branches
++    let _ = match 123 {
++        123 => [].iter(),
++        _ => ["test"].iter(),
++    };
++
++    let _ = if false { ["test"].iter() } else { [].iter() };
++}
++
++macro_rules! in_macros {
++    () => {
++        assert_eq!([123].into_iter().next(), Some(123));
++        assert_eq!([123].iter_mut().next(), Some(&mut 123));
++        assert_eq!([123].iter().next(), Some(&123));
++        assert_eq!(Some(123).into_iter().next(), Some(123));
++        assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
++        assert_eq!(Some(123).iter().next(), Some(&123));
++    };
++}
++
++// Don't trigger on a `Some` that isn't std's option
++mod custom_option {
++    #[allow(unused)]
++    enum CustomOption {
++        Some(i32),
++        None,
++    }
++
++    impl CustomOption {
++        fn iter(&self) {}
++        fn iter_mut(&mut self) {}
++        fn into_iter(self) {}
++    }
++    use CustomOption::*;
++
++    pub fn custom_option() {
++        Some(3).iter();
++        Some(3).iter_mut();
++        Some(3).into_iter();
++    }
++}
++
++fn main() {
++    array();
++    custom_option::custom_option();
++    in_macros!();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d6c547116363af8d77aee5d37feda615d0ac2541
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: `into_iter` call on a collection with only one item
++  --> $DIR/iter_on_single_items.rs:6:16
++   |
++LL |     assert_eq!([123].into_iter().next(), Some(123));
++   |                ^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)`
++   |
++   = note: `-D clippy::iter-on-single-items` implied by `-D warnings`
++
++error: `iter_mut` call on a collection with only one item
++  --> $DIR/iter_on_single_items.rs:7:16
++   |
++LL |     assert_eq!([123].iter_mut().next(), Some(&mut 123));
++   |                ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)`
++
++error: `iter` call on a collection with only one item
++  --> $DIR/iter_on_single_items.rs:8:16
++   |
++LL |     assert_eq!([123].iter().next(), Some(&123));
++   |                ^^^^^^^^^^^^ help: try: `std::iter::once(&123)`
++
++error: `into_iter` call on a collection with only one item
++  --> $DIR/iter_on_single_items.rs:9:16
++   |
++LL |     assert_eq!(Some(123).into_iter().next(), Some(123));
++   |                ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(123)`
++
++error: `iter_mut` call on a collection with only one item
++  --> $DIR/iter_on_single_items.rs:10:16
++   |
++LL |     assert_eq!(Some(123).iter_mut().next(), Some(&mut 123));
++   |                ^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&mut 123)`
++
++error: `iter` call on a collection with only one item
++  --> $DIR/iter_on_single_items.rs:11:16
++   |
++LL |     assert_eq!(Some(123).iter().next(), Some(&123));
++   |                ^^^^^^^^^^^^^^^^ help: try: `std::iter::once(&123)`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a376411bfbc804e3019f409f80c57050ff1fb4f6
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++
++#![warn(clippy::manual_string_new)]
++
++macro_rules! create_strings_from_macro {
++    // When inside a macro, nothing should warn to prevent false positives.
++    ($some_str:expr) => {
++        let _: String = $some_str.into();
++        let _ = $some_str.to_string();
++    };
++}
++
++fn main() {
++    // Method calls
++    let _ = String::new();
++    let _ = "no warning".to_string();
++
++    let _ = String::new();
++    let _ = "no warning".to_owned();
++
++    let _: String = String::new();
++    let _: String = "no warning".into();
++
++    let _: SomeOtherStruct = "no warning".into();
++    let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
++
++    // Calls
++    let _ = String::new();
++    let _ = String::new();
++    let _ = String::from("no warning");
++    let _ = SomeOtherStruct::from("no warning");
++    let _ = SomeOtherStruct::from(""); // Again: no warning.
++
++    let _ = String::new();
++    let _ = String::try_from("no warning").unwrap();
++    let _ = String::try_from("no warning").expect("this should not warn");
++    let _ = SomeOtherStruct::try_from("no warning").unwrap();
++    let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
++
++    let _: String = String::new();
++    let _: String = From::from("no warning");
++    let _: SomeOtherStruct = From::from("no warning");
++    let _: SomeOtherStruct = From::from(""); // Again: no warning.
++
++    let _: String = String::new();
++    let _: String = TryFrom::try_from("no warning").unwrap();
++    let _: String = TryFrom::try_from("no warning").expect("this should not warn");
++    let _: String = String::new();
++    let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
++    let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
++
++    // Macros (never warn)
++    create_strings_from_macro!("");
++    create_strings_from_macro!("Hey");
++}
++
++struct SomeOtherStruct {}
++
++impl From<&str> for SomeOtherStruct {
++    fn from(_value: &str) -> Self {
++        Self {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6bfc52fb1bce3ef6ecb8b0804af955d1fdd1b2f8
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++// run-rustfix
++
++#![warn(clippy::manual_string_new)]
++
++macro_rules! create_strings_from_macro {
++    // When inside a macro, nothing should warn to prevent false positives.
++    ($some_str:expr) => {
++        let _: String = $some_str.into();
++        let _ = $some_str.to_string();
++    };
++}
++
++fn main() {
++    // Method calls
++    let _ = "".to_string();
++    let _ = "no warning".to_string();
++
++    let _ = "".to_owned();
++    let _ = "no warning".to_owned();
++
++    let _: String = "".into();
++    let _: String = "no warning".into();
++
++    let _: SomeOtherStruct = "no warning".into();
++    let _: SomeOtherStruct = "".into(); // No warning too. We are not converting into String.
++
++    // Calls
++    let _ = String::from("");
++    let _ = <String>::from("");
++    let _ = String::from("no warning");
++    let _ = SomeOtherStruct::from("no warning");
++    let _ = SomeOtherStruct::from(""); // Again: no warning.
++
++    let _ = String::try_from("").unwrap();
++    let _ = String::try_from("no warning").unwrap();
++    let _ = String::try_from("no warning").expect("this should not warn");
++    let _ = SomeOtherStruct::try_from("no warning").unwrap();
++    let _ = SomeOtherStruct::try_from("").unwrap(); // Again: no warning.
++
++    let _: String = From::from("");
++    let _: String = From::from("no warning");
++    let _: SomeOtherStruct = From::from("no warning");
++    let _: SomeOtherStruct = From::from(""); // Again: no warning.
++
++    let _: String = TryFrom::try_from("").unwrap();
++    let _: String = TryFrom::try_from("no warning").unwrap();
++    let _: String = TryFrom::try_from("no warning").expect("this should not warn");
++    let _: String = TryFrom::try_from("").expect("this should warn");
++    let _: SomeOtherStruct = TryFrom::try_from("no_warning").unwrap();
++    let _: SomeOtherStruct = TryFrom::try_from("").unwrap(); // Again: no warning.
++
++    // Macros (never warn)
++    create_strings_from_macro!("");
++    create_strings_from_macro!("Hey");
++}
++
++struct SomeOtherStruct {}
++
++impl From<&str> for SomeOtherStruct {
++    fn from(_value: &str) -> Self {
++        Self {}
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e5ecfc61947a35f357e236eddf9f1bdf44914f3e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,58 @@@
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:15:13
++   |
++LL |     let _ = "".to_string();
++   |             ^^^^^^^^^^^^^^ help: consider using: `String::new()`
++   |
++   = note: `-D clippy::manual-string-new` implied by `-D warnings`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:18:13
++   |
++LL |     let _ = "".to_owned();
++   |             ^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:21:21
++   |
++LL |     let _: String = "".into();
++   |                     ^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:28:13
++   |
++LL |     let _ = String::from("");
++   |             ^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:29:13
++   |
++LL |     let _ = <String>::from("");
++   |             ^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:34:13
++   |
++LL |     let _ = String::try_from("").unwrap();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:40:21
++   |
++LL |     let _: String = From::from("");
++   |                     ^^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:45:21
++   |
++LL |     let _: String = TryFrom::try_from("").unwrap();
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: empty String is being created manually
++  --> $DIR/manual_string_new.rs:48:21
++   |
++LL |     let _: String = TryFrom::try_from("").expect("this should warn");
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::new()`
++
++error: aborting due to 9 previous errors
++
index 1ccbfda64b73a5240ae68929f83c37d07cd66c05,0000000000000000000000000000000000000000..95ca571d07bfbf88fcce5d626814d6f210156ee2
mode 100644,000000..100644
--- /dev/null
@@@ -1,170 -1,0 +1,195 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = matches!(x, Some(0));
 +
 +    // Lint
 +    let _w = matches!(x, Some(_));
 +
 +    // Turn into is_none
 +    let _z = x.is_none();
 +
 +    // Lint
 +    let _zz = !matches!(x, Some(r) if r == 0);
 +
 +    // Lint
 +    let _zzz = matches!(x, Some(5));
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        // skip rustfmt to prevent removing block for first pattern
 +        #[rustfmt::skip]
 +        let _ans = matches!(x, E::A(_) | E::B(_));
 +    }
 +    {
 +        // lint
 +        let _ans = !matches!(x, E::B(_) | E::C);
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = matches!(z, Some(3));
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = matches!(&z, Some(3));
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = matches!(&z, AnEnum::X);
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = matches!(&val, &Some(ref _a));
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
++
++    let x = ' ';
++    // ignore if match block contains comment
++    let _line_comments = match x {
++        // numbers are bad!
++        '1' | '2' | '3' => true,
++        // spaces are very important to be true.
++        ' ' => true,
++        // as are dots
++        '.' => true,
++        _ => false,
++    };
++
++    let _block_comments = match x {
++        /* numbers are bad!
++         */
++        '1' | '2' | '3' => true,
++        /* spaces are very important to be true.
++         */
++        ' ' => true,
++        /* as are dots
++         */
++        '.' => true,
++        _ => false,
++    };
 +}
index a49991f594174473c51cc41609d3f808b1d80768,0000000000000000000000000000000000000000..3b9c8cadadcc417ebbe731a3735dd98580837e46
mode 100644,000000..100644
--- /dev/null
@@@ -1,211 -1,0 +1,236 @@@
 +// run-rustfix
 +
 +#![warn(clippy::match_like_matches_macro)]
 +#![allow(unreachable_patterns, dead_code, clippy::equatable_if_let)]
 +
 +fn main() {
 +    let x = Some(5);
 +
 +    // Lint
 +    let _y = match x {
 +        Some(0) => true,
 +        _ => false,
 +    };
 +
 +    // Lint
 +    let _w = match x {
 +        Some(_) => true,
 +        _ => false,
 +    };
 +
 +    // Turn into is_none
 +    let _z = match x {
 +        Some(_) => false,
 +        None => true,
 +    };
 +
 +    // Lint
 +    let _zz = match x {
 +        Some(r) if r == 0 => false,
 +        _ => true,
 +    };
 +
 +    // Lint
 +    let _zzz = if let Some(5) = x { true } else { false };
 +
 +    // No lint
 +    let _a = match x {
 +        Some(_) => false,
 +        _ => false,
 +    };
 +
 +    // No lint
 +    let _ab = match x {
 +        Some(0) => false,
 +        _ => true,
 +        None => false,
 +    };
 +
 +    enum E {
 +        A(u32),
 +        B(i32),
 +        C,
 +        D,
 +    }
 +    let x = E::A(2);
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        // skip rustfmt to prevent removing block for first pattern
 +        #[rustfmt::skip]
 +        let _ans = match x {
 +            E::A(_) => {
 +                true
 +            }
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +    {
 +        // lint
 +        let _ans = match x {
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => false,
 +            E::C => true,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => true,
 +            E::B(_) => false,
 +            E::C => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) if a < 10 => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(a) if a < 10 => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(a) => a == 10,
 +            E::B(_) => false,
 +            _ => true,
 +        };
 +    }
 +    {
 +        // no lint
 +        let _ans = match x {
 +            E::A(_) => false,
 +            E::B(_) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // should print "z" in suggestion (#6503)
 +        let z = &Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        // this could also print "z" in suggestion..?
 +        let z = Some(3);
 +        let _z = match &z {
 +            Some(3) => true,
 +            _ => false,
 +        };
 +    }
 +
 +    {
 +        enum AnEnum {
 +            X,
 +            Y,
 +        }
 +
 +        fn foo(_x: AnEnum) {}
 +
 +        fn main() {
 +            let z = AnEnum::X;
 +            // we can't remove the reference here!
 +            let _ = match &z {
 +                AnEnum::X => true,
 +                _ => false,
 +            };
 +            foo(z);
 +        }
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        // we need the reference here because later val is consumed by fun()
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        struct S(i32);
 +
 +        fn fun(_val: Option<S>) {}
 +        let val = Some(S(42));
 +        let _res = match &val {
 +            &Some(ref _a) => true,
 +            _ => false,
 +        };
 +        fun(val);
 +    }
 +
 +    {
 +        enum E {
 +            A,
 +            B,
 +            C,
 +        }
 +
 +        let _ = match E::A {
 +            E::B => true,
 +            #[cfg(feature = "foo")]
 +            E::A => true,
 +            _ => false,
 +        };
 +    }
++
++    let x = ' ';
++    // ignore if match block contains comment
++    let _line_comments = match x {
++        // numbers are bad!
++        '1' | '2' | '3' => true,
++        // spaces are very important to be true.
++        ' ' => true,
++        // as are dots
++        '.' => true,
++        _ => false,
++    };
++
++    let _block_comments = match x {
++        /* numbers are bad!
++         */
++        '1' | '2' | '3' => true,
++        /* spaces are very important to be true.
++         */
++        ' ' => true,
++        /* as are dots
++         */
++        '.' => true,
++        _ => false,
++    };
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b186bf8bbdb424874495f06e80a9a1ddd6cedef3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++#![warn(clippy::multi_assignments)]
++fn main() {
++    let (mut a, mut b, mut c, mut d) = ((), (), (), ());
++    a = b = c;
++    a = b = c = d;
++    a = b = { c };
++    a = { b = c };
++    a = (b = c);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d6c42bb698cf97f34c5c02e30f9fd9e456bf6551
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,40 @@@
++error: assignments don't nest intuitively
++  --> $DIR/multi_assignments.rs:4:5
++   |
++LL |     a = b = c;
++   |     ^^^^^^^^^
++   |
++   = note: `-D clippy::multi-assignments` implied by `-D warnings`
++
++error: assignments don't nest intuitively
++  --> $DIR/multi_assignments.rs:5:5
++   |
++LL |     a = b = c = d;
++   |     ^^^^^^^^^^^^^
++
++error: assignments don't nest intuitively
++  --> $DIR/multi_assignments.rs:5:9
++   |
++LL |     a = b = c = d;
++   |         ^^^^^^^^^
++
++error: assignments don't nest intuitively
++  --> $DIR/multi_assignments.rs:6:5
++   |
++LL |     a = b = { c };
++   |     ^^^^^^^^^^^^^
++
++error: assignments don't nest intuitively
++  --> $DIR/multi_assignments.rs:7:5
++   |
++LL |     a = { b = c };
++   |     ^^^^^^^^^^^^^
++
++error: assignments don't nest intuitively
++  --> $DIR/multi_assignments.rs:8:5
++   |
++LL |     a = (b = c);
++   |     ^^^^^^^^^^^
++
++error: aborting due to 6 previous errors
++
index bfd2725ecaaa874b9409502e5b72514663ad7cb8,0000000000000000000000000000000000000000..8cf93bd248173ee93cef0b0ec5e87815e11f370b
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,300 @@@
- #![feature(lint_reasons)]
 +// run-rustfix
 +
++#![feature(custom_inner_attributes, lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 +fn main() {
 +    let a = 5;
 +    let ref_a = &a;
 +    let _ = x(&a); // no warning
 +    let _ = x(&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &a
 +        },
 +        46 => &a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&a);
 +    let _ = x(&a);
 +    let _ = x(&mut b);
 +    let _ = x(ref_a);
 +    {
 +        let b = &mut b;
 +        x(b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(x);
 +    mut_ref(x);
 +    let y: &mut i32 = x;
 +    let y: &mut i32 = x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = x.0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 +
 +    struct S;
 +    impl From<S> for u32 {
 +        fn from(s: S) -> Self {
 +            (&s).into()
 +        }
 +    }
 +    impl From<&S> for u32 {
 +        fn from(s: &S) -> Self {
 +            0
 +        }
 +    }
++
++    let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
++    let _ = std::path::Path::new(".").join(".");
++    deref_target_is_x(X);
++    multiple_constraints([[""]]);
++    multiple_constraints_normalizes_to_same(X, X);
++    let _ = Some("").unwrap_or("");
++
++    only_sized(&""); // Don't lint. `Sized` is only bound
++    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
++    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
++    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
++    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
++    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 +}
 +
 +#[allow(clippy::needless_borrowed_reference)]
 +fn x(y: &i32) -> i32 {
 +    *y
 +}
 +
 +fn mut_ref(y: &mut i32) {
 +    *y = 5;
 +}
 +
 +fn f<T: Copy>(y: &T) -> T {
 +    *y
 +}
 +
 +fn g(y: &[u8]) -> u8 {
 +    y[0]
 +}
 +
 +trait Trait {}
 +
 +impl<'a> Trait for &'a str {}
 +
 +fn h(_: &dyn Trait) {}
 +
 +#[allow(dead_code)]
 +fn check_expect_suppression() {
 +    let a = 5;
 +    #[expect(clippy::needless_borrow)]
 +    let _ = x(&&a);
 +}
 +
 +#[allow(dead_code)]
 +mod issue9160 {
 +    pub struct S<F> {
 +        f: F,
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: Fn() -> T,
 +    {
 +        fn calls_field(&self) -> T {
 +            (self.f)()
 +        }
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: FnMut() -> T,
 +    {
 +        fn calls_mut_field(&mut self) -> T {
 +            (self.f)()
 +        }
 +    }
 +}
++
++#[derive(Clone, Copy)]
++struct X;
++
++impl std::ops::Deref for X {
++    type Target = X;
++    fn deref(&self) -> &Self::Target {
++        self
++    }
++}
++
++fn deref_target_is_x<T>(_: T)
++where
++    T: std::ops::Deref<Target = X>,
++{
++}
++
++fn multiple_constraints<T, U, V, X, Y>(_: T)
++where
++    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
++    U: IntoIterator<Item = V>,
++    V: AsRef<str>,
++    X: IntoIterator<Item = Y>,
++    Y: AsRef<std::ffi::OsStr>,
++{
++}
++
++fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
++where
++    T: std::ops::Deref<Target = U>,
++    U: std::ops::Deref<Target = V>,
++{
++}
++
++fn only_sized<T>(_: T) {}
++
++fn ref_as_ref_path<T: 'static>(_: &'static T)
++where
++    &'static T: AsRef<std::path::Path>,
++{
++}
++
++trait RefsOnly {
++    type Referent;
++}
++
++impl<T> RefsOnly for &T {
++    type Referent = T;
++}
++
++fn refs_only<T, U>(_: T)
++where
++    T: RefsOnly<Referent = U>,
++{
++}
++
++fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
++where
++    T: IntoIterator<Item = U>,
++    U: IntoIterator<Item = V>,
++    V: AsRef<str>,
++{
++}
++
++// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
++#[allow(dead_code)]
++mod copyable_iterator {
++    #[derive(Clone, Copy)]
++    struct Iter;
++    impl Iterator for Iter {
++        type Item = ();
++        fn next(&mut self) -> Option<Self::Item> {
++            None
++        }
++    }
++    fn takes_iter(_: impl Iterator) {}
++    fn dont_warn(mut x: Iter) {
++        takes_iter(&mut x);
++    }
++    fn warn(mut x: &mut Iter) {
++        takes_iter(&mut x)
++    }
++}
++
++mod under_msrv {
++    #![allow(dead_code)]
++    #![clippy::msrv = "1.52.0"]
++
++    fn foo() {
++        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
++    }
++}
++
++mod meets_msrv {
++    #![allow(dead_code)]
++    #![clippy::msrv = "1.53.0"]
++
++    fn foo() {
++        let _ = std::process::Command::new("ls").args(["-a", "-l"]).status().unwrap();
++    }
++}
index c457d8c5471886f5491aba8bfc3c08f4a85d2621,0000000000000000000000000000000000000000..fd9b2a11df96f1cba7dcd147dd7d6ca96774435d
mode 100644,000000..100644
--- /dev/null
@@@ -1,185 -1,0 +1,300 @@@
- #![feature(lint_reasons)]
 +// run-rustfix
 +
++#![feature(custom_inner_attributes, lint_reasons)]
 +
 +#[warn(clippy::all, clippy::needless_borrow)]
 +#[allow(unused_variables, clippy::unnecessary_mut_passed)]
 +fn main() {
 +    let a = 5;
 +    let ref_a = &a;
 +    let _ = x(&a); // no warning
 +    let _ = x(&&a); // warn
 +
 +    let mut b = 5;
 +    mut_ref(&mut b); // no warning
 +    mut_ref(&mut &mut b); // warn
 +
 +    let s = &String::from("hi");
 +    let s_ident = f(&s); // should not error, because `&String` implements Copy, but `String` does not
 +    let g_val = g(&Vec::new()); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    let vec = Vec::new();
 +    let vec_val = g(&vec); // should not error, because `&Vec<T>` derefs to `&[T]`
 +    h(&"foo"); // should not error, because the `&&str` is required, due to `&Trait`
 +    let garbl = match 42 {
 +        44 => &a,
 +        45 => {
 +            println!("foo");
 +            &&a
 +        },
 +        46 => &&a,
 +        47 => {
 +            println!("foo");
 +            loop {
 +                println!("{}", a);
 +                if a == 25 {
 +                    break &ref_a;
 +                }
 +            }
 +        },
 +        _ => panic!(),
 +    };
 +
 +    let _ = x(&&&a);
 +    let _ = x(&mut &&a);
 +    let _ = x(&&&mut b);
 +    let _ = x(&&ref_a);
 +    {
 +        let b = &mut b;
 +        x(&b);
 +    }
 +
 +    // Issue #8191
 +    let mut x = 5;
 +    let mut x = &mut x;
 +
 +    mut_ref(&mut x);
 +    mut_ref(&mut &mut x);
 +    let y: &mut i32 = &mut x;
 +    let y: &mut i32 = &mut &mut x;
 +
 +    let y = match 0 {
 +        // Don't lint. Removing the borrow would move 'x'
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    let y: &mut i32 = match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    };
 +    fn ref_mut_i32(_: &mut i32) {}
 +    ref_mut_i32(match 0 {
 +        // Lint here. The type given above triggers auto-borrow.
 +        0 => &mut x,
 +        _ => &mut *x,
 +    });
 +    // use 'x' after to make sure it's still usable in the fixed code.
 +    *x = 5;
 +
 +    let s = String::new();
 +    // let _ = (&s).len();
 +    // let _ = (&s).capacity();
 +    // let _ = (&&s).capacity();
 +
 +    let x = (1, 2);
 +    let _ = (&x).0;
 +    let x = &x as *const (i32, i32);
 +    let _ = unsafe { (&*x).0 };
 +
 +    // Issue #8367
 +    trait Foo {
 +        fn foo(self);
 +    }
 +    impl Foo for &'_ () {
 +        fn foo(self) {}
 +    }
 +    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
 +    (&&()).foo();
 +
 +    impl Foo for i32 {
 +        fn foo(self) {}
 +    }
 +    impl Foo for &'_ i32 {
 +        fn foo(self) {}
 +    }
 +    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
 +    (&&5).foo();
 +
 +    trait FooRef {
 +        fn foo_ref(&self);
 +    }
 +    impl FooRef for () {
 +        fn foo_ref(&self) {}
 +    }
 +    impl FooRef for &'_ () {
 +        fn foo_ref(&self) {}
 +    }
 +    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 +
 +    struct S;
 +    impl From<S> for u32 {
 +        fn from(s: S) -> Self {
 +            (&s).into()
 +        }
 +    }
 +    impl From<&S> for u32 {
 +        fn from(s: &S) -> Self {
 +            0
 +        }
 +    }
++
++    let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
++    let _ = std::path::Path::new(".").join(&&".");
++    deref_target_is_x(&X);
++    multiple_constraints(&[[""]]);
++    multiple_constraints_normalizes_to_same(&X, X);
++    let _ = Some("").unwrap_or(&"");
++
++    only_sized(&""); // Don't lint. `Sized` is only bound
++    let _ = std::any::Any::type_id(&""); // Don't lint. `Any` is only bound
++    let _ = Box::new(&""); // Don't lint. Type parameter appears in return type
++    ref_as_ref_path(&""); // Don't lint. Argument type is not a type parameter
++    refs_only(&()); // Don't lint. `&T` implements trait, but `T` doesn't
++    multiple_constraints_normalizes_to_different(&[[""]], &[""]); // Don't lint. Projected type appears in arguments
 +}
 +
 +#[allow(clippy::needless_borrowed_reference)]
 +fn x(y: &i32) -> i32 {
 +    *y
 +}
 +
 +fn mut_ref(y: &mut i32) {
 +    *y = 5;
 +}
 +
 +fn f<T: Copy>(y: &T) -> T {
 +    *y
 +}
 +
 +fn g(y: &[u8]) -> u8 {
 +    y[0]
 +}
 +
 +trait Trait {}
 +
 +impl<'a> Trait for &'a str {}
 +
 +fn h(_: &dyn Trait) {}
 +
 +#[allow(dead_code)]
 +fn check_expect_suppression() {
 +    let a = 5;
 +    #[expect(clippy::needless_borrow)]
 +    let _ = x(&&a);
 +}
 +
 +#[allow(dead_code)]
 +mod issue9160 {
 +    pub struct S<F> {
 +        f: F,
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: Fn() -> T,
 +    {
 +        fn calls_field(&self) -> T {
 +            (&self.f)()
 +        }
 +    }
 +
 +    impl<T, F> S<F>
 +    where
 +        F: FnMut() -> T,
 +    {
 +        fn calls_mut_field(&mut self) -> T {
 +            (&mut self.f)()
 +        }
 +    }
 +}
++
++#[derive(Clone, Copy)]
++struct X;
++
++impl std::ops::Deref for X {
++    type Target = X;
++    fn deref(&self) -> &Self::Target {
++        self
++    }
++}
++
++fn deref_target_is_x<T>(_: T)
++where
++    T: std::ops::Deref<Target = X>,
++{
++}
++
++fn multiple_constraints<T, U, V, X, Y>(_: T)
++where
++    T: IntoIterator<Item = U> + IntoIterator<Item = X>,
++    U: IntoIterator<Item = V>,
++    V: AsRef<str>,
++    X: IntoIterator<Item = Y>,
++    Y: AsRef<std::ffi::OsStr>,
++{
++}
++
++fn multiple_constraints_normalizes_to_same<T, U, V>(_: T, _: V)
++where
++    T: std::ops::Deref<Target = U>,
++    U: std::ops::Deref<Target = V>,
++{
++}
++
++fn only_sized<T>(_: T) {}
++
++fn ref_as_ref_path<T: 'static>(_: &'static T)
++where
++    &'static T: AsRef<std::path::Path>,
++{
++}
++
++trait RefsOnly {
++    type Referent;
++}
++
++impl<T> RefsOnly for &T {
++    type Referent = T;
++}
++
++fn refs_only<T, U>(_: T)
++where
++    T: RefsOnly<Referent = U>,
++{
++}
++
++fn multiple_constraints_normalizes_to_different<T, U, V>(_: T, _: U)
++where
++    T: IntoIterator<Item = U>,
++    U: IntoIterator<Item = V>,
++    V: AsRef<str>,
++{
++}
++
++// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
++#[allow(dead_code)]
++mod copyable_iterator {
++    #[derive(Clone, Copy)]
++    struct Iter;
++    impl Iterator for Iter {
++        type Item = ();
++        fn next(&mut self) -> Option<Self::Item> {
++            None
++        }
++    }
++    fn takes_iter(_: impl Iterator) {}
++    fn dont_warn(mut x: Iter) {
++        takes_iter(&mut x);
++    }
++    fn warn(mut x: &mut Iter) {
++        takes_iter(&mut x)
++    }
++}
++
++mod under_msrv {
++    #![allow(dead_code)]
++    #![clippy::msrv = "1.52.0"]
++
++    fn foo() {
++        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
++    }
++}
++
++mod meets_msrv {
++    #![allow(dead_code)]
++    #![clippy::msrv = "1.53.0"]
++
++    fn foo() {
++        let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
++    }
++}
index 66588689d81851a8f81862bac08e1bf08ce95d58,0000000000000000000000000000000000000000..5af68706d4ba579311308199b560dbfcad016ad2
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,178 @@@
-   --> $DIR/needless_borrow.rs:173:13
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:11:15
 +   |
 +LL |     let _ = x(&&a); // warn
 +   |               ^^^ help: change this to: `&a`
 +   |
 +   = note: `-D clippy::needless-borrow` implied by `-D warnings`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:15:13
 +   |
 +LL |     mut_ref(&mut &mut b); // warn
 +   |             ^^^^^^^^^^^ help: change this to: `&mut b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:27:13
 +   |
 +LL |             &&a
 +   |             ^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:29:15
 +   |
 +LL |         46 => &&a,
 +   |               ^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:35:27
 +   |
 +LL |                     break &ref_a;
 +   |                           ^^^^^^ help: change this to: `ref_a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:42:15
 +   |
 +LL |     let _ = x(&&&a);
 +   |               ^^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:43:15
 +   |
 +LL |     let _ = x(&mut &&a);
 +   |               ^^^^^^^^ help: change this to: `&a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:44:15
 +   |
 +LL |     let _ = x(&&&mut b);
 +   |               ^^^^^^^^ help: change this to: `&mut b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:45:15
 +   |
 +LL |     let _ = x(&&ref_a);
 +   |               ^^^^^^^ help: change this to: `ref_a`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:48:11
 +   |
 +LL |         x(&b);
 +   |           ^^ help: change this to: `b`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:55:13
 +   |
 +LL |     mut_ref(&mut x);
 +   |             ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:56:13
 +   |
 +LL |     mut_ref(&mut &mut x);
 +   |             ^^^^^^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:57:23
 +   |
 +LL |     let y: &mut i32 = &mut x;
 +   |                       ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:58:23
 +   |
 +LL |     let y: &mut i32 = &mut &mut x;
 +   |                       ^^^^^^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:67:14
 +   |
 +LL |         0 => &mut x,
 +   |              ^^^^^^ help: change this to: `x`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:73:14
 +   |
 +LL |         0 => &mut x,
 +   |              ^^^^^^ help: change this to: `x`
 +
 +error: this expression borrows a value the compiler would automatically borrow
 +  --> $DIR/needless_borrow.rs:85:13
 +   |
 +LL |     let _ = (&x).0;
 +   |             ^^^^ help: change this to: `x`
 +
 +error: this expression borrows a value the compiler would automatically borrow
 +  --> $DIR/needless_borrow.rs:87:22
 +   |
 +LL |     let _ = unsafe { (&*x).0 };
 +   |                      ^^^^^ help: change this to: `(*x)`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:97:5
 +   |
 +LL |     (&&()).foo();
 +   |     ^^^^^^ help: change this to: `(&())`
 +
 +error: this expression creates a reference which is immediately dereferenced by the compiler
 +  --> $DIR/needless_borrow.rs:106:5
 +   |
 +LL |     (&&5).foo();
 +   |     ^^^^^ help: change this to: `(&5)`
 +
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:131:51
++   |
++LL |     let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
++   |                                                   ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:132:44
++   |
++LL |     let _ = std::path::Path::new(".").join(&&".");
++   |                                            ^^^^^ help: change this to: `"."`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:133:23
++   |
++LL |     deref_target_is_x(&X);
++   |                       ^^ help: change this to: `X`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:134:26
++   |
++LL |     multiple_constraints(&[[""]]);
++   |                          ^^^^^^^ help: change this to: `[[""]]`
++
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:135:45
++   |
++LL |     multiple_constraints_normalizes_to_same(&X, X);
++   |                                             ^^ help: change this to: `X`
++
++error: this expression creates a reference which is immediately dereferenced by the compiler
++  --> $DIR/needless_borrow.rs:136:32
++   |
++LL |     let _ = Some("").unwrap_or(&"");
++   |                                ^^^ help: change this to: `""`
++
 +error: this expression borrows a value the compiler would automatically borrow
-   --> $DIR/needless_borrow.rs:182:13
++  --> $DIR/needless_borrow.rs:187:13
 +   |
 +LL |             (&self.f)()
 +   |             ^^^^^^^^^ help: change this to: `(self.f)`
 +
 +error: this expression borrows a value the compiler would automatically borrow
- error: aborting due to 22 previous errors
++  --> $DIR/needless_borrow.rs:196:13
 +   |
 +LL |             (&mut self.f)()
 +   |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 +
++error: the borrowed expression implements the required traits
++  --> $DIR/needless_borrow.rs:298:55
++   |
++LL |         let _ = std::process::Command::new("ls").args(&["-a", "-l"]).status().unwrap();
++   |                                                       ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
++
++error: aborting due to 29 previous errors
 +
index 1f11d1f8d563c2bcb213f6924ac169512950d762,0000000000000000000000000000000000000000..12a9ace1ee688679a5745698e314990480eda9f7
mode 100644,000000..100644
--- /dev/null
@@@ -1,114 -1,0 +1,303 @@@
 +use std::collections::{BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 +
 +fn main() {
 +    let sample = [1; 5];
 +    let indirect_iter = sample.iter().collect::<Vec<_>>();
 +    indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +    let indirect_len = sample.iter().collect::<VecDeque<_>>();
 +    indirect_len.len();
 +    let indirect_empty = sample.iter().collect::<VecDeque<_>>();
 +    indirect_empty.is_empty();
 +    let indirect_contains = sample.iter().collect::<VecDeque<_>>();
 +    indirect_contains.contains(&&5);
 +    let indirect_negative = sample.iter().collect::<Vec<_>>();
 +    indirect_negative.len();
 +    indirect_negative
 +        .into_iter()
 +        .map(|x| (*x, *x + 1))
 +        .collect::<HashMap<_, _>>();
 +
 +    // #6202
 +    let a = "a".to_string();
 +    let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
 +    let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
 +    non_copy_contains.contains(&a);
 +
 +    // Fix #5991
 +    let vec_a = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 +    let vec_b = vec_a.iter().collect::<Vec<_>>();
 +    if vec_b.len() > 3 {}
 +    let other_vec = vec![1, 3, 12, 4, 16, 2];
 +    let we_got_the_same_numbers = other_vec.iter().filter(|item| vec_b.contains(item)).collect::<Vec<_>>();
 +
 +    // Fix #6297
 +    let sample = [1; 5];
 +    let multiple_indirect = sample.iter().collect::<Vec<_>>();
 +    let sample2 = vec![2, 3];
 +    if multiple_indirect.is_empty() {
 +        // do something
 +    } else {
 +        let found = sample2
 +            .iter()
 +            .filter(|i| multiple_indirect.iter().any(|s| **s % **i == 0))
 +            .collect::<Vec<_>>();
 +    }
 +}
 +
 +mod issue7110 {
 +    // #7110 - lint for type annotation cases
 +    use super::*;
 +
 +    fn lint_vec(string: &str) -> usize {
 +        let buffer: Vec<&str> = string.split('/').collect();
 +        buffer.len()
 +    }
 +    fn lint_vec_deque() -> usize {
 +        let sample = [1; 5];
 +        let indirect_len: VecDeque<_> = sample.iter().collect();
 +        indirect_len.len()
 +    }
 +    fn lint_linked_list() -> usize {
 +        let sample = [1; 5];
 +        let indirect_len: LinkedList<_> = sample.iter().collect();
 +        indirect_len.len()
 +    }
 +    fn lint_binary_heap() -> usize {
 +        let sample = [1; 5];
 +        let indirect_len: BinaryHeap<_> = sample.iter().collect();
 +        indirect_len.len()
 +    }
 +    fn dont_lint(string: &str) -> usize {
 +        let buffer: Vec<&str> = string.split('/').collect();
 +        for buff in &buffer {
 +            println!("{}", buff);
 +        }
 +        buffer.len()
 +    }
 +}
 +
 +mod issue7975 {
 +    use super::*;
 +
 +    fn direct_mapping_with_used_mutable_reference() -> Vec<()> {
 +        let test_vec: Vec<()> = vec![];
 +        let mut vec_2: Vec<()> = vec![];
 +        let mut_ref = &mut vec_2;
 +        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
 +        collected_vec.into_iter().map(|_| mut_ref.push(())).collect()
 +    }
 +
 +    fn indirectly_mapping_with_used_mutable_reference() -> Vec<()> {
 +        let test_vec: Vec<()> = vec![];
 +        let mut vec_2: Vec<()> = vec![];
 +        let mut_ref = &mut vec_2;
 +        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
 +        let iter = collected_vec.into_iter();
 +        iter.map(|_| mut_ref.push(())).collect()
 +    }
 +
 +    fn indirect_collect_after_indirect_mapping_with_used_mutable_reference() -> Vec<()> {
 +        let test_vec: Vec<()> = vec![];
 +        let mut vec_2: Vec<()> = vec![];
 +        let mut_ref = &mut vec_2;
 +        let collected_vec: Vec<_> = test_vec.into_iter().map(|_| mut_ref.push(())).collect();
 +        let iter = collected_vec.into_iter();
 +        let mapped_iter = iter.map(|_| mut_ref.push(()));
 +        mapped_iter.collect()
 +    }
 +}
 +
 +fn allow_test() {
 +    #[allow(clippy::needless_collect)]
 +    let v = [1].iter().collect::<Vec<_>>();
 +    v.into_iter().collect::<HashSet<_>>();
 +}
++
++mod issue_8553 {
++    fn test_for() {
++        let vec = vec![1, 2];
++        let w: Vec<usize> = vec.iter().map(|i| i * i).collect();
++
++        for i in 0..2 {
++            // Do not lint, because this method call is in the loop
++            w.contains(&i);
++        }
++
++        for i in 0..2 {
++            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++            // Do lint
++            y.contains(&i);
++            for j in 0..2 {
++                // Do not lint, because this method call is in the loop
++                z.contains(&j);
++            }
++        }
++
++        // Do not lint, because this variable is used.
++        w.contains(&0);
++    }
++
++    fn test_while() {
++        let vec = vec![1, 2];
++        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let mut n = 0;
++        while n > 1 {
++            // Do not lint, because this method call is in the loop
++            x.contains(&n);
++            n += 1;
++        }
++
++        while n > 2 {
++            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++            // Do lint
++            y.contains(&n);
++            n += 1;
++            while n > 4 {
++                // Do not lint, because this method call is in the loop
++                z.contains(&n);
++                n += 1;
++            }
++        }
++    }
++
++    fn test_loop() {
++        let vec = vec![1, 2];
++        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let mut n = 0;
++        loop {
++            if n < 1 {
++                // Do not lint, because this method call is in the loop
++                x.contains(&n);
++                n += 1;
++            } else {
++                break;
++            }
++        }
++
++        loop {
++            if n < 2 {
++                let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++                let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++                // Do lint
++                y.contains(&n);
++                n += 1;
++                loop {
++                    if n < 4 {
++                        // Do not lint, because this method call is in the loop
++                        z.contains(&n);
++                        n += 1;
++                    } else {
++                        break;
++                    }
++                }
++            } else {
++                break;
++            }
++        }
++    }
++
++    fn test_while_let() {
++        let vec = vec![1, 2];
++        let x: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let optional = Some(0);
++        let mut n = 0;
++        while let Some(value) = optional {
++            if n < 1 {
++                // Do not lint, because this method call is in the loop
++                x.contains(&n);
++                n += 1;
++            } else {
++                break;
++            }
++        }
++
++        while let Some(value) = optional {
++            let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++            let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++            if n < 2 {
++                // Do lint
++                y.contains(&n);
++                n += 1;
++            } else {
++                break;
++            }
++
++            while let Some(value) = optional {
++                if n < 4 {
++                    // Do not lint, because this method call is in the loop
++                    z.contains(&n);
++                    n += 1;
++                } else {
++                    break;
++                }
++            }
++        }
++    }
++
++    fn test_if_cond() {
++        let vec = vec![1, 2];
++        let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let w = v.iter().collect::<Vec<_>>();
++        // Do lint
++        for _ in 0..w.len() {
++            todo!();
++        }
++    }
++
++    fn test_if_cond_false_case() {
++        let vec = vec![1, 2];
++        let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let w = v.iter().collect::<Vec<_>>();
++        // Do not lint, because w is used.
++        for _ in 0..w.len() {
++            todo!();
++        }
++
++        w.len();
++    }
++
++    fn test_while_cond() {
++        let mut vec = vec![1, 2];
++        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let mut w = v.iter().collect::<Vec<_>>();
++        // Do lint
++        while 1 == w.len() {
++            todo!();
++        }
++    }
++
++    fn test_while_cond_false_case() {
++        let mut vec = vec![1, 2];
++        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let mut w = v.iter().collect::<Vec<_>>();
++        // Do not lint, because w is used.
++        while 1 == w.len() {
++            todo!();
++        }
++
++        w.len();
++    }
++
++    fn test_while_let_cond() {
++        let mut vec = vec![1, 2];
++        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let mut w = v.iter().collect::<Vec<_>>();
++        // Do lint
++        while let Some(i) = Some(w.len()) {
++            todo!();
++        }
++    }
++
++    fn test_while_let_cond_false_case() {
++        let mut vec = vec![1, 2];
++        let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
++        let mut w = v.iter().collect::<Vec<_>>();
++        // Do not lint, because w is used.
++        while let Some(i) = Some(w.len()) {
++            todo!();
++        }
++        w.len();
++    }
++}
index 0f5e78f91198c27dd50d74e25bce448c89207d37,0000000000000000000000000000000000000000..9f0880cc6069d1449c7ec43d6386387deddcc5b7
mode 100644,000000..100644
--- /dev/null
@@@ -1,129 -1,0 +1,246 @@@
- error: aborting due to 9 previous errors
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:5:39
 +   |
 +LL |     let indirect_iter = sample.iter().collect::<Vec<_>>();
 +   |                                       ^^^^^^^
 +LL |     indirect_iter.into_iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +   |     ------------------------- the iterator could be used here instead
 +   |
 +   = note: `-D clippy::needless-collect` implied by `-D warnings`
 +help: use the original Iterator instead of collecting it and then producing a new one
 +   |
 +LL ~     
 +LL ~     sample.iter().map(|x| (x, x + 1)).collect::<HashMap<_, _>>();
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:7:38
 +   |
 +LL |     let indirect_len = sample.iter().collect::<VecDeque<_>>();
 +   |                                      ^^^^^^^
 +LL |     indirect_len.len();
 +   |     ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~     
 +LL ~     sample.iter().count();
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:9:40
 +   |
 +LL |     let indirect_empty = sample.iter().collect::<VecDeque<_>>();
 +   |                                        ^^^^^^^
 +LL |     indirect_empty.is_empty();
 +   |     ------------------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator has anything instead of collecting it and seeing if it's empty
 +   |
 +LL ~     
 +LL ~     sample.iter().next().is_none();
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:11:43
 +   |
 +LL |     let indirect_contains = sample.iter().collect::<VecDeque<_>>();
 +   |                                           ^^^^^^^
 +LL |     indirect_contains.contains(&&5);
 +   |     ------------------------------- the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~     
 +LL ~     sample.iter().any(|x| x == &5);
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:23:48
 +   |
 +LL |     let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
 +   |                                                ^^^^^^^
 +LL |     non_copy_contains.contains(&a);
 +   |     ------------------------------ the iterator could be used here instead
 +   |
 +help: check if the original Iterator contains an element instead of collecting then checking
 +   |
 +LL ~     
 +LL ~     sample.into_iter().any(|x| x == a);
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:52:51
 +   |
 +LL |         let buffer: Vec<&str> = string.split('/').collect();
 +   |                                                   ^^^^^^^
 +LL |         buffer.len()
 +   |         ------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         string.split('/').count()
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:57:55
 +   |
 +LL |         let indirect_len: VecDeque<_> = sample.iter().collect();
 +   |                                                       ^^^^^^^
 +LL |         indirect_len.len()
 +   |         ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         sample.iter().count()
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:62:57
 +   |
 +LL |         let indirect_len: LinkedList<_> = sample.iter().collect();
 +   |                                                         ^^^^^^^
 +LL |         indirect_len.len()
 +   |         ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         sample.iter().count()
 +   |
 +
 +error: avoid using `collect()` when not needed
 +  --> $DIR/needless_collect_indirect.rs:67:57
 +   |
 +LL |         let indirect_len: BinaryHeap<_> = sample.iter().collect();
 +   |                                                         ^^^^^^^
 +LL |         indirect_len.len()
 +   |         ------------------ the iterator could be used here instead
 +   |
 +help: take the original Iterator's count instead of collecting it and finding the length
 +   |
 +LL ~         
 +LL ~         sample.iter().count()
 +   |
 +
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:127:59
++   |
++LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++   |                                                           ^^^^^^^
++...
++LL |             y.contains(&i);
++   |             -------------- the iterator could be used here instead
++   |
++help: check if the original Iterator contains an element instead of collecting then checking
++   |
++LL ~             
++LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++LL |             // Do lint
++LL ~             vec.iter().map(|k| k * k).any(|x| x == i);
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:152:59
++   |
++LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++   |                                                           ^^^^^^^
++...
++LL |             y.contains(&n);
++   |             -------------- the iterator could be used here instead
++   |
++help: check if the original Iterator contains an element instead of collecting then checking
++   |
++LL ~             
++LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++LL |             // Do lint
++LL ~             vec.iter().map(|k| k * k).any(|x| x == n);
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:181:63
++   |
++LL |                 let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++   |                                                               ^^^^^^^
++...
++LL |                 y.contains(&n);
++   |                 -------------- the iterator could be used here instead
++   |
++help: check if the original Iterator contains an element instead of collecting then checking
++   |
++LL ~                 
++LL |                 let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++LL |                 // Do lint
++LL ~                 vec.iter().map(|k| k * k).any(|x| x == n);
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:217:59
++   |
++LL |             let y: Vec<usize> = vec.iter().map(|k| k * k).collect();
++   |                                                           ^^^^^^^
++...
++LL |                 y.contains(&n);
++   |                 -------------- the iterator could be used here instead
++   |
++help: check if the original Iterator contains an element instead of collecting then checking
++   |
++LL ~             
++LL |             let z: Vec<usize> = vec.iter().map(|k| k * k).collect();
++LL |             if n < 2 {
++LL |                 // Do lint
++LL ~                 vec.iter().map(|k| k * k).any(|x| x == n);
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:242:26
++   |
++LL |         let w = v.iter().collect::<Vec<_>>();
++   |                          ^^^^^^^
++LL |         // Do lint
++LL |         for _ in 0..w.len() {
++   |                     ------- the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL ~         
++LL |         // Do lint
++LL ~         for _ in 0..v.iter().count() {
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:264:30
++   |
++LL |         let mut w = v.iter().collect::<Vec<_>>();
++   |                              ^^^^^^^
++LL |         // Do lint
++LL |         while 1 == w.len() {
++   |                    ------- the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL ~         
++LL |         // Do lint
++LL ~         while 1 == v.iter().count() {
++   |
++
++error: avoid using `collect()` when not needed
++  --> $DIR/needless_collect_indirect.rs:286:30
++   |
++LL |         let mut w = v.iter().collect::<Vec<_>>();
++   |                              ^^^^^^^
++LL |         // Do lint
++LL |         while let Some(i) = Some(w.len()) {
++   |                                  ------- the iterator could be used here instead
++   |
++help: take the original Iterator's count instead of collecting it and finding the length
++   |
++LL ~         
++LL |         // Do lint
++LL ~         while let Some(i) = Some(v.iter().count()) {
++   |
++
++error: aborting due to 16 previous errors
 +
index 0c9178fb85efe39a0a3e7938e658b162fcb2efc1,0000000000000000000000000000000000000000..7e47406798cf9b90d96e98893ed8c66c89109406
mode 100644,000000..100644
--- /dev/null
@@@ -1,210 -1,0 +1,249 @@@
 +// run-rustfix
 +#![warn(clippy::needless_match)]
 +#![allow(clippy::manual_map)]
 +#![allow(dead_code)]
 +
 +#[derive(Clone, Copy)]
 +enum Simple {
 +    A,
 +    B,
 +    C,
 +    D,
 +}
 +
 +fn useless_match() {
 +    let i = 10;
 +    let _: i32 = i;
 +    let s = "test";
 +    let _: &str = s;
 +}
 +
 +fn custom_type_match() {
 +    let se = Simple::A;
 +    let _: Simple = se;
 +    // Don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::A,
 +        Simple::B => Simple::B,
 +        _ => Simple::C,
 +    };
 +    // Mingled, don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::B,
 +        Simple::B => Simple::C,
 +        Simple::C => Simple::D,
 +        Simple::D => Simple::A,
 +    };
 +}
 +
 +fn option_match(x: Option<i32>) {
 +    let _: Option<i32> = x;
 +    // Don't trigger, this is the case for manual_map_option
 +    let _: Option<i32> = match x {
 +        Some(a) => Some(-a),
 +        None => None,
 +    };
 +}
 +
 +fn func_ret_err<T>(err: T) -> Result<i32, T> {
 +    Err(err)
 +}
 +
 +fn result_match() {
 +    let _: Result<i32, i32> = Ok(1);
 +    let _: Result<i32, i32> = func_ret_err(0_i32);
 +    // as ref, don't trigger
 +    let res = &func_ret_err(0_i32);
 +    let _: Result<&i32, &i32> = match *res {
 +        Ok(ref x) => Ok(x),
 +        Err(ref x) => Err(x),
 +    };
 +}
 +
 +fn if_let_option() {
 +    let _ = Some(1);
 +
 +    fn do_something() {}
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        Some(a)
 +    } else {
 +        do_something();
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        do_something();
 +        Some(a)
 +    } else {
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
 +}
 +
 +fn if_let_option_result() -> Result<(), ()> {
 +    fn f(x: i32) -> Result<Option<i32>, ()> {
 +        Ok(Some(x))
 +    }
 +    // Don't trigger
 +    let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
 +    Ok(())
 +}
 +
 +fn if_let_result() {
 +    let x: Result<i32, i32> = Ok(1);
 +    let _: Result<i32, i32> = x;
 +    let _: Result<i32, i32> = x;
 +    // Input type mismatch, don't trigger
 +    #[allow(clippy::question_mark)]
 +    let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 +}
 +
 +fn if_let_custom_enum(x: Simple) {
 +    let _: Simple = x;
 +
 +    // Don't trigger
 +    let _: Simple = if let Simple::A = x {
 +        Simple::A
 +    } else if true {
 +        Simple::B
 +    } else {
 +        x
 +    };
 +}
 +
 +mod issue8542 {
 +    #[derive(Clone, Copy)]
 +    enum E {
 +        VariantA(u8, u8),
 +        VariantB(u8, bool),
 +    }
 +
 +    enum Complex {
 +        A(u8),
 +        B(u8, bool),
 +        C(u8, i32, f64),
 +        D(E, bool),
 +    }
 +
 +    fn match_test() {
 +        let ce = Complex::B(8, false);
 +        let aa = 0_u8;
 +        let bb = false;
 +
 +        let _: Complex = ce;
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(_) => Complex::A(aa),
 +            Complex::B(_, b) => Complex::B(aa, b),
 +            Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
 +            Complex::D(e, b) => Complex::D(e, b),
 +        };
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(a) => Complex::A(a),
 +            Complex::B(a, _) => Complex::B(a, bb),
 +            Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
 +            _ => ce,
 +        };
 +    }
 +}
 +
 +/// Lint triggered when type coercions happen.
 +/// Do NOT trigger on any of these.
 +mod issue8551 {
 +    trait Trait {}
 +    struct Struct;
 +    impl Trait for Struct {}
 +
 +    fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
 +        match s {
 +            Some(s) => Some(s),
 +            None => None,
 +        }
 +    }
 +
 +    fn lint_tests() {
 +        let option: Option<&Struct> = None;
 +        let _: Option<&dyn Trait> = match option {
 +            Some(s) => Some(s),
 +            None => None,
 +        };
 +
 +        let _: Option<&dyn Trait> = if true {
 +            match option {
 +                Some(s) => Some(s),
 +                None => None,
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let result: Result<&Struct, i32> = Err(0);
 +        let _: Result<&dyn Trait, i32> = match result {
 +            Ok(s) => Ok(s),
 +            Err(e) => Err(e),
 +        };
 +
 +        let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
 +    }
 +}
 +
 +trait Tr {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
 +}
 +impl Tr for Result<i32, i32> {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
 +        match self {
 +            Ok(x) => Ok(x),
 +            Err(e) => Err(e),
 +        }
 +    }
 +}
 +
++mod issue9084 {
++    fn wildcard_if() {
++        let mut some_bool = true;
++        let e = Some(1);
++
++        // should lint
++        let _ = e;
++
++        // should lint
++        let _ = e;
++
++        // should not lint
++        let _ = match e {
++            _ if some_bool => e,
++            _ => Some(2),
++        };
++
++        // should not lint
++        let _ = match e {
++            Some(i) => Some(i + 1),
++            _ if some_bool => e,
++            _ => e,
++        };
++
++        // should not lint (guard has side effects)
++        let _ = match e {
++            Some(i) => Some(i),
++            _ if {
++                some_bool = false;
++                some_bool
++            } =>
++            {
++                e
++            },
++            _ => e,
++        };
++    }
++}
++
 +fn main() {}
index f66f01d7ccaf4ef2f96be1e2e2247cebf83884f2,0000000000000000000000000000000000000000..809c694bf400464bb4685b115010b04c63ce6147
mode 100644,000000..100644
--- /dev/null
@@@ -1,247 -1,0 +1,293 @@@
 +// run-rustfix
 +#![warn(clippy::needless_match)]
 +#![allow(clippy::manual_map)]
 +#![allow(dead_code)]
 +
 +#[derive(Clone, Copy)]
 +enum Simple {
 +    A,
 +    B,
 +    C,
 +    D,
 +}
 +
 +fn useless_match() {
 +    let i = 10;
 +    let _: i32 = match i {
 +        0 => 0,
 +        1 => 1,
 +        2 => 2,
 +        _ => i,
 +    };
 +    let s = "test";
 +    let _: &str = match s {
 +        "a" => "a",
 +        "b" => "b",
 +        s => s,
 +    };
 +}
 +
 +fn custom_type_match() {
 +    let se = Simple::A;
 +    let _: Simple = match se {
 +        Simple::A => Simple::A,
 +        Simple::B => Simple::B,
 +        Simple::C => Simple::C,
 +        Simple::D => Simple::D,
 +    };
 +    // Don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::A,
 +        Simple::B => Simple::B,
 +        _ => Simple::C,
 +    };
 +    // Mingled, don't trigger
 +    let _: Simple = match se {
 +        Simple::A => Simple::B,
 +        Simple::B => Simple::C,
 +        Simple::C => Simple::D,
 +        Simple::D => Simple::A,
 +    };
 +}
 +
 +fn option_match(x: Option<i32>) {
 +    let _: Option<i32> = match x {
 +        Some(a) => Some(a),
 +        None => None,
 +    };
 +    // Don't trigger, this is the case for manual_map_option
 +    let _: Option<i32> = match x {
 +        Some(a) => Some(-a),
 +        None => None,
 +    };
 +}
 +
 +fn func_ret_err<T>(err: T) -> Result<i32, T> {
 +    Err(err)
 +}
 +
 +fn result_match() {
 +    let _: Result<i32, i32> = match Ok(1) {
 +        Ok(a) => Ok(a),
 +        Err(err) => Err(err),
 +    };
 +    let _: Result<i32, i32> = match func_ret_err(0_i32) {
 +        Err(err) => Err(err),
 +        Ok(a) => Ok(a),
 +    };
 +    // as ref, don't trigger
 +    let res = &func_ret_err(0_i32);
 +    let _: Result<&i32, &i32> = match *res {
 +        Ok(ref x) => Ok(x),
 +        Err(ref x) => Err(x),
 +    };
 +}
 +
 +fn if_let_option() {
 +    let _ = if let Some(a) = Some(1) { Some(a) } else { None };
 +
 +    fn do_something() {}
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        Some(a)
 +    } else {
 +        do_something();
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) {
 +        do_something();
 +        Some(a)
 +    } else {
 +        None
 +    };
 +
 +    // Don't trigger
 +    let _ = if let Some(a) = Some(1) { Some(a) } else { Some(2) };
 +}
 +
 +fn if_let_option_result() -> Result<(), ()> {
 +    fn f(x: i32) -> Result<Option<i32>, ()> {
 +        Ok(Some(x))
 +    }
 +    // Don't trigger
 +    let _ = if let Some(v) = f(1)? { Some(v) } else { f(2)? };
 +    Ok(())
 +}
 +
 +fn if_let_result() {
 +    let x: Result<i32, i32> = Ok(1);
 +    let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
 +    let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
 +    // Input type mismatch, don't trigger
 +    #[allow(clippy::question_mark)]
 +    let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 +}
 +
 +fn if_let_custom_enum(x: Simple) {
 +    let _: Simple = if let Simple::A = x {
 +        Simple::A
 +    } else if let Simple::B = x {
 +        Simple::B
 +    } else if let Simple::C = x {
 +        Simple::C
 +    } else {
 +        x
 +    };
 +
 +    // Don't trigger
 +    let _: Simple = if let Simple::A = x {
 +        Simple::A
 +    } else if true {
 +        Simple::B
 +    } else {
 +        x
 +    };
 +}
 +
 +mod issue8542 {
 +    #[derive(Clone, Copy)]
 +    enum E {
 +        VariantA(u8, u8),
 +        VariantB(u8, bool),
 +    }
 +
 +    enum Complex {
 +        A(u8),
 +        B(u8, bool),
 +        C(u8, i32, f64),
 +        D(E, bool),
 +    }
 +
 +    fn match_test() {
 +        let ce = Complex::B(8, false);
 +        let aa = 0_u8;
 +        let bb = false;
 +
 +        let _: Complex = match ce {
 +            Complex::A(a) => Complex::A(a),
 +            Complex::B(a, b) => Complex::B(a, b),
 +            Complex::C(a, b, c) => Complex::C(a, b, c),
 +            Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
 +            Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
 +        };
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(_) => Complex::A(aa),
 +            Complex::B(_, b) => Complex::B(aa, b),
 +            Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
 +            Complex::D(e, b) => Complex::D(e, b),
 +        };
 +
 +        // Don't trigger
 +        let _: Complex = match ce {
 +            Complex::A(a) => Complex::A(a),
 +            Complex::B(a, _) => Complex::B(a, bb),
 +            Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
 +            _ => ce,
 +        };
 +    }
 +}
 +
 +/// Lint triggered when type coercions happen.
 +/// Do NOT trigger on any of these.
 +mod issue8551 {
 +    trait Trait {}
 +    struct Struct;
 +    impl Trait for Struct {}
 +
 +    fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
 +        match s {
 +            Some(s) => Some(s),
 +            None => None,
 +        }
 +    }
 +
 +    fn lint_tests() {
 +        let option: Option<&Struct> = None;
 +        let _: Option<&dyn Trait> = match option {
 +            Some(s) => Some(s),
 +            None => None,
 +        };
 +
 +        let _: Option<&dyn Trait> = if true {
 +            match option {
 +                Some(s) => Some(s),
 +                None => None,
 +            }
 +        } else {
 +            None
 +        };
 +
 +        let result: Result<&Struct, i32> = Err(0);
 +        let _: Result<&dyn Trait, i32> = match result {
 +            Ok(s) => Ok(s),
 +            Err(e) => Err(e),
 +        };
 +
 +        let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
 +    }
 +}
 +
 +trait Tr {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
 +}
 +impl Tr for Result<i32, i32> {
 +    fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
 +        match self {
 +            Ok(x) => Ok(x),
 +            Err(e) => Err(e),
 +        }
 +    }
 +}
 +
++mod issue9084 {
++    fn wildcard_if() {
++        let mut some_bool = true;
++        let e = Some(1);
++
++        // should lint
++        let _ = match e {
++            _ if some_bool => e,
++            _ => e,
++        };
++
++        // should lint
++        let _ = match e {
++            Some(i) => Some(i),
++            _ if some_bool => e,
++            _ => e,
++        };
++
++        // should not lint
++        let _ = match e {
++            _ if some_bool => e,
++            _ => Some(2),
++        };
++
++        // should not lint
++        let _ = match e {
++            Some(i) => Some(i + 1),
++            _ if some_bool => e,
++            _ => e,
++        };
++
++        // should not lint (guard has side effects)
++        let _ = match e {
++            Some(i) => Some(i),
++            _ if {
++                some_bool = false;
++                some_bool
++            } =>
++            {
++                e
++            },
++            _ => e,
++        };
++    }
++}
++
 +fn main() {}
index 5bc79800a1a748bcbbb696e6fa824c791af3e647,0000000000000000000000000000000000000000..28e78441c2522fcd65528069876fc567cf071b5b
mode 100644,000000..100644
--- /dev/null
@@@ -1,113 -1,0 +1,134 @@@
- error: aborting due to 11 previous errors
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:16:18
 +   |
 +LL |       let _: i32 = match i {
 +   |  __________________^
 +LL | |         0 => 0,
 +LL | |         1 => 1,
 +LL | |         2 => 2,
 +LL | |         _ => i,
 +LL | |     };
 +   | |_____^ help: replace it with: `i`
 +   |
 +   = note: `-D clippy::needless-match` implied by `-D warnings`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:23:19
 +   |
 +LL |       let _: &str = match s {
 +   |  ___________________^
 +LL | |         "a" => "a",
 +LL | |         "b" => "b",
 +LL | |         s => s,
 +LL | |     };
 +   | |_____^ help: replace it with: `s`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:32:21
 +   |
 +LL |       let _: Simple = match se {
 +   |  _____________________^
 +LL | |         Simple::A => Simple::A,
 +LL | |         Simple::B => Simple::B,
 +LL | |         Simple::C => Simple::C,
 +LL | |         Simple::D => Simple::D,
 +LL | |     };
 +   | |_____^ help: replace it with: `se`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:54:26
 +   |
 +LL |       let _: Option<i32> = match x {
 +   |  __________________________^
 +LL | |         Some(a) => Some(a),
 +LL | |         None => None,
 +LL | |     };
 +   | |_____^ help: replace it with: `x`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:70:31
 +   |
 +LL |       let _: Result<i32, i32> = match Ok(1) {
 +   |  _______________________________^
 +LL | |         Ok(a) => Ok(a),
 +LL | |         Err(err) => Err(err),
 +LL | |     };
 +   | |_____^ help: replace it with: `Ok(1)`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:74:31
 +   |
 +LL |       let _: Result<i32, i32> = match func_ret_err(0_i32) {
 +   |  _______________________________^
 +LL | |         Err(err) => Err(err),
 +LL | |         Ok(a) => Ok(a),
 +LL | |     };
 +   | |_____^ help: replace it with: `func_ret_err(0_i32)`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:87:13
 +   |
 +LL |     let _ = if let Some(a) = Some(1) { Some(a) } else { None };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:122:31
 +   |
 +LL |     let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:123:31
 +   |
 +LL |     let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
 +   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 +
 +error: this if-let expression is unnecessary
 +  --> $DIR/needless_match.rs:130:21
 +   |
 +LL |       let _: Simple = if let Simple::A = x {
 +   |  _____________________^
 +LL | |         Simple::A
 +LL | |     } else if let Simple::B = x {
 +LL | |         Simple::B
 +...  |
 +LL | |         x
 +LL | |     };
 +   | |_____^ help: replace it with: `x`
 +
 +error: this match expression is unnecessary
 +  --> $DIR/needless_match.rs:169:26
 +   |
 +LL |           let _: Complex = match ce {
 +   |  __________________________^
 +LL | |             Complex::A(a) => Complex::A(a),
 +LL | |             Complex::B(a, b) => Complex::B(a, b),
 +LL | |             Complex::C(a, b, c) => Complex::C(a, b, c),
 +LL | |             Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
 +LL | |             Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
 +LL | |         };
 +   | |_________^ help: replace it with: `ce`
 +
++error: this match expression is unnecessary
++  --> $DIR/needless_match.rs:253:17
++   |
++LL |           let _ = match e {
++   |  _________________^
++LL | |             _ if some_bool => e,
++LL | |             _ => e,
++LL | |         };
++   | |_________^ help: replace it with: `e`
++
++error: this match expression is unnecessary
++  --> $DIR/needless_match.rs:259:17
++   |
++LL |           let _ = match e {
++   |  _________________^
++LL | |             Some(i) => Some(i),
++LL | |             _ if some_bool => e,
++LL | |             _ => e,
++LL | |         };
++   | |_________^ help: replace it with: `e`
++
++error: aborting due to 13 previous errors
 +
index 0bc0d0011efe00cd009475cb7972c3162cde1c62,0000000000000000000000000000000000000000..87c8fc03b3c3627766edaaf6d965f5afdc6ced2c
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,236 @@@
- fn check_expect() -> bool {
-     if true {
-         // no error!
-         return true;
-     }
-     #[expect(clippy::needless_return)]
-     return true;
 +// run-rustfix
 +
 +#![feature(lint_reasons)]
 +#![feature(let_else)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::if_same_then_else,
 +    clippy::single_match,
 +    clippy::needless_bool,
 +    clippy::equatable_if_let
 +)]
 +#![warn(clippy::needless_return)]
 +
 +use std::cell::RefCell;
 +
 +macro_rules! the_answer {
 +    () => {
 +        42
 +    };
 +}
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    true
 +}
 +
 +fn test_no_semicolon() -> bool {
 +    true
 +}
 +
 +fn test_if_block() -> bool {
 +    if true {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => {
 +            true
 +        },
 +    }
 +}
 +
 +fn test_closure() {
 +    let _ = || {
 +        true
 +    };
 +    let _ = || true;
 +}
 +
 +fn test_macro_call() -> i32 {
 +    the_answer!()
 +}
 +
 +fn test_void_fun() {
 +    
 +}
 +
 +fn test_void_if_fun(b: bool) {
 +    if b {
 +        
 +    } else {
 +        
 +    }
 +}
 +
 +fn test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => (),
 +    }
 +}
 +
 +fn test_nested_match(x: u32) {
 +    match x {
 +        0 => (),
 +        1 => {
 +            let _ = 42;
 +            
 +        },
 +        _ => (),
 +    }
 +}
 +
 +fn temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +fn borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        String::from("test")
 +    } else {
 +        String::new()
 +    }
 +}
 +
 +macro_rules! needed_return {
 +    ($e:expr) => {
 +        if $e > 3 {
 +            return;
 +        }
 +    };
 +}
 +
 +fn test_return_in_macro() {
 +    // This will return and the macro below won't be executed. Removing the `return` from the macro
 +    // will change semantics.
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +mod issue6501 {
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn foo(bar: Result<(), ()>) {
 +        bar.unwrap_or_else(|_| {})
 +    }
 +
 +    fn test_closure() {
 +        let _ = || {
 +            
 +        };
 +        let _ = || {};
 +    }
 +
 +    struct Foo;
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn bar(res: Result<Foo, u8>) -> Foo {
 +        res.unwrap_or_else(|_| Foo)
 +    }
 +}
 +
 +async fn async_test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    true
 +}
 +
 +async fn async_test_no_semicolon() -> bool {
 +    true
 +}
 +
 +async fn async_test_if_block() -> bool {
 +    if true {
 +        true
 +    } else {
 +        false
 +    }
 +}
 +
 +async fn async_test_match(x: bool) -> bool {
 +    match x {
 +        true => false,
 +        false => {
 +            true
 +        },
 +    }
 +}
 +
 +async fn async_test_closure() {
 +    let _ = || {
 +        true
 +    };
 +    let _ = || true;
 +}
 +
 +async fn async_test_macro_call() -> i32 {
 +    the_answer!()
 +}
 +
 +async fn async_test_void_fun() {
 +    
 +}
 +
 +async fn async_test_void_if_fun(b: bool) {
 +    if b {
 +        
 +    } else {
 +        
 +    }
 +}
 +
 +async fn async_test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => (),
 +    }
 +}
 +
 +async fn async_temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +async fn async_borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        String::from("test")
 +    } else {
 +        String::new()
 +    }
 +}
 +
 +async fn async_test_return_in_macro() {
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +fn let_else() {
 +    let Some(1) = Some(1) else { return };
 +}
 +
 +fn needless_return_macro() -> String {
 +    let _ = "foo";
 +    let _ = "bar";
 +    format!("Hello {}", "world!")
 +}
 +
++fn issue_9361() -> i32 {
++    #[allow(clippy::integer_arithmetic)]
++    return 1 + 2;
 +}
 +
 +fn main() {}
index eb9f72e8e7822fe8839060d77bc9ca237d1f5057,0000000000000000000000000000000000000000..5a86e656255dd1413d7ca1e3dd2d543846cf7350
mode 100644,000000..100644
--- /dev/null
@@@ -1,240 -1,0 +1,236 @@@
- fn check_expect() -> bool {
-     if true {
-         // no error!
-         return true;
-     }
-     #[expect(clippy::needless_return)]
-     return true;
 +// run-rustfix
 +
 +#![feature(lint_reasons)]
 +#![feature(let_else)]
 +#![allow(unused)]
 +#![allow(
 +    clippy::if_same_then_else,
 +    clippy::single_match,
 +    clippy::needless_bool,
 +    clippy::equatable_if_let
 +)]
 +#![warn(clippy::needless_return)]
 +
 +use std::cell::RefCell;
 +
 +macro_rules! the_answer {
 +    () => {
 +        42
 +    };
 +}
 +
 +fn test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    return true;
 +}
 +
 +fn test_no_semicolon() -> bool {
 +    return true;
 +}
 +
 +fn test_if_block() -> bool {
 +    if true {
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +fn test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => {
 +            return true;
 +        },
 +    }
 +}
 +
 +fn test_closure() {
 +    let _ = || {
 +        return true;
 +    };
 +    let _ = || return true;
 +}
 +
 +fn test_macro_call() -> i32 {
 +    return the_answer!();
 +}
 +
 +fn test_void_fun() {
 +    return;
 +}
 +
 +fn test_void_if_fun(b: bool) {
 +    if b {
 +        return;
 +    } else {
 +        return;
 +    }
 +}
 +
 +fn test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => return,
 +    }
 +}
 +
 +fn test_nested_match(x: u32) {
 +    match x {
 +        0 => (),
 +        1 => {
 +            let _ = 42;
 +            return;
 +        },
 +        _ => return,
 +    }
 +}
 +
 +fn temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +fn borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        return String::from("test");
 +    } else {
 +        return String::new();
 +    }
 +}
 +
 +macro_rules! needed_return {
 +    ($e:expr) => {
 +        if $e > 3 {
 +            return;
 +        }
 +    };
 +}
 +
 +fn test_return_in_macro() {
 +    // This will return and the macro below won't be executed. Removing the `return` from the macro
 +    // will change semantics.
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +mod issue6501 {
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn foo(bar: Result<(), ()>) {
 +        bar.unwrap_or_else(|_| return)
 +    }
 +
 +    fn test_closure() {
 +        let _ = || {
 +            return;
 +        };
 +        let _ = || return;
 +    }
 +
 +    struct Foo;
 +    #[allow(clippy::unnecessary_lazy_evaluations)]
 +    fn bar(res: Result<Foo, u8>) -> Foo {
 +        res.unwrap_or_else(|_| return Foo)
 +    }
 +}
 +
 +async fn async_test_end_of_fn() -> bool {
 +    if true {
 +        // no error!
 +        return true;
 +    }
 +    return true;
 +}
 +
 +async fn async_test_no_semicolon() -> bool {
 +    return true;
 +}
 +
 +async fn async_test_if_block() -> bool {
 +    if true {
 +        return true;
 +    } else {
 +        return false;
 +    }
 +}
 +
 +async fn async_test_match(x: bool) -> bool {
 +    match x {
 +        true => return false,
 +        false => {
 +            return true;
 +        },
 +    }
 +}
 +
 +async fn async_test_closure() {
 +    let _ = || {
 +        return true;
 +    };
 +    let _ = || return true;
 +}
 +
 +async fn async_test_macro_call() -> i32 {
 +    return the_answer!();
 +}
 +
 +async fn async_test_void_fun() {
 +    return;
 +}
 +
 +async fn async_test_void_if_fun(b: bool) {
 +    if b {
 +        return;
 +    } else {
 +        return;
 +    }
 +}
 +
 +async fn async_test_void_match(x: u32) {
 +    match x {
 +        0 => (),
 +        _ => return,
 +    }
 +}
 +
 +async fn async_temporary_outlives_local() -> String {
 +    let x = RefCell::<String>::default();
 +    return x.borrow().clone();
 +}
 +
 +async fn async_borrows_but_not_last(value: bool) -> String {
 +    if value {
 +        let x = RefCell::<String>::default();
 +        let _a = x.borrow().clone();
 +        return String::from("test");
 +    } else {
 +        return String::new();
 +    }
 +}
 +
 +async fn async_test_return_in_macro() {
 +    needed_return!(10);
 +    needed_return!(0);
 +}
 +
 +fn let_else() {
 +    let Some(1) = Some(1) else { return };
 +}
 +
 +fn needless_return_macro() -> String {
 +    let _ = "foo";
 +    let _ = "bar";
 +    return format!("Hello {}", "world!");
 +}
 +
++fn issue_9361() -> i32 {
++    #[allow(clippy::integer_arithmetic)]
++    return 1 + 2;
 +}
 +
 +fn main() {}
index 5768434f988ebf8b141fc4ee5e67a3971df67f29,0000000000000000000000000000000000000000..f71e8ead5195ea27653bbfd7f24869490ffcc41b
mode 100644,000000..100644
--- /dev/null
@@@ -1,122 -1,0 +1,113 @@@
- fn simple(a: usize, b: usize) -> usize {
-     if a == 0 { 1 } else { simple(a - 1, b) }
 +#![warn(clippy::only_used_in_recursion)]
 +
- fn with_calc(a: usize, b: isize) -> usize {
-     if a == 0 { 1 } else { with_calc(a - 1, -b + 1) }
++fn _simple(x: u32) -> u32 {
++    x
 +}
 +
- fn tuple((a, b): (usize, usize)) -> usize {
-     if a == 0 { 1 } else { tuple((a - 1, b + 1)) }
++fn _simple2(x: u32) -> u32 {
++    _simple(x)
 +}
 +
- fn let_tuple(a: usize, b: usize) -> usize {
-     let (c, d) = (a, b);
-     if c == 0 { 1 } else { let_tuple(c - 1, d + 1) }
++fn _one_unused(flag: u32, a: usize) -> usize {
++    if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
 +}
 +
- fn array([a, b]: [usize; 2]) -> usize {
-     if a == 0 { 1 } else { array([a - 1, b + 1]) }
- }
- fn index(a: usize, mut b: &[usize], c: usize) -> usize {
-     if a == 0 { 1 } else { index(a - 1, b, c + b[0]) }
- }
- fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
-     let c = loop {
-         b += 1;
-         c += 1;
-         if c == 10 {
-             break b;
-         }
-     };
-     if a == 0 { 1 } else { break_(a - 1, c, c) }
++fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
++    if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
 +}
 +
- // this has a side effect
- fn mut_ref(a: usize, b: &mut usize) -> usize {
-     *b = 1;
-     if a == 0 { 1 } else { mut_ref(a - 1, b) }
++fn _with_calc(flag: u32, a: i64) -> usize {
++    if flag == 0 {
++        0
++    } else {
++        _with_calc(flag - 1, (-a + 10) * 5)
++    }
 +}
 +
- fn mut_ref2(a: usize, b: &mut usize) -> usize {
-     let mut c = *b;
-     if a == 0 { 1 } else { mut_ref2(a - 1, &mut c) }
++// Don't lint
++fn _used_with_flag(flag: u32, a: u32) -> usize {
++    if flag == 0 { 0 } else { _used_with_flag(flag ^ a, a - 1) }
 +}
 +
- fn not_primitive(a: usize, b: String) -> usize {
-     if a == 0 { 1 } else { not_primitive(a - 1, b) }
++fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
++    if flag == 0 {
++        0
++    } else {
++        _used_with_unused(flag - 1, -a, a + b)
++    }
 +}
 +
- // this doesn't have a side effect,
- // but `String` is not primitive.
- fn not_primitive_op(a: usize, b: String, c: &str) -> usize {
-     if a == 1 { 1 } else { not_primitive_op(a, b + c, c) }
++fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
++    if flag == 0 {
++        0
++    } else {
++        _codependent_unused(flag - 1, a * b, a + b)
++    }
 +}
 +
-     fn method(a: usize, b: usize) -> usize {
-         if a == 0 { 1 } else { A::method(a - 1, b - 1) }
++fn _not_primitive(flag: u32, b: String) -> usize {
++    if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
 +}
 +
 +struct A;
 +
 +impl A {
-     fn method2(&self, a: usize, b: usize) -> usize {
-         if a == 0 { 1 } else { self.method2(a - 1, b + 1) }
++    fn _method(flag: usize, a: usize) -> usize {
++        if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
 +    }
 +
-     fn hello(a: usize, b: usize) -> usize;
-     fn hello2(&self, a: usize, b: usize) -> usize;
++    fn _method_self(&self, flag: usize, a: usize) -> usize {
++        if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
 +    }
 +}
 +
 +trait B {
-     fn hello(a: usize, b: usize) -> usize {
-         if a == 0 { 1 } else { A::hello(a - 1, b + 1) }
++    fn method(flag: u32, a: usize) -> usize;
++    fn method_self(&self, flag: u32, a: usize) -> usize;
 +}
 +
 +impl B for A {
-     fn hello2(&self, a: usize, b: usize) -> usize {
-         if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
++    fn method(flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { Self::method(flag - 1, a) }
 +    }
 +
- trait C {
-     fn hello(a: usize, b: usize) -> usize {
-         if a == 0 { 1 } else { Self::hello(a - 1, b + 1) }
++    fn method_self(&self, flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
 +    }
 +}
 +
-     fn hello2(&self, a: usize, b: usize) -> usize {
-         if a == 0 { 1 } else { self.hello2(a - 1, b + 1) }
++impl B for () {
++    fn method(flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { a }
 +    }
 +
- fn ignore(a: usize, _: usize) -> usize {
-     if a == 1 { 1 } else { ignore(a - 1, 0) }
- }
++    fn method_self(&self, flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { a }
 +    }
 +}
 +
- fn ignore2(a: usize, _b: usize) -> usize {
-     if a == 1 { 1 } else { ignore2(a - 1, _b) }
++impl B for u32 {
++    fn method(flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { <() as B>::method(flag, a) }
++    }
 +
- fn f1(a: u32) -> u32 {
-     a
- }
++    fn method_self(&self, flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { ().method_self(flag, a) }
++    }
 +}
 +
- fn f2(a: u32) -> u32 {
-     f1(a)
++trait C {
++    fn method(flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { Self::method(flag - 1, a) }
++    }
 +
- fn inner_fn(a: u32) -> u32 {
-     fn inner_fn(a: u32) -> u32 {
-         a
-     }
-     inner_fn(a)
++    fn method_self(&self, flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
++    }
 +}
 +
++fn _ignore(flag: usize, _a: usize) -> usize {
++    if flag == 0 { 0 } else { _ignore(flag - 1, _a) }
 +}
 +
 +fn main() {}
index 6fe9361bf5feb2eee0d903887ae911f3673f5706,0000000000000000000000000000000000000000..74057ddcfda4c2cca6e77a64c43313539fe5066c
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,195 @@@
-   --> $DIR/only_used_in_recursion.rs:3:21
 +error: parameter is only used in recursion
- LL | fn simple(a: usize, b: usize) -> usize {
-    |                     ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:11:27
 +   |
-   --> $DIR/only_used_in_recursion.rs:7:24
++LL | fn _one_unused(flag: u32, a: usize) -> usize {
++   |                           ^ help: if this is intentional, prefix it with an underscore: `_a`
 +   |
 +   = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:12:53
++   |
++LL |     if flag == 0 { 0 } else { _one_unused(flag - 1, a) }
++   |                                                     ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion.rs:15:27
++   |
++LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
++   |                           ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:16:53
++   |
++LL |     if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
++   |                                                     ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion.rs:15:35
++   |
++LL | fn _two_unused(flag: u32, a: u32, b: i32) -> usize {
++   |                                   ^ help: if this is intentional, prefix it with an underscore: `_b`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:16:56
++   |
++LL |     if flag == 0 { 0 } else { _two_unused(flag - 1, a, b) }
++   |                                                        ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion.rs:19:26
++   |
++LL | fn _with_calc(flag: u32, a: i64) -> usize {
++   |                          ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:23:32
++   |
++LL |         _with_calc(flag - 1, (-a + 10) * 5)
++   |                                ^
 +
 +error: parameter is only used in recursion
- LL | fn with_calc(a: usize, b: isize) -> usize {
-    |                        ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:32:33
++   |
++LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
++   |                                 ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:36:38
 +   |
-   --> $DIR/only_used_in_recursion.rs:11:14
++LL |         _used_with_unused(flag - 1, -a, a + b)
++   |                                      ^  ^
 +
 +error: parameter is only used in recursion
- LL | fn tuple((a, b): (usize, usize)) -> usize {
-    |              ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:32:41
++   |
++LL | fn _used_with_unused(flag: u32, a: i32, b: i32) -> usize {
++   |                                         ^ help: if this is intentional, prefix it with an underscore: `_b`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:36:45
 +   |
-   --> $DIR/only_used_in_recursion.rs:15:24
++LL |         _used_with_unused(flag - 1, -a, a + b)
++   |                                             ^
 +
 +error: parameter is only used in recursion
- LL | fn let_tuple(a: usize, b: usize) -> usize {
-    |                        ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:40:35
 +   |
-   --> $DIR/only_used_in_recursion.rs:20:14
++LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
++   |                                   ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:44:39
++   |
++LL |         _codependent_unused(flag - 1, a * b, a + b)
++   |                                       ^      ^
 +
 +error: parameter is only used in recursion
- LL | fn array([a, b]: [usize; 2]) -> usize {
-    |              ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:40:43
++   |
++LL | fn _codependent_unused(flag: u32, a: i32, b: i32) -> usize {
++   |                                           ^ help: if this is intentional, prefix it with an underscore: `_b`
 +   |
-   --> $DIR/only_used_in_recursion.rs:24:20
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:44:43
++   |
++LL |         _codependent_unused(flag - 1, a * b, a + b)
++   |                                           ^      ^
 +
 +error: parameter is only used in recursion
- LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
-    |                    ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:48:30
++   |
++LL | fn _not_primitive(flag: u32, b: String) -> usize {
++   |                              ^ help: if this is intentional, prefix it with an underscore: `_b`
 +   |
-   --> $DIR/only_used_in_recursion.rs:24:37
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:49:56
++   |
++LL |     if flag == 0 { 0 } else { _not_primitive(flag - 1, b) }
++   |                                                        ^
 +
 +error: parameter is only used in recursion
- LL | fn index(a: usize, mut b: &[usize], c: usize) -> usize {
-    |                                     ^ help: if this is intentional, prefix with an underscore: `_c`
++  --> $DIR/only_used_in_recursion.rs:55:29
++   |
++LL |     fn _method(flag: usize, a: usize) -> usize {
++   |                             ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:56:59
 +   |
-   --> $DIR/only_used_in_recursion.rs:28:21
++LL |         if flag == 0 { 0 } else { Self::_method(flag - 1, a) }
++   |                                                           ^
 +
 +error: parameter is only used in recursion
- LL | fn break_(a: usize, mut b: usize, mut c: usize) -> usize {
-    |                     ^^^^^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:59:22
++   |
++LL |     fn _method_self(&self, flag: usize, a: usize) -> usize {
++   |                      ^^^^
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:60:35
 +   |
-   --> $DIR/only_used_in_recursion.rs:46:23
++LL |         if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
++   |                                   ^^^^
 +
 +error: parameter is only used in recursion
- LL | fn mut_ref2(a: usize, b: &mut usize) -> usize {
-    |                       ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:59:41
 +   |
-   --> $DIR/only_used_in_recursion.rs:51:28
++LL |     fn _method_self(&self, flag: usize, a: usize) -> usize {
++   |                                         ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:60:63
++   |
++LL |         if flag == 0 { 0 } else { self._method_self(flag - 1, a) }
++   |                                                               ^
 +
 +error: parameter is only used in recursion
- LL | fn not_primitive(a: usize, b: String) -> usize {
-    |                            ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:70:26
++   |
++LL |     fn method(flag: u32, a: usize) -> usize {
++   |                          ^ help: if this is intentional, prefix it with an underscore: `_a`
 +   |
-   --> $DIR/only_used_in_recursion.rs:68:33
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:71:58
++   |
++LL |         if flag == 0 { 0 } else { Self::method(flag - 1, a) }
++   |                                                          ^
 +
 +error: parameter is only used in recursion
- LL |     fn method2(&self, a: usize, b: usize) -> usize {
-    |                                 ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:74:38
++   |
++LL |     fn method_self(&self, flag: u32, a: usize) -> usize {
++   |                                      ^ help: if this is intentional, prefix it with an underscore: `_a`
 +   |
-   --> $DIR/only_used_in_recursion.rs:90:24
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:75:62
++   |
++LL |         if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
++   |                                                              ^
 +
 +error: parameter is only used in recursion
- LL |     fn hello(a: usize, b: usize) -> usize {
-    |                        ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:100:26
++   |
++LL |     fn method(flag: u32, a: usize) -> usize {
++   |                          ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:101:58
 +   |
-   --> $DIR/only_used_in_recursion.rs:94:32
++LL |         if flag == 0 { 0 } else { Self::method(flag - 1, a) }
++   |                                                          ^
 +
 +error: parameter is only used in recursion
- LL |     fn hello2(&self, a: usize, b: usize) -> usize {
-    |                                ^ help: if this is intentional, prefix with an underscore: `_b`
++  --> $DIR/only_used_in_recursion.rs:104:38
++   |
++LL |     fn method_self(&self, flag: u32, a: usize) -> usize {
++   |                                      ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion.rs:105:62
 +   |
- error: aborting due to 13 previous errors
++LL |         if flag == 0 { 0 } else { self.method_self(flag - 1, a) }
++   |                                                              ^
 +
++error: aborting due to 16 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..45dd0553f58acb31b9c15119db4e4f2d066564a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++#![warn(clippy::only_used_in_recursion)]
++
++fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
++    fn inner(flag: u32, a: u32) -> u32 {
++        if flag == 0 { 0 } else { inner(flag, a) }
++    }
++
++    let x = inner(flag, a);
++    if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
++}
++
++fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
++    if let Some(x) = a.and_then(|x| f(x, x)) {
++        _with_closure(Some(x), b, f)
++    } else {
++        0
++    }
++}
++
++// Issue #8560
++trait D {
++    fn foo(&mut self, arg: u32) -> u32;
++}
++
++mod m {
++    pub struct S(u32);
++    impl S {
++        pub fn foo(&mut self, arg: u32) -> u32 {
++            arg + self.0
++        }
++    }
++}
++
++impl D for m::S {
++    fn foo(&mut self, arg: u32) -> u32 {
++        self.foo(arg)
++    }
++}
++
++// Issue #8782
++fn only_let(x: u32) {
++    let y = 10u32;
++    let _z = x * y;
++}
++
++trait E<T: E<()>> {
++    fn method(flag: u32, a: usize) -> usize {
++        if flag == 0 {
++            0
++        } else {
++            <T as E<()>>::method(flag - 1, a)
++        }
++    }
++}
++
++impl E<()> for () {
++    fn method(flag: u32, a: usize) -> usize {
++        if flag == 0 { 0 } else { a }
++    }
++}
++
++fn overwritten_param(flag: u32, mut a: usize) -> usize {
++    if flag == 0 {
++        return 0;
++    } else if flag > 5 {
++        a += flag as usize;
++    } else {
++        a = 5;
++    }
++    overwritten_param(flag, a)
++}
++
++fn field_direct(flag: u32, mut a: (usize,)) -> usize {
++    if flag == 0 {
++        0
++    } else {
++        a.0 += 5;
++        field_direct(flag - 1, a)
++    }
++}
++
++fn field_deref(flag: u32, a: &mut Box<(usize,)>) -> usize {
++    if flag == 0 {
++        0
++    } else {
++        a.0 += 5;
++        field_deref(flag - 1, a)
++    }
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..23f6ffd30c9763de2033510ee3083f84f808874b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,63 @@@
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion2.rs:3:35
++   |
++LL | fn _with_inner(flag: u32, a: u32, b: u32) -> usize {
++   |                                   ^ help: if this is intentional, prefix it with an underscore: `_b`
++   |
++   = note: `-D clippy::only-used-in-recursion` implied by `-D warnings`
++note: parameter used here
++  --> $DIR/only_used_in_recursion2.rs:9:52
++   |
++LL |     if flag == 0 { 0 } else { _with_inner(flag, a, b + x) }
++   |                                                    ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion2.rs:4:25
++   |
++LL |     fn inner(flag: u32, a: u32) -> u32 {
++   |                         ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion2.rs:5:47
++   |
++LL |         if flag == 0 { 0 } else { inner(flag, a) }
++   |                                               ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion2.rs:12:34
++   |
++LL | fn _with_closure(a: Option<u32>, b: u32, f: impl Fn(u32, u32) -> Option<u32>) -> u32 {
++   |                                  ^ help: if this is intentional, prefix it with an underscore: `_b`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion2.rs:14:32
++   |
++LL |         _with_closure(Some(x), b, f)
++   |                                ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion2.rs:62:37
++   |
++LL | fn overwritten_param(flag: u32, mut a: usize) -> usize {
++   |                                     ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion2.rs:70:29
++   |
++LL |     overwritten_param(flag, a)
++   |                             ^
++
++error: parameter is only used in recursion
++  --> $DIR/only_used_in_recursion2.rs:73:32
++   |
++LL | fn field_direct(flag: u32, mut a: (usize,)) -> usize {
++   |                                ^ help: if this is intentional, prefix it with an underscore: `_a`
++   |
++note: parameter used here
++  --> $DIR/only_used_in_recursion2.rs:78:32
++   |
++LL |         field_direct(flag - 1, a)
++   |                                ^
++
++error: aborting due to 5 previous errors
++
index b6d5e106f057a36ab1503f701f144a37614e98db,0000000000000000000000000000000000000000..f15ac551bb3ccfeefa774ae183e91f98a66f6ce0
mode 100644,000000..100644
--- /dev/null
@@@ -1,182 -1,0 +1,191 @@@
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
 +#![allow(
 +    unused_tuple_struct_fields,
 +    clippy::redundant_closure,
 +    clippy::ref_option_ref,
 +    clippy::equatable_if_let,
 +    clippy::let_unit_value
 +)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    string.map_or((false, "hello"), |x| (true, x))
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = string.map_or(0, |s| s.len());
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +    let _ = num.as_ref().map_or(&0, |s| s);
 +    let _ = num.map_or(0, |mut s| {
 +        s += 1;
 +        s
 +    });
 +    let _ = num.as_mut().map_or(&mut 0, |s| {
 +        *s += 1;
 +        s
 +    });
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    arg.map_or(13, |x| {
 +        let y = x * x;
 +        y * y
 +    })
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = arg.map_or_else(|| side_effect(), |x| x);
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = arg.map_or_else(|| {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    }, |x| x * x * x * x);
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +// #7973
 +fn pattern_to_vec(pattern: &str) -> Vec<String> {
 +    pattern
 +        .trim_matches('/')
 +        .split('/')
 +        .flat_map(|s| {
 +            s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])
 +        })
 +        .collect::<Vec<_>>()
 +}
 +
 +enum DummyEnum {
 +    One(u8),
 +    Two,
 +}
 +
 +// should not warn since there is a compled complex subpat
 +// see #7991
 +fn complex_subpat() -> DummyEnum {
 +    let x = Some(DummyEnum::One(1));
 +    let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
 +    DummyEnum::Two
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = optional.map_or(5, |x| x + 2);
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
 +
 +    let _ = Some(0).map_or(0, |x| loop {
 +            if x == 0 {
 +                break x;
 +            }
 +        });
 +
 +    // #7576
 +    const fn _f(x: Option<u32>) -> u32 {
 +        // Don't lint, `map_or` isn't const
 +        if let Some(x) = x { x } else { 10 }
 +    }
 +
 +    // #5822
 +    let s = String::new();
 +    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        s.len()
 +    };
 +
 +    let s = String::new();
 +    // Lint, both branches immutably borrow `s`.
 +    let _ = Some(0).map_or(s.len(), |x| s.len() + x);
 +
 +    let s = String::new();
 +    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
 +    let _ = Some(0).map_or(1, |x| {
 +        let s = s;
 +        s.len() + x
 +    });
 +
 +    let s = Some(String::new());
 +    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
 +    let _ = if let Some(x) = &s {
 +        x.len()
 +    } else {
 +        let _s = s;
 +        10
 +    };
 +
 +    let mut s = Some(String::new());
 +    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
 +    let _ = if let Some(x) = &mut s {
 +        x.push_str("test");
 +        x.len()
 +    } else {
 +        let _s = &s;
 +        10
 +    };
 +
 +    async fn _f1(x: u32) -> u32 {
 +        x
 +    }
 +
 +    async fn _f2() {
 +        // Don't lint. `await` can't be moved into a closure.
 +        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
 +    }
 +
 +    let _ = pattern_to_vec("hello world");
 +    let _ = complex_subpat();
++
++    // issue #8492
++    let _ = s.map_or(1, |string| string.len());
++    let _ = Some(10).map_or(5, |a| a + 1);
++
++    let res: Result<i32, i32> = Ok(5);
++    let _ = res.map_or(1, |a| a + 1);
++    let _ = res.map_or(1, |a| a + 1);
++    let _ = res.map_or(5, |a| a + 1);
 +}
index 35bae159343587be7ea1a98c959260a0cc3ab62b,0000000000000000000000000000000000000000..9eeaea12d3bc91e615cb2fac5f3d3b10c0ffd0c9
mode 100644,000000..100644
--- /dev/null
@@@ -1,211 -1,0 +1,232 @@@
 +// run-rustfix
 +#![warn(clippy::option_if_let_else)]
 +#![allow(
 +    unused_tuple_struct_fields,
 +    clippy::redundant_closure,
 +    clippy::ref_option_ref,
 +    clippy::equatable_if_let,
 +    clippy::let_unit_value
 +)]
 +
 +fn bad1(string: Option<&str>) -> (bool, &str) {
 +    if let Some(x) = string {
 +        (true, x)
 +    } else {
 +        (false, "hello")
 +    }
 +}
 +
 +fn else_if_option(string: Option<&str>) -> Option<(bool, &str)> {
 +    if string.is_none() {
 +        None
 +    } else if let Some(x) = string {
 +        Some((true, x))
 +    } else {
 +        Some((false, ""))
 +    }
 +}
 +
 +fn unop_bad(string: &Option<&str>, mut num: Option<i32>) {
 +    let _ = if let Some(s) = *string { s.len() } else { 0 };
 +    let _ = if let Some(s) = &num { s } else { &0 };
 +    let _ = if let Some(s) = &mut num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +    let _ = if let Some(ref s) = num { s } else { &0 };
 +    let _ = if let Some(mut s) = num {
 +        s += 1;
 +        s
 +    } else {
 +        0
 +    };
 +    let _ = if let Some(ref mut s) = num {
 +        *s += 1;
 +        s
 +    } else {
 +        &mut 0
 +    };
 +}
 +
 +fn longer_body(arg: Option<u32>) -> u32 {
 +    if let Some(x) = arg {
 +        let y = x * x;
 +        y * y
 +    } else {
 +        13
 +    }
 +}
 +
 +fn impure_else(arg: Option<i32>) {
 +    let side_effect = || {
 +        println!("return 1");
 +        1
 +    };
 +    let _ = if let Some(x) = arg {
 +        x
 +    } else {
 +        // map_or_else must be suggested
 +        side_effect()
 +    };
 +}
 +
 +fn test_map_or_else(arg: Option<u32>) {
 +    let _ = if let Some(x) = arg {
 +        x * x * x * x
 +    } else {
 +        let mut y = 1;
 +        y = (y + 2 / y) / 2;
 +        y = (y + 2 / y) / 2;
 +        y
 +    };
 +}
 +
 +fn negative_tests(arg: Option<u32>) -> u32 {
 +    let _ = if let Some(13) = arg { "unlucky" } else { "lucky" };
 +    for _ in 0..10 {
 +        let _ = if let Some(x) = arg {
 +            x
 +        } else {
 +            continue;
 +        };
 +    }
 +    let _ = if let Some(x) = arg {
 +        return x;
 +    } else {
 +        5
 +    };
 +    7
 +}
 +
 +// #7973
 +fn pattern_to_vec(pattern: &str) -> Vec<String> {
 +    pattern
 +        .trim_matches('/')
 +        .split('/')
 +        .flat_map(|s| {
 +            if let Some(idx) = s.find('.') {
 +                vec![s[..idx].to_string(), s[idx..].to_string()]
 +            } else {
 +                vec![s.to_string()]
 +            }
 +        })
 +        .collect::<Vec<_>>()
 +}
 +
 +enum DummyEnum {
 +    One(u8),
 +    Two,
 +}
 +
 +// should not warn since there is a compled complex subpat
 +// see #7991
 +fn complex_subpat() -> DummyEnum {
 +    let x = Some(DummyEnum::One(1));
 +    let _ = if let Some(_one @ DummyEnum::One(..)) = x { 1 } else { 2 };
 +    DummyEnum::Two
 +}
 +
 +fn main() {
 +    let optional = Some(5);
 +    let _ = if let Some(x) = optional { x + 2 } else { 5 };
 +    let _ = bad1(None);
 +    let _ = else_if_option(None);
 +    unop_bad(&None, None);
 +    let _ = longer_body(None);
 +    test_map_or_else(None);
 +    let _ = negative_tests(None);
 +    let _ = impure_else(None);
 +
 +    let _ = if let Some(x) = Some(0) {
 +        loop {
 +            if x == 0 {
 +                break x;
 +            }
 +        }
 +    } else {
 +        0
 +    };
 +
 +    // #7576
 +    const fn _f(x: Option<u32>) -> u32 {
 +        // Don't lint, `map_or` isn't const
 +        if let Some(x) = x { x } else { 10 }
 +    }
 +
 +    // #5822
 +    let s = String::new();
 +    // Don't lint, `Some` branch consumes `s`, but else branch uses `s`
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        s.len()
 +    };
 +
 +    let s = String::new();
 +    // Lint, both branches immutably borrow `s`.
 +    let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
 +
 +    let s = String::new();
 +    // Lint, `Some` branch consumes `s`, but else branch doesn't use `s`.
 +    let _ = if let Some(x) = Some(0) {
 +        let s = s;
 +        s.len() + x
 +    } else {
 +        1
 +    };
 +
 +    let s = Some(String::new());
 +    // Don't lint, `Some` branch borrows `s`, but else branch consumes `s`
 +    let _ = if let Some(x) = &s {
 +        x.len()
 +    } else {
 +        let _s = s;
 +        10
 +    };
 +
 +    let mut s = Some(String::new());
 +    // Don't lint, `Some` branch mutably borrows `s`, but else branch also borrows  `s`
 +    let _ = if let Some(x) = &mut s {
 +        x.push_str("test");
 +        x.len()
 +    } else {
 +        let _s = &s;
 +        10
 +    };
 +
 +    async fn _f1(x: u32) -> u32 {
 +        x
 +    }
 +
 +    async fn _f2() {
 +        // Don't lint. `await` can't be moved into a closure.
 +        let _ = if let Some(x) = Some(0) { _f1(x).await } else { 0 };
 +    }
 +
 +    let _ = pattern_to_vec("hello world");
 +    let _ = complex_subpat();
++
++    // issue #8492
++    let _ = match s {
++        Some(string) => string.len(),
++        None => 1,
++    };
++    let _ = match Some(10) {
++        Some(a) => a + 1,
++        None => 5,
++    };
++
++    let res: Result<i32, i32> = Ok(5);
++    let _ = match res {
++        Ok(a) => a + 1,
++        _ => 1,
++    };
++    let _ = match res {
++        Err(_) => 1,
++        Ok(a) => a + 1,
++    };
++    let _ = if let Ok(a) = res { a + 1 } else { 5 };
 +}
index daba606004e114d68ed95d95682e94a4fd59c7f2,0000000000000000000000000000000000000000..a5dbf6e1f2218020a7439059f9a59f5dba493fde
mode 100644,000000..100644
--- /dev/null
@@@ -1,210 -1,0 +1,256 @@@
- error: aborting due to 15 previous errors
 +error: use Option::map_or instead of an if let/else
 +  --> $DIR/option_if_let_else.rs:12: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:30: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:31: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:32: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:38: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:39: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:45: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:54: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:67: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:76: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_else instead of an if let/else
 +  --> $DIR/option_if_let_else.rs:109:13
 +   |
 +LL | /             if let Some(idx) = s.find('.') {
 +LL | |                 vec![s[..idx].to_string(), s[idx..].to_string()]
 +LL | |             } else {
 +LL | |                 vec![s.to_string()]
 +LL | |             }
 +   | |_____________^ help: try: `s.find('.').map_or_else(|| vec![s.to_string()], |idx| vec![s[..idx].to_string(), s[idx..].to_string()])`
 +
 +error: use Option::map_or instead of an if let/else
 +  --> $DIR/option_if_let_else.rs:133: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:142: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 instead of an if let/else
 +  --> $DIR/option_if_let_else.rs:170:13
 +   |
 +LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
 +
 +error: use Option::map_or instead of an if let/else
 +  --> $DIR/option_if_let_else.rs:174: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: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:213:13
++   |
++LL |       let _ = match s {
++   |  _____________^
++LL | |         Some(string) => string.len(),
++LL | |         None => 1,
++LL | |     };
++   | |_____^ help: try: `s.map_or(1, |string| string.len())`
++
++error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:217:13
++   |
++LL |       let _ = match Some(10) {
++   |  _____________^
++LL | |         Some(a) => a + 1,
++LL | |         None => 5,
++LL | |     };
++   | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)`
++
++error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:223:13
++   |
++LL |       let _ = match res {
++   |  _____________^
++LL | |         Ok(a) => a + 1,
++LL | |         _ => 1,
++LL | |     };
++   | |_____^ help: try: `res.map_or(1, |a| a + 1)`
++
++error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:227:13
++   |
++LL |       let _ = match res {
++   |  _____________^
++LL | |         Err(_) => 1,
++LL | |         Ok(a) => a + 1,
++LL | |     };
++   | |_____^ help: try: `res.map_or(1, |a| a + 1)`
++
++error: use Option::map_or instead of an if let/else
++  --> $DIR/option_if_let_else.rs:231:13
++   |
++LL |     let _ = if let Ok(a) = res { a + 1 } else { 5 };
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)`
++
++error: aborting due to 20 previous errors
 +
index fdb08d953ff1dbc95263e6d41e88861310d9a6b5,0000000000000000000000000000000000000000..18ea4e550292a4b3ddd5a729f4ba946afe2f4d13
mode 100644,000000..100644
--- /dev/null
@@@ -1,229 -1,0 +1,229 @@@
-     let stringy = Some(String::from(""));
-     let _ = stringy.unwrap_or_else(|| "".to_owned());
 +// run-rustfix
 +
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or_else(make);
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or_default();
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or_else(|| Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or_else(|_| make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or_else(|_| Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or_default();
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or_default();
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or_else(<FakeDefault>::default);
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or_default();
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or_default();
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or_else(Foo::new);
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_insert(String::new());
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_insert(vec![]);
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_insert(String::new());
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_insert(vec![]);
 +
++    let stringy = Some(String::new());
++    let _ = stringy.unwrap_or_default();
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or_else(|| map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or_else(|| Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +mod issue6675 {
 +    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
 +        #[allow(unused)]
 +        let x = vec![0; 1000]; // future-proofing, make this function expensive.
 +        &*p
 +    }
 +
 +    unsafe fn foo() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
 +        #[rustfmt::skip]
 +        None.unwrap_or_else(|| unsafe { ptr_to_ref(s) });
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or_default();
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or_default();
 +    }
 +}
 +
 +fn main() {}
index 57ab5f03ee2851318b3ba58d9fcc2c72d23e442e,0000000000000000000000000000000000000000..c353b41e4495d96feee4525b5f1d20634d8bc694
mode 100644,000000..100644
--- /dev/null
@@@ -1,229 -1,0 +1,229 @@@
-     let stringy = Some(String::from(""));
-     let _ = stringy.unwrap_or("".to_owned());
 +// run-rustfix
 +
 +#![warn(clippy::or_fun_call)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps, clippy::borrow_as_ptr)]
 +
 +use std::collections::BTreeMap;
 +use std::collections::HashMap;
 +use std::time::Duration;
 +
 +/// Checks implementation of the `OR_FUN_CALL` lint.
 +fn or_fun_call() {
 +    struct Foo;
 +
 +    impl Foo {
 +        fn new() -> Foo {
 +            Foo
 +        }
 +    }
 +
 +    struct FakeDefault;
 +    impl FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    impl Default for FakeDefault {
 +        fn default() -> Self {
 +            FakeDefault
 +        }
 +    }
 +
 +    enum Enum {
 +        A(i32),
 +    }
 +
 +    fn make<T>() -> T {
 +        unimplemented!();
 +    }
 +
 +    let with_enum = Some(Enum::A(1));
 +    with_enum.unwrap_or(Enum::A(5));
 +
 +    let with_const_fn = Some(Duration::from_secs(1));
 +    with_const_fn.unwrap_or(Duration::from_secs(5));
 +
 +    let with_constructor = Some(vec![1]);
 +    with_constructor.unwrap_or(make());
 +
 +    let with_new = Some(vec![1]);
 +    with_new.unwrap_or(Vec::new());
 +
 +    let with_const_args = Some(vec![1]);
 +    with_const_args.unwrap_or(Vec::with_capacity(12));
 +
 +    let with_err: Result<_, ()> = Ok(vec![1]);
 +    with_err.unwrap_or(make());
 +
 +    let with_err_args: Result<_, ()> = Ok(vec![1]);
 +    with_err_args.unwrap_or(Vec::with_capacity(12));
 +
 +    let with_default_trait = Some(1);
 +    with_default_trait.unwrap_or(Default::default());
 +
 +    let with_default_type = Some(1);
 +    with_default_type.unwrap_or(u64::default());
 +
 +    let self_default = None::<FakeDefault>;
 +    self_default.unwrap_or(<FakeDefault>::default());
 +
 +    let real_default = None::<FakeDefault>;
 +    real_default.unwrap_or(<FakeDefault as Default>::default());
 +
 +    let with_vec = Some(vec![1]);
 +    with_vec.unwrap_or(vec![]);
 +
 +    let without_default = Some(Foo);
 +    without_default.unwrap_or(Foo::new());
 +
 +    let mut map = HashMap::<u64, String>::new();
 +    map.entry(42).or_insert(String::new());
 +
 +    let mut map_vec = HashMap::<u64, Vec<i32>>::new();
 +    map_vec.entry(42).or_insert(vec![]);
 +
 +    let mut btree = BTreeMap::<u64, String>::new();
 +    btree.entry(42).or_insert(String::new());
 +
 +    let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
 +    btree_vec.entry(42).or_insert(vec![]);
 +
++    let stringy = Some(String::new());
++    let _ = stringy.unwrap_or(String::new());
 +
 +    let opt = Some(1);
 +    let hello = "Hello";
 +    let _ = opt.ok_or(format!("{} world.", hello));
 +
 +    // index
 +    let map = HashMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or(map[&1]);
 +    let map = BTreeMap::<u64, u64>::new();
 +    let _ = Some(1).unwrap_or(map[&1]);
 +    // don't lint index vec
 +    let vec = vec![1];
 +    let _ = Some(1).unwrap_or(vec[1]);
 +}
 +
 +struct Foo(u8);
 +struct Bar(String, Duration);
 +#[rustfmt::skip]
 +fn test_or_with_ctors() {
 +    let opt = Some(1);
 +    let opt_opt = Some(Some(1));
 +    // we also test for const promotion, this makes sure we don't hit that
 +    let two = 2;
 +
 +    let _ = opt_opt.unwrap_or(Some(2));
 +    let _ = opt_opt.unwrap_or(Some(two));
 +    let _ = opt.ok_or(Some(2));
 +    let _ = opt.ok_or(Some(two));
 +    let _ = opt.ok_or(Foo(2));
 +    let _ = opt.ok_or(Foo(two));
 +    let _ = opt.or(Some(2));
 +    let _ = opt.or(Some(two));
 +
 +    let _ = Some("a".to_string()).or(Some("b".to_string()));
 +
 +    let b = "b".to_string();
 +    let _ = Some(Bar("a".to_string(), Duration::from_secs(1)))
 +        .or(Some(Bar(b, Duration::from_secs(2))));
 +
 +    let vec = vec!["foo"];
 +    let _ = opt.ok_or(vec.len());
 +
 +    let array = ["foo"];
 +    let _ = opt.ok_or(array.len());
 +
 +    let slice = &["foo"][..];
 +    let _ = opt.ok_or(slice.len());
 +
 +    let string = "foo";
 +    let _ = opt.ok_or(string.len());
 +}
 +
 +// Issue 4514 - early return
 +fn f() -> Option<()> {
 +    let a = Some(1);
 +    let b = 1i32;
 +
 +    let _ = a.unwrap_or(b.checked_mul(3)?.min(240));
 +
 +    Some(())
 +}
 +
 +mod issue6675 {
 +    unsafe fn ptr_to_ref<'a, T>(p: *const T) -> &'a T {
 +        #[allow(unused)]
 +        let x = vec![0; 1000]; // future-proofing, make this function expensive.
 +        &*p
 +    }
 +
 +    unsafe fn foo() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or(ptr_to_ref(s));
 +    }
 +
 +    fn bar() {
 +        let s = "test".to_owned();
 +        let s = &s as *const _;
 +        None.unwrap_or(unsafe { ptr_to_ref(s) });
 +        #[rustfmt::skip]
 +        None.unwrap_or( unsafe { ptr_to_ref(s) }    );
 +    }
 +}
 +
 +mod issue8239 {
 +    fn more_than_max_suggestion_highest_lines_0() {
 +        let frames = Vec::new();
 +        frames
 +            .iter()
 +            .map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn more_to_max_suggestion_highest_lines_1() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn equal_to_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        iter.map(|f: &String| f.to_lowercase())
 +            .reduce(|mut acc, f| {
 +                let _ = "";
 +                acc.push_str(&f);
 +                acc
 +            })
 +            .unwrap_or(String::new());
 +    }
 +
 +    fn less_than_max_suggestion_highest_lines() {
 +        let frames = Vec::new();
 +        let iter = frames.iter();
 +        let map = iter.map(|f: &String| f.to_lowercase());
 +        map.reduce(|mut acc, f| {
 +            acc.push_str(&f);
 +            acc
 +        })
 +        .unwrap_or(String::new());
 +    }
 +}
 +
 +fn main() {}
index 4c5938ab88b90926c25c189063ad1b4ab1e268f5,0000000000000000000000000000000000000000..887f23ac9761dfd7167f44f269d3224cb6d75e75
mode 100644,000000..100644
--- /dev/null
@@@ -1,136 -1,0 +1,136 @@@
- error: use of `unwrap_or` followed by a function call
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:49:22
 +   |
 +LL |     with_constructor.unwrap_or(make());
 +   |                      ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(make)`
 +   |
 +   = note: `-D clippy::or-fun-call` implied by `-D warnings`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:52:14
 +   |
 +LL |     with_new.unwrap_or(Vec::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:55:21
 +   |
 +LL |     with_const_args.unwrap_or(Vec::with_capacity(12));
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:58:14
 +   |
 +LL |     with_err.unwrap_or(make());
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| make())`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:61:19
 +   |
 +LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
 +   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:64:24
 +   |
 +LL |     with_default_trait.unwrap_or(Default::default());
 +   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:67:23
 +   |
 +LL |     with_default_type.unwrap_or(u64::default());
 +   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:70:18
 +   |
 +LL |     self_default.unwrap_or(<FakeDefault>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
 +
 +error: use of `unwrap_or` followed by a call to `default`
 +  --> $DIR/or_fun_call.rs:73:18
 +   |
 +LL |     real_default.unwrap_or(<FakeDefault as Default>::default());
 +   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:76:14
 +   |
 +LL |     with_vec.unwrap_or(vec![]);
 +   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:79:21
 +   |
 +LL |     without_default.unwrap_or(Foo::new());
 +   |                     ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(Foo::new)`
 +
- LL |     let _ = stringy.unwrap_or("".to_owned());
-    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| "".to_owned())`
++error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:94:21
 +   |
++LL |     let _ = stringy.unwrap_or(String::new());
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:102:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:104:21
 +   |
 +LL |     let _ = Some(1).unwrap_or(map[&1]);
 +   |                     ^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| map[&1])`
 +
 +error: use of `or` followed by a function call
 +  --> $DIR/or_fun_call.rs:128:35
 +   |
 +LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some("b".to_string()))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:167:14
 +   |
 +LL |         None.unwrap_or(ptr_to_ref(s));
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| ptr_to_ref(s))`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:173:14
 +   |
 +LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 +
 +error: use of `unwrap_or` followed by a function call
 +  --> $DIR/or_fun_call.rs:175:14
 +   |
 +LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:189:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:202:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:214:14
 +   |
 +LL |             .unwrap_or(String::new());
 +   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: use of `unwrap_or` followed by a call to `new`
 +  --> $DIR/or_fun_call.rs:225:10
 +   |
 +LL |         .unwrap_or(String::new());
 +   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 +
 +error: aborting due to 22 previous errors
 +
index f3e4c58d6949223e0fe16b7e32c5bac5d22a34de,0000000000000000000000000000000000000000..4644ea8f51da1ecd6def7c067aff736cdcebfd8a
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,74 @@@
 +// run-rustfix
 +#![warn(clippy::partialeq_to_none)]
 +
 +struct Foobar;
 +
 +impl PartialEq<Option<()>> for Foobar {
 +    fn eq(&self, _: &Option<()>) -> bool {
 +        false
 +    }
 +}
 +
 +#[allow(dead_code)]
 +fn foo(f: Option<u32>) -> &'static str {
 +    if f.is_some() { "yay" } else { "nay" }
 +}
 +
 +fn foobar() -> Option<()> {
 +    None
 +}
 +
 +fn bar() -> Result<(), ()> {
 +    Ok(())
 +}
 +
 +fn optref() -> &'static &'static Option<()> {
 +    &&None
 +}
 +
++pub fn macro_expansion() {
++    macro_rules! foo {
++        () => {
++            None::<()>
++        };
++    }
++
++    let _ = foobar() == foo!();
++    let _ = foo!() == foobar();
++    let _ = foo!() == foo!();
++}
++
 +fn main() {
 +    let x = Some(0);
 +
 +    let _ = x.is_none();
 +    let _ = x.is_some();
 +    let _ = x.is_none();
 +    let _ = x.is_some();
 +
 +    if foobar().is_none() {}
 +
 +    if bar().ok().is_some() {}
 +
 +    let _ = Some(1 + 2).is_some();
 +
 +    let _ = { Some(0) }.is_none();
 +
 +    let _ = {
 +        /*
 +          This comment runs long
 +        */
 +        Some(1)
 +    }.is_some();
 +
 +    // Should not trigger, as `Foobar` is not an `Option` and has no `is_none`
 +    let _ = Foobar == None;
 +
 +    let _ = optref().is_none();
 +    let _ = optref().is_some();
 +    let _ = optref().is_none();
 +    let _ = optref().is_some();
 +
 +    let x = Box::new(Option::<()>::None);
 +    let _ = (*x).is_some();
 +}
index 767b2a38bcc17ddba17267c2faa09ad10bef6cc1,0000000000000000000000000000000000000000..61011b3a8c553cdb4a70704a1f46aaeea838494a
mode 100644,000000..100644
--- /dev/null
@@@ -1,62 -1,0 +1,74 @@@
 +// run-rustfix
 +#![warn(clippy::partialeq_to_none)]
 +
 +struct Foobar;
 +
 +impl PartialEq<Option<()>> for Foobar {
 +    fn eq(&self, _: &Option<()>) -> bool {
 +        false
 +    }
 +}
 +
 +#[allow(dead_code)]
 +fn foo(f: Option<u32>) -> &'static str {
 +    if f != None { "yay" } else { "nay" }
 +}
 +
 +fn foobar() -> Option<()> {
 +    None
 +}
 +
 +fn bar() -> Result<(), ()> {
 +    Ok(())
 +}
 +
 +fn optref() -> &'static &'static Option<()> {
 +    &&None
 +}
 +
++pub fn macro_expansion() {
++    macro_rules! foo {
++        () => {
++            None::<()>
++        };
++    }
++
++    let _ = foobar() == foo!();
++    let _ = foo!() == foobar();
++    let _ = foo!() == foo!();
++}
++
 +fn main() {
 +    let x = Some(0);
 +
 +    let _ = x == None;
 +    let _ = x != None;
 +    let _ = None == x;
 +    let _ = None != x;
 +
 +    if foobar() == None {}
 +
 +    if bar().ok() != None {}
 +
 +    let _ = Some(1 + 2) != None;
 +
 +    let _ = { Some(0) } == None;
 +
 +    let _ = {
 +        /*
 +          This comment runs long
 +        */
 +        Some(1)
 +    } != None;
 +
 +    // Should not trigger, as `Foobar` is not an `Option` and has no `is_none`
 +    let _ = Foobar == None;
 +
 +    let _ = optref() == &&None;
 +    let _ = &&None != optref();
 +    let _ = **optref() == None;
 +    let _ = &None != *optref();
 +
 +    let x = Box::new(Option::<()>::None);
 +    let _ = None != *x;
 +}
index de15a9f7baaf030b02b5d4ab7a33a2e960134082,0000000000000000000000000000000000000000..d06ab7aee558b3e04b59948413d022199a980956
mode 100644,000000..100644
--- /dev/null
@@@ -1,110 -1,0 +1,110 @@@
-   --> $DIR/partialeq_to_none.rs:32:13
 +error: binary comparison to literal `Option::None`
 +  --> $DIR/partialeq_to_none.rs:14:8
 +   |
 +LL |     if f != None { "yay" } else { "nay" }
 +   |        ^^^^^^^^^ help: use `Option::is_some()` instead: `f.is_some()`
 +   |
 +   = note: `-D clippy::partialeq-to-none` implied by `-D warnings`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:33:13
++  --> $DIR/partialeq_to_none.rs:44:13
 +   |
 +LL |     let _ = x == None;
 +   |             ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:34:13
++  --> $DIR/partialeq_to_none.rs:45:13
 +   |
 +LL |     let _ = x != None;
 +   |             ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:35:13
++  --> $DIR/partialeq_to_none.rs:46:13
 +   |
 +LL |     let _ = None == x;
 +   |             ^^^^^^^^^ help: use `Option::is_none()` instead: `x.is_none()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:37:8
++  --> $DIR/partialeq_to_none.rs:47:13
 +   |
 +LL |     let _ = None != x;
 +   |             ^^^^^^^^^ help: use `Option::is_some()` instead: `x.is_some()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:39:8
++  --> $DIR/partialeq_to_none.rs:49:8
 +   |
 +LL |     if foobar() == None {}
 +   |        ^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `foobar().is_none()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:41:13
++  --> $DIR/partialeq_to_none.rs:51:8
 +   |
 +LL |     if bar().ok() != None {}
 +   |        ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `bar().ok().is_some()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:43:13
++  --> $DIR/partialeq_to_none.rs:53:13
 +   |
 +LL |     let _ = Some(1 + 2) != None;
 +   |             ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `Some(1 + 2).is_some()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:45:13
++  --> $DIR/partialeq_to_none.rs:55:13
 +   |
 +LL |     let _ = { Some(0) } == None;
 +   |             ^^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `{ Some(0) }.is_none()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:55:13
++  --> $DIR/partialeq_to_none.rs:57:13
 +   |
 +LL |       let _ = {
 +   |  _____________^
 +LL | |         /*
 +LL | |           This comment runs long
 +LL | |         */
 +LL | |         Some(1)
 +LL | |     } != None;
 +   | |_____________^
 +   |
 +help: use `Option::is_some()` instead
 +   |
 +LL ~     let _ = {
 +LL +         /*
 +LL +           This comment runs long
 +LL +         */
 +LL +         Some(1)
 +LL ~     }.is_some();
 +   |
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:56:13
++  --> $DIR/partialeq_to_none.rs:67:13
 +   |
 +LL |     let _ = optref() == &&None;
 +   |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:57:13
++  --> $DIR/partialeq_to_none.rs:68:13
 +   |
 +LL |     let _ = &&None != optref();
 +   |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:58:13
++  --> $DIR/partialeq_to_none.rs:69:13
 +   |
 +LL |     let _ = **optref() == None;
 +   |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_none()` instead: `optref().is_none()`
 +
 +error: binary comparison to literal `Option::None`
-   --> $DIR/partialeq_to_none.rs:61:13
++  --> $DIR/partialeq_to_none.rs:70:13
 +   |
 +LL |     let _ = &None != *optref();
 +   |             ^^^^^^^^^^^^^^^^^^ help: use `Option::is_some()` instead: `optref().is_some()`
 +
 +error: binary comparison to literal `Option::None`
++  --> $DIR/partialeq_to_none.rs:73:13
 +   |
 +LL |     let _ = None != *x;
 +   |             ^^^^^^^^^^ help: use `Option::is_some()` instead: `(*x).is_some()`
 +
 +error: aborting due to 15 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4170e109820457a5d4c667cc00941e9be3fc1fc0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++// run-rustfix
++#![allow(unused_must_use)]
++#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
++#![warn(clippy::positional_named_format_parameters)]
++
++use std::io::Write;
++
++fn main() {
++    let mut v = Vec::new();
++    let hello = "Hello";
++
++    println!("{hello:.foo$}", foo = 2);
++    writeln!(v, "{hello:.foo$}", foo = 2);
++
++    // Warnings
++    println!("{zero} {one:?}", zero = 0, one = 1);
++    println!("This is a test {zero} {one:?}", zero = 0, one = 1);
++    println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
++    println!("Hello {one:zero$}!", zero = 5, one = 1);
++    println!("Hello {zero:one$}!", zero = 4, one = 1);
++    println!("Hello {zero:0one$}!", zero = 4, one = 1);
++    println!("Hello is {one:.zero$}", zero = 5, one = 0.01);
++    println!("Hello is {one:<6.zero$}", zero = 5, one = 0.01);
++    println!("{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
++    println!("Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
++    println!("Hello {world} {world}!", world = 5);
++
++    writeln!(v, "{zero} {one:?}", zero = 0, one = 1);
++    writeln!(v, "This is a test {zero} {one:?}", zero = 0, one = 1);
++    writeln!(v, "Hello {one} is {two:.zero$}", zero = 5, one = hello, two = 0.01);
++    writeln!(v, "Hello {one:zero$}!", zero = 4, one = 1);
++    writeln!(v, "Hello {zero:one$}!", zero = 4, one = 1);
++    writeln!(v, "Hello {zero:0one$}!", zero = 4, one = 1);
++    writeln!(v, "Hello is {one:.zero$}", zero = 3, one = 0.01);
++    writeln!(v, "Hello is {one:<6.zero$}", zero = 2, one = 0.01);
++    writeln!(v, "{zero}, `{two:>8.one$}` has 3", zero = hello, one = 3, two = hello);
++    writeln!(v, "Hello {one} is {two:.zero$}", zero = 1, one = hello, two = 0.01);
++    writeln!(v, "Hello {world} {world}!", world = 0);
++
++    // Tests from other files
++    println!("{w:w$}", w = 1);
++    println!("{p:.p$}", p = 1);
++    println!("{v}", v = 1);
++    println!("{v:v$}", v = 1);
++    println!("{v:v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{v:v$.v$}", v = 1);
++    println!("{w:w$}", w = 1);
++    println!("{p:.p$}", p = 1);
++    println!("{:p$.w$}", 1, w = 1, p = 1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..553d8494ecc04303778cb9aa184faef5178e996a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,56 @@@
++// run-rustfix
++#![allow(unused_must_use)]
++#![allow(named_arguments_used_positionally)] // Unstable at time of writing.
++#![warn(clippy::positional_named_format_parameters)]
++
++use std::io::Write;
++
++fn main() {
++    let mut v = Vec::new();
++    let hello = "Hello";
++
++    println!("{hello:.foo$}", foo = 2);
++    writeln!(v, "{hello:.foo$}", foo = 2);
++
++    // Warnings
++    println!("{} {1:?}", zero = 0, one = 1);
++    println!("This is a test { } {000001:?}", zero = 0, one = 1);
++    println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++    println!("Hello {1:0$}!", zero = 5, one = 1);
++    println!("Hello {0:1$}!", zero = 4, one = 1);
++    println!("Hello {0:01$}!", zero = 4, one = 1);
++    println!("Hello is {1:.*}", zero = 5, one = 0.01);
++    println!("Hello is {:<6.*}", zero = 5, one = 0.01);
++    println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
++    println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++    println!("Hello {world} {}!", world = 5);
++
++    writeln!(v, "{} {1:?}", zero = 0, one = 1);
++    writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
++    writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++    writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
++    writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
++    writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
++    writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
++    writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
++    writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
++    writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
++    writeln!(v, "Hello {world} {}!", world = 0);
++
++    // Tests from other files
++    println!("{:w$}", w = 1);
++    println!("{:.p$}", p = 1);
++    println!("{}", v = 1);
++    println!("{:0$}", v = 1);
++    println!("{0:0$}", v = 1);
++    println!("{:0$.0$}", v = 1);
++    println!("{0:0$.0$}", v = 1);
++    println!("{0:0$.v$}", v = 1);
++    println!("{0:v$.0$}", v = 1);
++    println!("{v:0$.0$}", v = 1);
++    println!("{v:v$.0$}", v = 1);
++    println!("{v:0$.v$}", v = 1);
++    println!("{:w$}", w = 1);
++    println!("{:.p$}", p = 1);
++    println!("{:p$.w$}", 1, w = 1, p = 1);
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..48ddb6d67ad246084bdbb951887d8759969c297d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,418 @@@
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:16:16
++   |
++LL |     println!("{} {1:?}", zero = 0, one = 1);
++   |                ^ help: replace it with: `zero`
++   |
++   = note: `-D clippy::positional-named-format-parameters` implied by `-D warnings`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:16:19
++   |
++LL |     println!("{} {1:?}", zero = 0, one = 1);
++   |                   ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:17:31
++   |
++LL |     println!("This is a test { } {000001:?}", zero = 0, one = 1);
++   |                               ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:17:35
++   |
++LL |     println!("This is a test { } {000001:?}", zero = 0, one = 1);
++   |                                   ^^^^^^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:18:32
++   |
++LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                                ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:18:22
++   |
++LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                      ^ help: replace it with: `one`
++
++error: named parameter two is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:18:29
++   |
++LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                             ^ help: replace it with: `two`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:19:24
++   |
++LL |     println!("Hello {1:0$}!", zero = 5, one = 1);
++   |                        ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:19:22
++   |
++LL |     println!("Hello {1:0$}!", zero = 5, one = 1);
++   |                      ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:20:22
++   |
++LL |     println!("Hello {0:1$}!", zero = 4, one = 1);
++   |                      ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:20:24
++   |
++LL |     println!("Hello {0:1$}!", zero = 4, one = 1);
++   |                        ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:21:22
++   |
++LL |     println!("Hello {0:01$}!", zero = 4, one = 1);
++   |                      ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:21:25
++   |
++LL |     println!("Hello {0:01$}!", zero = 4, one = 1);
++   |                         ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:22:28
++   |
++LL |     println!("Hello is {1:.*}", zero = 5, one = 0.01);
++   |                            ^ help: replace it with: `zero$`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:22:25
++   |
++LL |     println!("Hello is {1:.*}", zero = 5, one = 0.01);
++   |                         ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:23:29
++   |
++LL |     println!("Hello is {:<6.*}", zero = 5, one = 0.01);
++   |                             ^ help: replace it with: `zero$`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:23:25
++   |
++LL |     println!("Hello is {:<6.*}", zero = 5, one = 0.01);
++   |                         ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:24:16
++   |
++LL |     println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
++   |                ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:24:28
++   |
++LL |     println!("{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
++   |                            ^ help: replace it with: `one$`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:25:32
++   |
++LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                                ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:25:22
++   |
++LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                      ^ help: replace it with: `one`
++
++error: named parameter two is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:25:29
++   |
++LL |     println!("Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                             ^ help: replace it with: `two`
++
++error: named parameter world is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:26:30
++   |
++LL |     println!("Hello {world} {}!", world = 5);
++   |                              ^ help: replace it with: `world`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:28:19
++   |
++LL |     writeln!(v, "{} {1:?}", zero = 0, one = 1);
++   |                   ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:28:22
++   |
++LL |     writeln!(v, "{} {1:?}", zero = 0, one = 1);
++   |                      ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:29:34
++   |
++LL |     writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
++   |                                  ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:29:38
++   |
++LL |     writeln!(v, "This is a test { } {000001:?}", zero = 0, one = 1);
++   |                                      ^^^^^^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:30:35
++   |
++LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                                   ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:30:25
++   |
++LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                         ^ help: replace it with: `one`
++
++error: named parameter two is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:30:32
++   |
++LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 5, one = hello, two = 0.01);
++   |                                ^ help: replace it with: `two`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:31:27
++   |
++LL |     writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
++   |                           ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:31:25
++   |
++LL |     writeln!(v, "Hello {1:0$}!", zero = 4, one = 1);
++   |                         ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:32:25
++   |
++LL |     writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
++   |                         ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:32:27
++   |
++LL |     writeln!(v, "Hello {0:1$}!", zero = 4, one = 1);
++   |                           ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:33:25
++   |
++LL |     writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
++   |                         ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:33:28
++   |
++LL |     writeln!(v, "Hello {0:01$}!", zero = 4, one = 1);
++   |                            ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:34:31
++   |
++LL |     writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
++   |                               ^ help: replace it with: `zero$`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:34:28
++   |
++LL |     writeln!(v, "Hello is {1:.*}", zero = 3, one = 0.01);
++   |                            ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:35:32
++   |
++LL |     writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
++   |                                ^ help: replace it with: `zero$`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:35:28
++   |
++LL |     writeln!(v, "Hello is {:<6.*}", zero = 2, one = 0.01);
++   |                            ^ help: replace it with: `one`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:36:19
++   |
++LL |     writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
++   |                   ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:36:31
++   |
++LL |     writeln!(v, "{}, `{two:>8.*}` has 3", zero = hello, one = 3, two = hello);
++   |                               ^ help: replace it with: `one$`
++
++error: named parameter zero is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:37:35
++   |
++LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
++   |                                   ^ help: replace it with: `zero`
++
++error: named parameter one is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:37:25
++   |
++LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
++   |                         ^ help: replace it with: `one`
++
++error: named parameter two is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:37:32
++   |
++LL |     writeln!(v, "Hello {1} is {2:.0$}", zero = 1, one = hello, two = 0.01);
++   |                                ^ help: replace it with: `two`
++
++error: named parameter world is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:38:33
++   |
++LL |     writeln!(v, "Hello {world} {}!", world = 0);
++   |                                 ^ help: replace it with: `world`
++
++error: named parameter w is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:41:16
++   |
++LL |     println!("{:w$}", w = 1);
++   |                ^ help: replace it with: `w`
++
++error: named parameter p is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:42:16
++   |
++LL |     println!("{:.p$}", p = 1);
++   |                ^ help: replace it with: `p`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:43:16
++   |
++LL |     println!("{}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:44:16
++   |
++LL |     println!("{:0$}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:44:17
++   |
++LL |     println!("{:0$}", v = 1);
++   |                 ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:45:16
++   |
++LL |     println!("{0:0$}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:45:18
++   |
++LL |     println!("{0:0$}", v = 1);
++   |                  ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:46:16
++   |
++LL |     println!("{:0$.0$}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:46:20
++   |
++LL |     println!("{:0$.0$}", v = 1);
++   |                    ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:46:17
++   |
++LL |     println!("{:0$.0$}", v = 1);
++   |                 ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:47:16
++   |
++LL |     println!("{0:0$.0$}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:47:21
++   |
++LL |     println!("{0:0$.0$}", v = 1);
++   |                     ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:47:18
++   |
++LL |     println!("{0:0$.0$}", v = 1);
++   |                  ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:48:16
++   |
++LL |     println!("{0:0$.v$}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:48:18
++   |
++LL |     println!("{0:0$.v$}", v = 1);
++   |                  ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:49:16
++   |
++LL |     println!("{0:v$.0$}", v = 1);
++   |                ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:49:21
++   |
++LL |     println!("{0:v$.0$}", v = 1);
++   |                     ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:50:21
++   |
++LL |     println!("{v:0$.0$}", v = 1);
++   |                     ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:50:18
++   |
++LL |     println!("{v:0$.0$}", v = 1);
++   |                  ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:51:21
++   |
++LL |     println!("{v:v$.0$}", v = 1);
++   |                     ^ help: replace it with: `v`
++
++error: named parameter v is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:52:18
++   |
++LL |     println!("{v:0$.v$}", v = 1);
++   |                  ^ help: replace it with: `v`
++
++error: named parameter w is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:53:16
++   |
++LL |     println!("{:w$}", w = 1);
++   |                ^ help: replace it with: `w`
++
++error: named parameter p is used as a positional parameter
++  --> $DIR/positional_named_format_parameters.rs:54:16
++   |
++LL |     println!("{:.p$}", p = 1);
++   |                ^ help: replace it with: `p`
++
++error: aborting due to 69 previous errors
++
index c4c9c82143336647e2dafb04292544c81797c6d5,0000000000000000000000000000000000000000..57f23bd1916ffaa46272f152ebc52f5d79c1f8e3
mode 100644,000000..100644
--- /dev/null
@@@ -1,210 -1,0 +1,225 @@@
 +// run-rustfix
 +#![allow(unreachable_code)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    a?;
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        (self.opt)?;
 +
 +        self.opt?;
 +
 +        let _ = Some(self.opt?);
 +
 +        let _ = self.opt?;
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        self.opt.as_ref()?;
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = self.opt.as_ref()?;
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = self.opt?;
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    f()?;
 +
 +    Some(0)
 +}
 +
 +fn func_returning_result() -> Result<i32, i32> {
 +    Ok(1)
 +}
 +
 +fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 +    let _ = x?;
 +
 +    x?;
 +
 +    // No warning
 +    let y = if let Ok(x) = x {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // issue #7859
 +    // no warning
 +    let _ = if let Ok(x) = func_returning_result() {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // no warning
 +    if func_returning_result().is_err() {
 +        return func_returning_result();
 +    }
 +
 +    Ok(y)
 +}
 +
 +// see issue #8019
 +pub enum NotOption {
 +    None,
 +    First,
 +    AfterFirst,
 +}
 +
 +fn obj(_: i32) -> Result<(), NotOption> {
 +    Err(NotOption::First)
 +}
 +
 +fn f() -> NotOption {
 +    if obj(2).is_err() {
 +        return NotOption::None;
 +    }
 +    NotOption::First
 +}
 +
 +fn do_something() {}
 +
 +fn err_immediate_return() -> Result<i32, i32> {
 +    func_returning_result()?;
 +    Ok(1)
 +}
 +
 +fn err_immediate_return_and_do_something() -> Result<i32, i32> {
 +    func_returning_result()?;
 +    do_something();
 +    Ok(1)
 +}
 +
 +// No warning
 +fn no_immediate_return() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        do_something();
 +        return Err(err);
 +    }
 +    Ok(1)
 +}
 +
 +// No warning
 +fn mixed_result_and_option() -> Option<i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Some(err);
 +    }
 +    None
 +}
 +
 +// No warning
 +fn else_if_check() -> Result<i32, i32> {
 +    if true {
 +        Ok(1)
 +    } else if let Err(e) = func_returning_result() {
 +        Err(e)
 +    } else {
 +        Err(-1)
 +    }
 +}
 +
 +// No warning
 +#[allow(clippy::manual_map)]
 +#[rustfmt::skip]
 +fn option_map() -> Option<bool> {
 +    if let Some(a) = Some(false) {
 +        Some(!a)
 +    } else {
 +        None
 +    }
 +}
 +
++pub struct PatternedError {
++    flag: bool,
++}
++
++// No warning
++fn pattern() -> Result<(), PatternedError> {
++    let res = Ok(());
++
++    if let Err(err @ PatternedError { flag: true }) = res {
++        return Err(err);
++    }
++
++    res
++}
++
 +fn main() {}
index cdbc7b1606f80782c7d107e213311e4b79cb6769,0000000000000000000000000000000000000000..436f027c215d54f459d244628acd9b358899c51c
mode 100644,000000..100644
--- /dev/null
@@@ -1,246 -1,0 +1,261 @@@
 +// run-rustfix
 +#![allow(unreachable_code)]
 +#![allow(dead_code)]
 +#![allow(clippy::unnecessary_wraps)]
 +
 +fn some_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    }
 +
 +    a
 +}
 +
 +fn some_other_func(a: Option<u32>) -> Option<u32> {
 +    if a.is_none() {
 +        return None;
 +    } else {
 +        return Some(0);
 +    }
 +    unreachable!()
 +}
 +
 +pub enum SeemsOption<T> {
 +    Some(T),
 +    None,
 +}
 +
 +impl<T> SeemsOption<T> {
 +    pub fn is_none(&self) -> bool {
 +        match *self {
 +            SeemsOption::None => true,
 +            SeemsOption::Some(_) => false,
 +        }
 +    }
 +}
 +
 +fn returns_something_similar_to_option(a: SeemsOption<u32>) -> SeemsOption<u32> {
 +    if a.is_none() {
 +        return SeemsOption::None;
 +    }
 +
 +    a
 +}
 +
 +pub struct CopyStruct {
 +    pub opt: Option<u32>,
 +}
 +
 +impl CopyStruct {
 +    #[rustfmt::skip]
 +    pub fn func(&self) -> Option<u32> {
 +        if (self.opt).is_none() {
 +            return None;
 +        }
 +
 +        if self.opt.is_none() {
 +            return None
 +        }
 +
 +        let _ = if self.opt.is_none() {
 +            return None;
 +        } else {
 +            self.opt
 +        };
 +
 +        let _ = if let Some(x) = self.opt {
 +            x
 +        } else {
 +            return None;
 +        };
 +
 +        self.opt
 +    }
 +}
 +
 +#[derive(Clone)]
 +pub struct MoveStruct {
 +    pub opt: Option<Vec<u32>>,
 +}
 +
 +impl MoveStruct {
 +    pub fn ref_func(&self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt.clone()
 +    }
 +
 +    pub fn mov_func_reuse(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +
 +        self.opt
 +    }
 +
 +    pub fn mov_func_no_use(self) -> Option<Vec<u32>> {
 +        if self.opt.is_none() {
 +            return None;
 +        }
 +        Some(Vec::new())
 +    }
 +
 +    pub fn if_let_ref_func(self) -> Option<Vec<u32>> {
 +        let v: &Vec<_> = if let Some(ref v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v.clone())
 +    }
 +
 +    pub fn if_let_mov_func(self) -> Option<Vec<u32>> {
 +        let v = if let Some(v) = self.opt {
 +            v
 +        } else {
 +            return None;
 +        };
 +
 +        Some(v)
 +    }
 +}
 +
 +fn func() -> Option<i32> {
 +    fn f() -> Option<String> {
 +        Some(String::new())
 +    }
 +
 +    if f().is_none() {
 +        return None;
 +    }
 +
 +    Some(0)
 +}
 +
 +fn func_returning_result() -> Result<i32, i32> {
 +    Ok(1)
 +}
 +
 +fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 +    let _ = if let Ok(x) = x { x } else { return x };
 +
 +    if x.is_err() {
 +        return x;
 +    }
 +
 +    // No warning
 +    let y = if let Ok(x) = x {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // issue #7859
 +    // no warning
 +    let _ = if let Ok(x) = func_returning_result() {
 +        x
 +    } else {
 +        return Err(0);
 +    };
 +
 +    // no warning
 +    if func_returning_result().is_err() {
 +        return func_returning_result();
 +    }
 +
 +    Ok(y)
 +}
 +
 +// see issue #8019
 +pub enum NotOption {
 +    None,
 +    First,
 +    AfterFirst,
 +}
 +
 +fn obj(_: i32) -> Result<(), NotOption> {
 +    Err(NotOption::First)
 +}
 +
 +fn f() -> NotOption {
 +    if obj(2).is_err() {
 +        return NotOption::None;
 +    }
 +    NotOption::First
 +}
 +
 +fn do_something() {}
 +
 +fn err_immediate_return() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Err(err);
 +    }
 +    Ok(1)
 +}
 +
 +fn err_immediate_return_and_do_something() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Err(err);
 +    }
 +    do_something();
 +    Ok(1)
 +}
 +
 +// No warning
 +fn no_immediate_return() -> Result<i32, i32> {
 +    if let Err(err) = func_returning_result() {
 +        do_something();
 +        return Err(err);
 +    }
 +    Ok(1)
 +}
 +
 +// No warning
 +fn mixed_result_and_option() -> Option<i32> {
 +    if let Err(err) = func_returning_result() {
 +        return Some(err);
 +    }
 +    None
 +}
 +
 +// No warning
 +fn else_if_check() -> Result<i32, i32> {
 +    if true {
 +        Ok(1)
 +    } else if let Err(e) = func_returning_result() {
 +        Err(e)
 +    } else {
 +        Err(-1)
 +    }
 +}
 +
 +// No warning
 +#[allow(clippy::manual_map)]
 +#[rustfmt::skip]
 +fn option_map() -> Option<bool> {
 +    if let Some(a) = Some(false) {
 +        Some(!a)
 +    } else {
 +        None
 +    }
 +}
 +
++pub struct PatternedError {
++    flag: bool,
++}
++
++// No warning
++fn pattern() -> Result<(), PatternedError> {
++    let res = Ok(());
++
++    if let Err(err @ PatternedError { flag: true }) = res {
++        return Err(err);
++    }
++
++    res
++}
++
 +fn main() {}
index f7f3b195ccc18f28143e4503dceab210a0d583e9,0000000000000000000000000000000000000000..f0e1a8128d7c36b5bc44d3d77c7ee270988780d7
mode 100644,000000..100644
--- /dev/null
@@@ -1,82 -1,0 +1,82 @@@
- #![allow(unused)]
++#![allow(unused, clippy::needless_borrow)]
 +#![warn(clippy::invalid_regex, clippy::trivial_regex)]
 +
 +extern crate regex;
 +
 +use regex::bytes::{Regex as BRegex, RegexBuilder as BRegexBuilder, RegexSet as BRegexSet};
 +use regex::{Regex, RegexBuilder, RegexSet};
 +
 +const OPENING_PAREN: &str = "(";
 +const NOT_A_REAL_REGEX: &str = "foobar";
 +
 +fn syntax_error() {
 +    let pipe_in_wrong_position = Regex::new("|");
 +    let pipe_in_wrong_position_builder = RegexBuilder::new("|");
 +    let wrong_char_ranice = Regex::new("[z-a]");
 +    let some_unicode = Regex::new("[é-è]");
 +
 +    let some_regex = Regex::new(OPENING_PAREN);
 +
 +    let binary_pipe_in_wrong_position = BRegex::new("|");
 +    let some_binary_regex = BRegex::new(OPENING_PAREN);
 +    let some_binary_regex_builder = BRegexBuilder::new(OPENING_PAREN);
 +
 +    let closing_paren = ")";
 +    let not_linted = Regex::new(closing_paren);
 +
 +    let set = RegexSet::new(&[r"[a-z]+@[a-z]+\.(com|org|net)", r"[a-z]+\.(com|org|net)"]);
 +    let bset = BRegexSet::new(&[
 +        r"[a-z]+@[a-z]+\.(com|org|net)",
 +        r"[a-z]+\.(com|org|net)",
 +        r".", // regression test
 +    ]);
 +
 +    let set_error = RegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
 +    let bset_error = BRegexSet::new(&[OPENING_PAREN, r"[a-z]+\.(com|org|net)"]);
 +
 +    let raw_string_error = Regex::new(r"[...\/...]");
 +    let raw_string_error = Regex::new(r#"[...\/...]"#);
 +}
 +
 +fn trivial_regex() {
 +    let trivial_eq = Regex::new("^foobar$");
 +
 +    let trivial_eq_builder = RegexBuilder::new("^foobar$");
 +
 +    let trivial_starts_with = Regex::new("^foobar");
 +
 +    let trivial_ends_with = Regex::new("foobar$");
 +
 +    let trivial_contains = Regex::new("foobar");
 +
 +    let trivial_contains = Regex::new(NOT_A_REAL_REGEX);
 +
 +    let trivial_backslash = Regex::new("a\\.b");
 +
 +    // unlikely corner cases
 +    let trivial_empty = Regex::new("");
 +
 +    let trivial_empty = Regex::new("^");
 +
 +    let trivial_empty = Regex::new("^$");
 +
 +    let binary_trivial_empty = BRegex::new("^$");
 +
 +    // non-trivial regexes
 +    let non_trivial_dot = Regex::new("a.b");
 +    let non_trivial_dot_builder = RegexBuilder::new("a.b");
 +    let non_trivial_eq = Regex::new("^foo|bar$");
 +    let non_trivial_starts_with = Regex::new("^foo|bar");
 +    let non_trivial_ends_with = Regex::new("^foo|bar");
 +    let non_trivial_ends_with = Regex::new("foo|bar");
 +    let non_trivial_binary = BRegex::new("foo|bar");
 +    let non_trivial_binary_builder = BRegexBuilder::new("foo|bar");
 +
 +    // #6005: unicode classes in bytes::Regex
 +    let a_byte_of_unicode = BRegex::new(r"\p{C}");
 +}
 +
 +fn main() {
 +    syntax_error();
 +    trivial_regex();
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..78d8f76fe669fb32926d4459252735166997062d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,98 @@@
++#![warn(clippy::result_large_err)]
++
++pub fn small_err() -> Result<(), u128> {
++    Ok(())
++}
++
++pub fn large_err() -> Result<(), [u8; 512]> {
++    Ok(())
++}
++
++pub struct FullyDefinedLargeError {
++    _foo: u128,
++    _bar: [u8; 100],
++    _foobar: [u8; 120],
++}
++
++impl FullyDefinedLargeError {
++    pub fn ret() -> Result<(), Self> {
++        Ok(())
++    }
++}
++
++pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
++    Ok(())
++}
++
++type Fdlr<T> = std::result::Result<T, FullyDefinedLargeError>;
++pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
++    Ok(x)
++}
++
++pub fn param_small_error<R>() -> Result<(), (R, u128)> {
++    Ok(())
++}
++
++pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
++    Ok(())
++}
++
++pub enum LargeErrorVariants<T> {
++    _Small(u8),
++    _Omg([u8; 512]),
++    _Param(T),
++}
++
++impl LargeErrorVariants<()> {
++    pub fn large_enum_error() -> Result<(), Self> {
++        Ok(())
++    }
++}
++
++trait TraitForcesLargeError {
++    fn large_error() -> Result<(), [u8; 512]> {
++        Ok(())
++    }
++}
++
++struct TraitImpl;
++
++impl TraitForcesLargeError for TraitImpl {
++    // Should not lint
++    fn large_error() -> Result<(), [u8; 512]> {
++        Ok(())
++    }
++}
++
++pub union FullyDefinedUnionError {
++    _maybe: u8,
++    _or_even: [[u8; 16]; 32],
++}
++
++pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
++    Ok(())
++}
++
++pub union UnionError<T: Copy> {
++    _maybe: T,
++    _or_perhaps_even: (T, [u8; 512]),
++}
++
++pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
++    Ok(())
++}
++
++pub struct ArrayError<T, U> {
++    _large_array: [T; 32],
++    _other_stuff: U,
++}
++
++pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
++    Ok(())
++}
++
++pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
++    Ok(())
++}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0f1f39d72cba187cff97f89762840cc32023c90c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,91 @@@
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:7:23
++   |
++LL | pub fn large_err() -> Result<(), [u8; 512]> {
++   |                       ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
++   |
++   = note: `-D clippy::result-large-err` implied by `-D warnings`
++   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:18:21
++   |
++LL |     pub fn ret() -> Result<(), Self> {
++   |                     ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
++   |
++   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:23:26
++   |
++LL | pub fn struct_error() -> Result<(), FullyDefinedLargeError> {
++   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 240 bytes
++   |
++   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:28:45
++   |
++LL | pub fn large_err_via_type_alias<T>(x: T) -> Fdlr<T> {
++   |                                             ^^^^^^^ the `Err`-variant is at least 240 bytes
++   |
++   = help: try reducing the size of `FullyDefinedLargeError`, for example by boxing large elements or replacing it with `Box<FullyDefinedLargeError>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:36:34
++   |
++LL | pub fn param_large_error<R>() -> Result<(), (u128, R, FullyDefinedLargeError)> {
++   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 256 bytes
++   |
++   = help: try reducing the size of `(u128, R, FullyDefinedLargeError)`, for example by boxing large elements or replacing it with `Box<(u128, R, FullyDefinedLargeError)>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:47:34
++   |
++LL |     pub fn large_enum_error() -> Result<(), Self> {
++   |                                  ^^^^^^^^^^^^^^^^ the `Err`-variant is at least 513 bytes
++   |
++   = help: try reducing the size of `LargeErrorVariants<()>`, for example by boxing large elements or replacing it with `Box<LargeErrorVariants<()>>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:53:25
++   |
++LL |     fn large_error() -> Result<(), [u8; 512]> {
++   |                         ^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
++   |
++   = help: try reducing the size of `[u8; 512]`, for example by boxing large elements or replacing it with `Box<[u8; 512]>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:72:29
++   |
++LL | pub fn large_union_err() -> Result<(), FullyDefinedUnionError> {
++   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
++   |
++   = help: try reducing the size of `FullyDefinedUnionError`, for example by boxing large elements or replacing it with `Box<FullyDefinedUnionError>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:81:40
++   |
++LL | pub fn param_large_union<T: Copy>() -> Result<(), UnionError<T>> {
++   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 512 bytes
++   |
++   = help: try reducing the size of `UnionError<T>`, for example by boxing large elements or replacing it with `Box<UnionError<T>>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:90:34
++   |
++LL | pub fn array_error_subst<U>() -> Result<(), ArrayError<i32, U>> {
++   |                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
++   |
++   = help: try reducing the size of `ArrayError<i32, U>`, for example by boxing large elements or replacing it with `Box<ArrayError<i32, U>>`
++
++error: the `Err`-variant returned from this function is very large
++  --> $DIR/result_large_err.rs:94:31
++   |
++LL | pub fn array_error<T, U>() -> Result<(), ArrayError<(i32, T), U>> {
++   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `Err`-variant is at least 128 bytes
++   |
++   = help: try reducing the size of `ArrayError<(i32, T), U>`, for example by boxing large elements or replacing it with `Box<ArrayError<(i32, T), U>>`
++
++error: aborting due to 11 previous errors
++
index 99964f0de075c04a53467f12fd732e2f97a9506e,0000000000000000000000000000000000000000..af01a8df71b015cff39987006088878f8a4db000
mode 100644,000000..100644
--- /dev/null
@@@ -1,158 -1,0 +1,159 @@@
 +#![warn(clippy::same_item_push)]
 +
 +const VALUE: u8 = 7;
 +
 +fn mutate_increment(x: &mut u8) -> u8 {
 +    *x += 1;
 +    *x
 +}
 +
 +fn increment(x: u8) -> u8 {
 +    x + 1
 +}
 +
 +fn fun() -> usize {
 +    42
 +}
 +
 +fn main() {
 +    // ** linted cases **
 +    let mut vec: Vec<u8> = Vec::new();
 +    let item = 2;
 +    for _ in 5..=20 {
 +        vec.push(item);
 +    }
 +
 +    let mut vec: Vec<u8> = Vec::new();
 +    for _ in 0..15 {
 +        let item = 2;
 +        vec.push(item);
 +    }
 +
 +    let mut vec: Vec<u8> = Vec::new();
 +    for _ in 0..15 {
 +        vec.push(13);
 +    }
 +
 +    let mut vec = Vec::new();
 +    for _ in 0..20 {
 +        vec.push(VALUE);
 +    }
 +
 +    let mut vec = Vec::new();
 +    let item = VALUE;
 +    for _ in 0..20 {
 +        vec.push(item);
 +    }
 +
 +    // ** non-linted cases **
 +    let mut spaces = Vec::with_capacity(10);
 +    for _ in 0..10 {
 +        spaces.push(vec![b' ']);
 +    }
 +
 +    // Suggestion should not be given as pushed variable can mutate
 +    let mut vec: Vec<u8> = Vec::new();
 +    let mut item: u8 = 2;
 +    for _ in 0..30 {
 +        vec.push(mutate_increment(&mut item));
 +    }
 +
 +    let mut vec: Vec<u8> = Vec::new();
 +    let mut item: u8 = 2;
 +    let mut item2 = &mut mutate_increment(&mut item);
 +    for _ in 0..30 {
 +        vec.push(mutate_increment(item2));
 +    }
 +
 +    let mut vec: Vec<usize> = Vec::new();
 +    for (a, b) in [0, 1, 4, 9, 16].iter().enumerate() {
 +        vec.push(a);
 +    }
 +
 +    let mut vec: Vec<u8> = Vec::new();
 +    for i in 0..30 {
 +        vec.push(increment(i));
 +    }
 +
 +    let mut vec: Vec<u8> = Vec::new();
 +    for i in 0..30 {
 +        vec.push(i + i * i);
 +    }
 +
 +    // Suggestion should not be given as there are multiple pushes that are not the same
 +    let mut vec: Vec<u8> = Vec::new();
 +    let item: u8 = 2;
 +    for _ in 0..30 {
 +        vec.push(item);
 +        vec.push(item * 2);
 +    }
 +
 +    // Suggestion should not be given as Vec is not involved
 +    for _ in 0..5 {
 +        println!("Same Item Push");
 +    }
 +
 +    struct A {
 +        kind: u32,
 +    }
 +    let mut vec_a: Vec<A> = Vec::new();
 +    for i in 0..30 {
 +        vec_a.push(A { kind: i });
 +    }
 +    let mut vec: Vec<u8> = Vec::new();
 +    for a in vec_a {
 +        vec.push(2u8.pow(a.kind));
 +    }
 +
 +    // Fix #5902
 +    let mut vec: Vec<u8> = Vec::new();
 +    let mut item = 0;
 +    for _ in 0..10 {
 +        vec.push(item);
 +        item += 10;
 +    }
 +
 +    // Fix #5979
 +    let mut vec: Vec<std::fs::File> = Vec::new();
 +    for _ in 0..10 {
 +        vec.push(std::fs::File::open("foobar").unwrap());
 +    }
 +    // Fix #5979
 +    #[derive(Clone)]
 +    struct S;
 +
 +    trait T {}
 +    impl T for S {}
 +
 +    let mut vec: Vec<Box<dyn T>> = Vec::new();
 +    for _ in 0..10 {
 +        vec.push(Box::new(S {}));
 +    }
 +
 +    // Fix #5985
 +    let mut vec = Vec::new();
 +    let item = 42;
 +    let item = fun();
 +    for _ in 0..20 {
 +        vec.push(item);
 +    }
 +
 +    // Fix #5985
 +    let mut vec = Vec::new();
 +    let key = 1;
 +    for _ in 0..20 {
 +        let item = match key {
 +            1 => 10,
 +            _ => 0,
 +        };
 +        vec.push(item);
 +    }
 +
 +    // Fix #6987
 +    let mut vec = Vec::new();
++    #[allow(clippy::needless_borrow)]
 +    for _ in 0..10 {
 +        vec.push(1);
 +        vec.extend(&[2]);
 +    }
 +}
index 30fd17c59e518ed931dd82d8ed01c5e64865cf62,0000000000000000000000000000000000000000..16673c01e63017da5769a39ea8085cf9f348dc76
mode 100644,000000..100644
--- /dev/null
@@@ -1,26 -1,0 +1,26 @@@
-     let mut x = "".to_owned();
 +// aux-build:macro_rules.rs
 +
 +#[macro_use]
 +extern crate macro_rules;
 +
 +#[warn(clippy::string_add)]
 +#[allow(clippy::string_add_assign, unused)]
 +fn main() {
 +    // ignores assignment distinction
-     let y = "".to_owned();
++    let mut x = String::new();
 +
 +    for _ in 1..3 {
 +        x = x + ".";
 +    }
 +
++    let y = String::new();
 +    let z = y + "...";
 +
 +    assert_eq!(&x, &z);
 +
 +    let mut x = 1;
 +    x = x + 1;
 +    assert_eq!(2, x);
 +
 +    string_add!();
 +}
index db71bab1e5214a96baceeb1a62ee426e3d6173a3,0000000000000000000000000000000000000000..b687f43b2541a016af4cb3abe8a07ce865b5de2c
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,21 @@@
-     let mut x = "".to_owned();
 +// run-rustfix
 +
 +#[allow(clippy::string_add, unused)]
 +#[warn(clippy::string_add_assign)]
 +fn main() {
 +    // ignores assignment distinction
-     let y = "".to_owned();
++    let mut x = String::new();
 +
 +    for _ in 1..3 {
 +        x += ".";
 +    }
 +
++    let y = String::new();
 +    let z = y + "...";
 +
 +    assert_eq!(&x, &z);
 +
 +    let mut x = 1;
 +    x += 1;
 +    assert_eq!(2, x);
 +}
index 644991945cbe2b77da49ca71a1e5e8a8830301a3,0000000000000000000000000000000000000000..e5dbde108fbdbebd46df2e51583b9f18f34603c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,21 -1,0 +1,21 @@@
-     let mut x = "".to_owned();
 +// run-rustfix
 +
 +#[allow(clippy::string_add, unused)]
 +#[warn(clippy::string_add_assign)]
 +fn main() {
 +    // ignores assignment distinction
-     let y = "".to_owned();
++    let mut x = String::new();
 +
 +    for _ in 1..3 {
 +        x = x + ".";
 +    }
 +
++    let y = String::new();
 +    let z = y + "...";
 +
 +    assert_eq!(&x, &z);
 +
 +    let mut x = 1;
 +    x = x + 1;
 +    assert_eq!(2, x);
 +}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cba21bf4a93a1b4260b0c30490a3468091bb116e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,62 @@@
++#![warn(clippy::suspicious_to_owned)]
++#![warn(clippy::implicit_clone)]
++#![allow(clippy::redundant_clone)]
++use std::borrow::Cow;
++use std::ffi::{c_char, CStr};
++
++fn main() {
++    let moo = "Moooo";
++    let c_moo = b"Moooo\0";
++    let c_moo_ptr = c_moo.as_ptr() as *const c_char;
++    let moos = ['M', 'o', 'o'];
++    let moos_vec = moos.to_vec();
++
++    // we expect this to be linted
++    let cow = Cow::Borrowed(moo);
++    let _ = cow.to_owned();
++    // we expect no lints for this
++    let cow = Cow::Borrowed(moo);
++    let _ = cow.into_owned();
++    // we expect no lints for this
++    let cow = Cow::Borrowed(moo);
++    let _ = cow.clone();
++
++    // we expect this to be linted
++    let cow = Cow::Borrowed(&moos);
++    let _ = cow.to_owned();
++    // we expect no lints for this
++    let cow = Cow::Borrowed(&moos);
++    let _ = cow.into_owned();
++    // we expect no lints for this
++    let cow = Cow::Borrowed(&moos);
++    let _ = cow.clone();
++
++    // we expect this to be linted
++    let cow = Cow::Borrowed(&moos_vec);
++    let _ = cow.to_owned();
++    // we expect no lints for this
++    let cow = Cow::Borrowed(&moos_vec);
++    let _ = cow.into_owned();
++    // we expect no lints for this
++    let cow = Cow::Borrowed(&moos_vec);
++    let _ = cow.clone();
++
++    // we expect this to be linted
++    let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
++    let _ = cow.to_owned();
++    // we expect no lints for this
++    let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
++    let _ = cow.into_owned();
++    // we expect no lints for this
++    let cow = unsafe { CStr::from_ptr(c_moo_ptr) }.to_string_lossy();
++    let _ = cow.clone();
++
++    // we expect no lints for these
++    let _ = moo.to_owned();
++    let _ = c_moo.to_owned();
++    let _ = moos.to_owned();
++
++    // we expect implicit_clone lints for these
++    let _ = String::from(moo).to_owned();
++    let _ = moos_vec.to_owned();
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..92e1024bf1f4ecd212275549e05dd323296bb49a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,42 @@@
++error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned
++  --> $DIR/suspicious_to_owned.rs:16:13
++   |
++LL |     let _ = cow.to_owned();
++   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
++   |
++   = note: `-D clippy::suspicious-to-owned` implied by `-D warnings`
++
++error: this `to_owned` call clones the std::borrow::Cow<[char; 3]> itself and does not cause the std::borrow::Cow<[char; 3]> contents to become owned
++  --> $DIR/suspicious_to_owned.rs:26:13
++   |
++LL |     let _ = cow.to_owned();
++   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
++
++error: this `to_owned` call clones the std::borrow::Cow<std::vec::Vec<char>> itself and does not cause the std::borrow::Cow<std::vec::Vec<char>> contents to become owned
++  --> $DIR/suspicious_to_owned.rs:36:13
++   |
++LL |     let _ = cow.to_owned();
++   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
++
++error: this `to_owned` call clones the std::borrow::Cow<str> itself and does not cause the std::borrow::Cow<str> contents to become owned
++  --> $DIR/suspicious_to_owned.rs:46:13
++   |
++LL |     let _ = cow.to_owned();
++   |             ^^^^^^^^^^^^^^ help: consider using, depending on intent: `cow.clone()` or `cow.into_owned()`
++
++error: implicitly cloning a `String` by calling `to_owned` on its dereferenced type
++  --> $DIR/suspicious_to_owned.rs:60:13
++   |
++LL |     let _ = String::from(moo).to_owned();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `String::from(moo).clone()`
++   |
++   = note: `-D clippy::implicit-clone` implied by `-D warnings`
++
++error: implicitly cloning a `Vec` by calling `to_owned` on its dereferenced type
++  --> $DIR/suspicious_to_owned.rs:61:13
++   |
++LL |     let _ = moos_vec.to_owned();
++   |             ^^^^^^^^^^^^^^^^^^^ help: consider using: `moos_vec.clone()`
++
++error: aborting due to 6 previous errors
++
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4ce5d421782250987f5efcbaab50ba825f2a9c2b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,112 @@@
++// run-rustfix
++#![deny(clippy::trait_duplication_in_bounds)]
++#![allow(unused)]
++
++fn bad_foo<T: Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
++    unimplemented!();
++}
++
++fn bad_bar<T, U>(arg0: T, arg1: U)
++where
++    T: Clone + Copy,
++    U: Clone + Copy,
++{
++    unimplemented!();
++}
++
++fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
++    unimplemented!();
++}
++
++fn good_foo<T, U>(arg0: T, arg1: U)
++where
++    T: Clone + Copy,
++    U: Clone + Copy,
++{
++    unimplemented!();
++}
++
++trait GoodSelfTraitBound: Clone + Copy {
++    fn f();
++}
++
++trait GoodSelfWhereClause {
++    fn f()
++    where
++        Self: Clone + Copy;
++}
++
++trait BadSelfTraitBound: Clone {
++    fn f();
++}
++
++trait BadSelfWhereClause {
++    fn f()
++    where
++        Self: Clone;
++}
++
++trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
++    fn f();
++}
++
++trait GoodWhereClause<T, U> {
++    fn f()
++    where
++        T: Clone + Copy,
++        U: Clone + Copy;
++}
++
++trait BadTraitBound<T: Clone + Copy, U: Clone + Copy> {
++    fn f();
++}
++
++trait BadWhereClause<T, U> {
++    fn f()
++    where
++        T: Clone + Copy,
++        U: Clone + Copy;
++}
++
++struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
++    t: T,
++    u: U,
++}
++
++impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
++    // this should not warn
++    fn f() {}
++}
++
++struct GoodStructWhereClause;
++
++impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
++where
++    T: Clone + Copy,
++    U: Clone + Copy,
++{
++    // this should not warn
++    fn f() {}
++}
++
++fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
++
++trait GenericTrait<T> {}
++
++fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
++    unimplemented!();
++}
++
++fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
++    unimplemented!();
++}
++
++mod foo {
++    pub trait Clone {}
++}
++
++fn qualified_path<T: std::clone::Clone + foo::Clone>(arg0: T) {
++    unimplemented!();
++}
++
++fn main() {}
index a5751c58aab8f09f1b7c032f281d4281e001e2d5,0000000000000000000000000000000000000000..7f2e96a22e6648eb27aaf1178eec55ff2c86cab5
mode 100644,000000..100644
--- /dev/null
@@@ -1,212 -1,0 +1,112 @@@
- use std::collections::BTreeMap;
- use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
++// run-rustfix
 +#![deny(clippy::trait_duplication_in_bounds)]
 +#![allow(unused)]
 +
- fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
++fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
++    unimplemented!();
++}
 +
-     T: Clone,
-     T: Default,
++fn bad_bar<T, U>(arg0: T, arg1: U)
 +where
- fn good_bar<T: Clone + Default>(arg: T) {
++    T: Clone + Clone + Clone + Copy,
++    U: Clone + Copy,
 +{
 +    unimplemented!();
 +}
 +
- fn good_foo<T>(arg: T)
++fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
 +    unimplemented!();
 +}
 +
-     T: Clone + Default,
++fn good_foo<T, U>(arg0: T, arg1: U)
 +where
- fn good_foobar<T: Default>(arg: T)
- where
-     T: Clone,
- {
-     unimplemented!();
++    T: Clone + Copy,
++    U: Clone + Copy,
 +{
 +    unimplemented!();
 +}
 +
- trait T: Default {
++trait GoodSelfTraitBound: Clone + Copy {
++    fn f();
 +}
 +
-         Self: Default;
++trait GoodSelfWhereClause {
 +    fn f()
 +    where
- trait U: Default {
++        Self: Clone + Copy;
 +}
 +
-         Self: Clone;
++trait BadSelfTraitBound: Clone + Clone + Clone {
++    fn f();
++}
++
++trait BadSelfWhereClause {
 +    fn f()
 +    where
- trait ZZ: Default {
-     fn g();
-     fn h();
++        Self: Clone + Clone + Clone;
++}
++
++trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
++    fn f();
 +}
 +
-         Self: Default + Clone;
++trait GoodWhereClause<T, U> {
 +    fn f()
 +    where
- trait BadTrait: Default + Clone {
++        T: Clone + Copy,
++        U: Clone + Copy;
 +}
 +
-         Self: Default + Clone;
-     fn g()
-     where
-         Self: Default;
-     fn h()
-     where
-         Self: Copy;
++trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
++    fn f();
++}
++
++trait BadWhereClause<T, U> {
 +    fn f()
 +    where
- #[derive(Default, Clone)]
- struct Life;
++        T: Clone + Clone + Clone + Copy,
++        U: Clone + Copy;
 +}
 +
- impl T for Life {
++struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
++    t: T,
++    u: U,
++}
 +
- impl U for Life {
++impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
 +    // this should not warn
 +    fn f() {}
 +}
 +
- // should not warn
- trait Iter: Iterator {
-     fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
-     where
-         Self: Iterator<Item = (K, V)> + Sized,
-         K: Ord + Eq,
-     {
-         unimplemented!();
-     }
- }
++struct GoodStructWhereClause;
++
++impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
++where
++    T: Clone + Copy,
++    U: Clone + Copy,
++{
 +    // this should not warn
 +    fn f() {}
 +}
 +
- struct Foo;
++fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
 +
- trait FooIter: Iterator<Item = Foo> {
-     fn bar()
-     where
-         Self: Iterator<Item = Foo>,
-     {
-     }
++trait GenericTrait<T> {}
 +
- // This should not lint
- fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
- mod repeated_where_clauses_or_trait_bounds {
-     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
-         unimplemented!();
-     }
-     fn bad_bar<T, U>(arg0: T, arg1: U)
-     where
-         T: Clone + Clone + Clone + Copy,
-         U: Clone + Copy,
-     {
-         unimplemented!();
-     }
-     fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
-         unimplemented!();
-     }
-     fn good_foo<T, U>(arg0: T, arg1: U)
-     where
-         T: Clone + Copy,
-         U: Clone + Copy,
-     {
-         unimplemented!();
-     }
-     trait GoodSelfTraitBound: Clone + Copy {
-         fn f();
-     }
-     trait GoodSelfWhereClause {
-         fn f()
-         where
-             Self: Clone + Copy;
-     }
-     trait BadSelfTraitBound: Clone + Clone + Clone {
-         fn f();
-     }
-     trait BadSelfWhereClause {
-         fn f()
-         where
-             Self: Clone + Clone + Clone;
-     }
-     trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
-         fn f();
-     }
-     trait GoodWhereClause<T, U> {
-         fn f()
-         where
-             T: Clone + Copy,
-             U: Clone + Copy;
-     }
-     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
-         fn f();
-     }
-     trait BadWhereClause<T, U> {
-         fn f()
-         where
-             T: Clone + Clone + Clone + Copy,
-             U: Clone + Copy;
-     }
-     struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
-         t: T,
-         u: U,
-     }
-     impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
-         // this should not warn
-         fn f() {}
-     }
-     struct GoodStructWhereClause;
-     impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
-     where
-         T: Clone + Copy,
-         U: Clone + Copy,
-     {
-         // this should not warn
-         fn f() {}
-     }
-     fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
-     trait GenericTrait<T> {}
-     // This should not warn but currently does see #8757
-     fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
-         unimplemented!();
-     }
-     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
-         unimplemented!();
-     }
++fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
++    unimplemented!();
 +}
 +
-     mod foo {
-         pub trait Clone {}
-     }
++fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
++    unimplemented!();
++}
 +
-     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
-         unimplemented!();
-     }
++mod foo {
++    pub trait Clone {}
++}
 +
++fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
++    unimplemented!();
 +}
 +
 +fn main() {}
index 7ef04e52708f4c91e16c67c6dddf6cc786779c9b,0000000000000000000000000000000000000000..af800ba78880c39ed5e5b936c850e5138f993c59
mode 100644,000000..100644
--- /dev/null
@@@ -1,167 -1,0 +1,56 @@@
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:7:15
++error: these bounds contain repeated elements
++  --> $DIR/trait_duplication_in_bounds.rs:5:15
 +   |
- LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
-    |               ^^^^^
++LL | fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
++   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 +   |
 +note: the lint level is defined here
-   --> $DIR/trait_duplication_in_bounds.rs:1:9
++  --> $DIR/trait_duplication_in_bounds.rs:2:9
 +   |
 +LL | #![deny(clippy::trait_duplication_in_bounds)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-    = help: consider removing this trait bound
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:7:23
-    |
- LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
-    |                       ^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:36:15
-    |
- LL |         Self: Default;
-    |               ^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:50:15
-    |
- LL |         Self: Default + Clone;
-    |               ^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:56:15
-    |
- LL |         Self: Default + Clone;
-    |               ^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:56:25
-    |
- LL |         Self: Default + Clone;
-    |                         ^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:59:15
-    |
- LL |         Self: Default;
-    |               ^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in trait declaration
-   --> $DIR/trait_duplication_in_bounds.rs:94:15
-    |
- LL |         Self: Iterator<Item = Foo>,
-    |               ^^^^^^^^^^^^^^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:103:19
-    |
- LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
-    |                   ^^^^^
-    |
-    = help: consider removing this trait bound
- error: these bounds contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:103:19
-    |
- LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
-    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:109:12
-    |
- LL |         T: Clone + Clone + Clone + Copy,
-    |            ^^^^^
-    |
-    = help: consider removing this trait bound
 +
 +error: these where clauses contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:109:12
++  --> $DIR/trait_duplication_in_bounds.rs:11:8
 +   |
- LL |         T: Clone + Clone + Clone + Copy,
-    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
++LL |     T: Clone + Clone + Clone + Copy,
++   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 +
 +error: these bounds contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:137:30
++  --> $DIR/trait_duplication_in_bounds.rs:39:26
 +   |
- LL |     trait BadSelfTraitBound: Clone + Clone + Clone {
-    |                              ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
++LL | trait BadSelfTraitBound: Clone + Clone + Clone {
++   |                          ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
 +
 +error: these where clauses contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:144:19
-    |
- LL |             Self: Clone + Clone + Clone;
-    |                   ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:158:28
++  --> $DIR/trait_duplication_in_bounds.rs:46:15
 +   |
- LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
-    |                            ^^^^^
-    |
-    = help: consider removing this trait bound
++LL |         Self: Clone + Clone + Clone;
++   |               ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
 +
 +error: these bounds contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:158:28
++  --> $DIR/trait_duplication_in_bounds.rs:60:24
 +   |
- LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
-    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
++LL | trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
++   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 +
 +error: these where clauses contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:165:16
-    |
- LL |             T: Clone + Clone + Clone + Copy,
-    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:195:24
-    |
- LL |     fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
-    |                        ^^^^^^^^^^^^^^^^^
-    |
-    = help: consider removing this trait bound
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:199:23
++  --> $DIR/trait_duplication_in_bounds.rs:67:12
 +   |
- LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
-    |                       ^^^^^^^^^^^^^^^^^
-    |
-    = help: consider removing this trait bound
++LL |         T: Clone + Clone + Clone + Copy,
++   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
 +
 +error: these bounds contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:199:23
-    |
- LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
-    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
- error: this trait bound is already specified in the where clause
-   --> $DIR/trait_duplication_in_bounds.rs:207:26
-    |
- LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
-    |                          ^^^^^^^^^^^^^^^^^
++  --> $DIR/trait_duplication_in_bounds.rs:100:19
 +   |
-    = help: consider removing this trait bound
++LL | fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
++   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u64> + GenericTrait<u32>`
 +
 +error: these bounds contain repeated elements
-   --> $DIR/trait_duplication_in_bounds.rs:207:26
++  --> $DIR/trait_duplication_in_bounds.rs:108:22
 +   |
- LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
-    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
++LL | fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
++   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::clone::Clone + foo::Clone`
 +
- error: aborting due to 22 previous errors
++error: aborting due to 8 previous errors
 +
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5630a0345adb1278492d6f44e2c3b8923df9fbaa
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,166 @@@
++#![deny(clippy::trait_duplication_in_bounds)]
++
++use std::collections::BTreeMap;
++use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
++
++fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
++where
++    T: Clone,
++    T: Default,
++{
++    unimplemented!();
++}
++
++fn good_bar<T: Clone + Default>(arg: T) {
++    unimplemented!();
++}
++
++fn good_foo<T>(arg: T)
++where
++    T: Clone + Default,
++{
++    unimplemented!();
++}
++
++fn good_foobar<T: Default>(arg: T)
++where
++    T: Clone,
++{
++    unimplemented!();
++}
++
++trait T: Default {
++    fn f()
++    where
++        Self: Default;
++}
++
++trait U: Default {
++    fn f()
++    where
++        Self: Clone;
++}
++
++trait ZZ: Default {
++    fn g();
++    fn h();
++    fn f()
++    where
++        Self: Default + Clone;
++}
++
++trait BadTrait: Default + Clone {
++    fn f()
++    where
++        Self: Default + Clone;
++    fn g()
++    where
++        Self: Default;
++    fn h()
++    where
++        Self: Copy;
++}
++
++#[derive(Default, Clone)]
++struct Life;
++
++impl T for Life {
++    // this should not warn
++    fn f() {}
++}
++
++impl U for Life {
++    // this should not warn
++    fn f() {}
++}
++
++// should not warn
++trait Iter: Iterator {
++    fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
++    where
++        Self: Iterator<Item = (K, V)> + Sized,
++        K: Ord + Eq,
++    {
++        unimplemented!();
++    }
++}
++
++struct Foo;
++
++trait FooIter: Iterator<Item = Foo> {
++    fn bar()
++    where
++        Self: Iterator<Item = Foo>,
++    {
++    }
++}
++
++// The below should not lint and exist to guard against false positives
++fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
++
++pub mod one {
++    #[derive(Clone, Debug)]
++    struct MultiProductIter<I>
++    where
++        I: Iterator + Clone,
++        I::Item: Clone,
++    {
++        _marker: I,
++    }
++
++    pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
++    where
++        I: Iterator + Clone,
++        I::Item: Clone;
++
++    pub fn multi_cartesian_product<H>(_: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
++    where
++        H: Iterator,
++        H::Item: IntoIterator,
++        <H::Item as IntoIterator>::IntoIter: Clone,
++        <H::Item as IntoIterator>::Item: Clone,
++    {
++        todo!()
++    }
++}
++
++pub mod two {
++    use std::iter::Peekable;
++
++    pub struct MergeBy<I, J, F>
++    where
++        I: Iterator,
++        J: Iterator<Item = I::Item>,
++    {
++        _i: Peekable<I>,
++        _j: Peekable<J>,
++        _f: F,
++    }
++
++    impl<I, J, F> Clone for MergeBy<I, J, F>
++    where
++        I: Iterator,
++        J: Iterator<Item = I::Item>,
++        std::iter::Peekable<I>: Clone,
++        std::iter::Peekable<J>: Clone,
++        F: Clone,
++    {
++        fn clone(&self) -> Self {
++            Self {
++                _i: self._i.clone(),
++                _j: self._j.clone(),
++                _f: self._f.clone(),
++            }
++        }
++    }
++}
++
++pub trait Trait {}
++
++pub fn f(_a: impl Trait, _b: impl Trait) {}
++
++pub trait ImplTrait<T> {}
++
++impl<A, B> ImplTrait<(A, B)> for Foo where Foo: ImplTrait<A> + ImplTrait<B> {}
++
++fn main() {}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fbd9abb005f1f1fdf069c3c96691c12964ff5d24
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,71 @@@
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:15
++   |
++LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
++   |               ^^^^^
++   |
++note: the lint level is defined here
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:1:9
++   |
++LL | #![deny(clippy::trait_duplication_in_bounds)]
++   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in the where clause
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:6:23
++   |
++LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
++   |                       ^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in trait declaration
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:35:15
++   |
++LL |         Self: Default;
++   |               ^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in trait declaration
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:49:15
++   |
++LL |         Self: Default + Clone;
++   |               ^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in trait declaration
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:15
++   |
++LL |         Self: Default + Clone;
++   |               ^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in trait declaration
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:55:25
++   |
++LL |         Self: Default + Clone;
++   |                         ^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in trait declaration
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:58:15
++   |
++LL |         Self: Default;
++   |               ^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: this trait bound is already specified in trait declaration
++  --> $DIR/trait_duplication_in_bounds_unfixable.rs:93:15
++   |
++LL |         Self: Iterator<Item = Foo>,
++   |               ^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing this trait bound
++
++error: aborting due to 8 previous errors
++
index ebcaa7a84cfb1e9efc1601d6ac0691c4ad89f790,0000000000000000000000000000000000000000..5aad0b44270a3cc5b9e72a5dd1405e9d52fee5e7
mode 100644,000000..100644
--- /dev/null
@@@ -1,144 -1,0 +1,156 @@@
 +#![warn(clippy::transmute_undefined_repr)]
 +#![allow(clippy::unit_arg, clippy::transmute_ptr_to_ref, clippy::useless_transmute)]
 +
 +use core::any::TypeId;
 +use core::ffi::c_void;
 +use core::mem::{size_of, transmute, MaybeUninit};
++use core::ptr::NonNull;
 +
 +fn value<T>() -> T {
 +    unimplemented!()
 +}
 +
 +struct Empty;
 +struct Ty<T>(T);
 +struct Ty2<T, U>(T, U);
 +
 +#[repr(C)]
 +struct Ty2C<T, U>(T, U);
 +
 +fn main() {
 +    unsafe {
 +        let _: () = transmute(value::<Empty>());
 +        let _: Empty = transmute(value::<()>());
 +
 +        let _: Ty<u32> = transmute(value::<u32>());
 +        let _: Ty<u32> = transmute(value::<u32>());
 +
 +        let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
 +        let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
 +
 +        let _: Ty2<u32, i32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Ok, Ty2 types are the same
 +        let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
 +
 +        let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
 +        let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +
 +        let _: Ty<&()> = transmute(value::<&()>());
 +        let _: &() = transmute(value::<Ty<&()>>());
 +
 +        let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
 +        let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +
 +        let _: Ty<usize> = transmute(value::<&Ty2<u32, i32>>()); // Ok, pointer to usize conversion
 +        let _: &Ty2<u32, i32> = transmute(value::<Ty<usize>>()); // Ok, pointer to usize conversion
 +
 +        let _: Ty<[u8; 8]> = transmute(value::<Ty2<u32, i32>>()); // Ok, transmute to byte array
 +        let _: Ty2<u32, i32> = transmute(value::<Ty<[u8; 8]>>()); // Ok, transmute from byte array
 +
 +        // issue #8417
 +        let _: Ty2C<Ty2<u32, i32>, ()> = transmute(value::<Ty2<u32, i32>>()); // Ok, Ty2 types are the same
 +        let _: Ty2<u32, i32> = transmute(value::<Ty2C<Ty2<u32, i32>, ()>>()); // Ok, Ty2 types are the same
 +
 +        let _: &'static mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
 +        let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
 +        let _: *mut Ty2<u32, u32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Ok, Ty2 types are the same
 +        let _: Box<Ty2<u32, u32>> = transmute(value::<*mut Ty2<u32, u32>>()); // Ok, Ty2 types are the same
 +
 +        let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
 +        let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +
 +        let _: *const () = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const ()>()); // Ok, reverse type erasure
 +
 +        let _: *const c_void = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const c_void>()); // Ok, reverse type erasure
 +
 +        enum Erase {}
 +        let _: *const Erase = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase>()); // Ok, reverse type erasure
 +
 +        struct Erase2(
 +            [u8; 0],
 +            core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
 +        );
 +        let _: *const Erase2 = transmute(value::<Ty<&Ty2<u32, f32>>>()); // Ok, type erasure
 +        let _: Ty<&Ty2<u32, f32>> = transmute(value::<*const Erase2>()); // Ok, reverse type erasure
 +
 +        let _: *const () = transmute(value::<&&[u8]>()); // Ok, type erasure
 +        let _: &&[u8] = transmute(value::<*const ()>()); // Ok, reverse type erasure
 +
 +        let _: *mut c_void = transmute(value::<&mut &[u8]>()); // Ok, type erasure
 +        let _: &mut &[u8] = transmute(value::<*mut c_void>()); // Ok, reverse type erasure
 +
 +        let _: [u8; size_of::<&[u8]>()] = transmute(value::<&[u8]>()); // Ok, transmute to byte array
 +        let _: &[u8] = transmute(value::<[u8; size_of::<&[u8]>()]>()); // Ok, transmute from byte array
 +
 +        let _: [usize; 2] = transmute(value::<&[u8]>()); // Ok, transmute to int array
 +        let _: &[u8] = transmute(value::<[usize; 2]>()); // Ok, transmute from int array
 +
 +        let _: *const [u8] = transmute(value::<Box<[u8]>>()); // Ok
 +        let _: Box<[u8]> = transmute(value::<*mut [u8]>()); // Ok
 +
 +        let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>,)>()); // Ok
 +        let _: (Ty2<u32, u32>,) = transmute(value::<Ty2<u32, u32>>()); // Ok
 +
 +        let _: Ty2<u32, u32> = transmute(value::<(Ty2<u32, u32>, ())>()); // Ok
 +        let _: (Ty2<u32, u32>, ()) = transmute(value::<Ty2<u32, u32>>()); // Ok
 +
 +        let _: Ty2<u32, u32> = transmute(value::<((), Ty2<u32, u32>)>()); // Ok
 +        let _: ((), Ty2<u32, u32>) = transmute(value::<Ty2<u32, u32>>()); // Ok
 +
 +        let _: (usize, usize) = transmute(value::<&[u8]>()); // Ok
 +        let _: &[u8] = transmute(value::<(usize, usize)>()); // Ok
 +
 +        trait Trait {}
 +        let _: (isize, isize) = transmute(value::<&dyn Trait>()); // Ok
 +        let _: &dyn Trait = transmute(value::<(isize, isize)>()); // Ok
 +
 +        let _: MaybeUninit<Ty2<u32, u32>> = transmute(value::<Ty2<u32, u32>>()); // Ok
 +        let _: Ty2<u32, u32> = transmute(value::<MaybeUninit<Ty2<u32, u32>>>()); // Ok
 +
 +        let _: Ty<&[u32]> = transmute::<&[u32], _>(value::<&Vec<u32>>()); // Ok
++
++        let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<Ty2<u32, u32>, u32>>()); // Ok
++        let _: *const Ty2C<Ty2<u32, u32>, u32> = transmute(value::<*const Ty2<u32, u32>>()); // Ok
++        let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<(), Ty2<u32, u32>>>()); // Ok
++        let _: *const Ty2C<(), Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Ok
++
++        let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err
++        let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err
++
++        let _: NonNull<u8> = transmute(value::<NonNull<(String, String)>>()); // Ok
++        let _: NonNull<(String, String)> = transmute(value::<NonNull<u8>>()); // Ok
 +    }
 +}
 +
 +fn _with_generics<T: 'static, U: 'static>() {
 +    if TypeId::of::<T>() != TypeId::of::<u32>() || TypeId::of::<T>() != TypeId::of::<U>() {
 +        return;
 +    }
 +    unsafe {
 +        let _: &u32 = transmute(value::<&T>()); // Ok
 +        let _: &T = transmute(value::<&u32>()); // Ok
 +
 +        let _: Vec<U> = transmute(value::<Vec<T>>()); // Ok
 +        let _: Vec<T> = transmute(value::<Vec<U>>()); // Ok
 +
 +        let _: Ty<&u32> = transmute(value::<&T>()); // Ok
 +        let _: Ty<&T> = transmute(value::<&u32>()); // Ok
 +
 +        let _: Vec<u32> = transmute(value::<Vec<T>>()); // Ok
 +        let _: Vec<T> = transmute(value::<Vec<u32>>()); // Ok
 +
 +        let _: &Ty2<u32, u32> = transmute(value::<&Ty2<T, U>>()); // Ok
 +        let _: &Ty2<T, U> = transmute(value::<&Ty2<u32, u32>>()); // Ok
 +
 +        let _: Vec<Vec<u32>> = transmute(value::<Vec<Vec<T>>>()); // Ok
 +        let _: Vec<Vec<T>> = transmute(value::<Vec<Vec<u32>>>()); // Ok
 +
 +        let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
 +        let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
 +
 +        let _: *const u32 = transmute(value::<Box<T>>()); // Ok
 +        let _: Box<T> = transmute(value::<*const u32>()); // Ok
 +    }
 +}
index 28bfba6c7571dc249ad3c97ae5c31982a4e07f9a,0000000000000000000000000000000000000000..e50a773290e17c60fe6a5bc839e0d009fe821650
mode 100644,000000..100644
--- /dev/null
@@@ -1,80 -1,0 +1,96 @@@
-   --> $DIR/transmute_undefined_repr.rs:27:33
 +error: transmute from `Ty2<u32, i32>` which has an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:28:32
++  --> $DIR/transmute_undefined_repr.rs:28:33
 +   |
 +LL |         let _: Ty2C<u32, i32> = transmute(value::<Ty2<u32, i32>>()); // Lint, Ty2 is unordered
 +   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::transmute-undefined-repr` implied by `-D warnings`
 +
 +error: transmute into `Ty2<u32, i32>` which has an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:33:32
++  --> $DIR/transmute_undefined_repr.rs:29:32
 +   |
 +LL |         let _: Ty2<u32, i32> = transmute(value::<Ty2C<u32, i32>>()); // Lint, Ty2 is unordered
 +   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +
 +error: transmute from `Ty<Ty2<u32, i32>>` to `Ty2<u32, f32>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:34:36
++  --> $DIR/transmute_undefined_repr.rs:34:32
 +   |
 +LL |         let _: Ty2<u32, f32> = transmute(value::<Ty<Ty2<u32, i32>>>()); // Lint, different Ty2 instances
 +   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
 +error: transmute from `Ty2<u32, f32>` to `Ty<Ty2<u32, i32>>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:39:33
++  --> $DIR/transmute_undefined_repr.rs:35:36
 +   |
 +LL |         let _: Ty<Ty2<u32, i32>> = transmute(value::<Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
 +error: transmute from `Ty<&Ty2<u32, i32>>` to `&Ty2<u32, f32>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:40:37
++  --> $DIR/transmute_undefined_repr.rs:40:33
 +   |
 +LL |         let _: &Ty2<u32, f32> = transmute(value::<Ty<&Ty2<u32, i32>>>()); // Lint, different Ty2 instances
 +   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
 +error: transmute from `&Ty2<u32, f32>` to `Ty<&Ty2<u32, i32>>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:57:45
++  --> $DIR/transmute_undefined_repr.rs:41:37
 +   |
 +LL |         let _: Ty<&Ty2<u32, i32>> = transmute(value::<&Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
 +error: transmute from `std::boxed::Box<Ty2<u32, u32>>` to `&mut Ty2<u32, f32>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:58:37
++  --> $DIR/transmute_undefined_repr.rs:58:45
 +   |
 +LL |         let _: &'static mut Ty2<u32, f32> = transmute(value::<Box<Ty2<u32, u32>>>()); // Lint, different Ty2 instances
 +   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
 +error: transmute from `&mut Ty2<u32, f32>` to `std::boxed::Box<Ty2<u32, u32>>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:138:35
++  --> $DIR/transmute_undefined_repr.rs:59:37
 +   |
 +LL |         let _: Box<Ty2<u32, u32>> = transmute(value::<&'static mut Ty2<u32, f32>>()); // Lint, different Ty2 instances
 +   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Ty2`) may have different layouts
 +
++error: transmute into `*const Ty2<u32, u32>` which has an undefined layout
++  --> $DIR/transmute_undefined_repr.rs:119:39
++   |
++LL |         let _: *const Ty2<u32, u32> = transmute(value::<*const Ty2C<u32, Ty2<u32, u32>>>()); // Err
++   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: the contained type `Ty2<u32, u32>` has an undefined layout
++
++error: transmute from `*const Ty2<u32, u32>` which has an undefined layout
++  --> $DIR/transmute_undefined_repr.rs:120:50
++   |
++LL |         let _: *const Ty2C<u32, Ty2<u32, u32>> = transmute(value::<*const Ty2<u32, u32>>()); // Err
++   |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = note: the contained type `Ty2<u32, u32>` has an undefined layout
++
 +error: transmute from `std::vec::Vec<Ty2<U, i32>>` to `std::vec::Vec<Ty2<T, u32>>`, both of which have an undefined layout
-   --> $DIR/transmute_undefined_repr.rs:139:35
++  --> $DIR/transmute_undefined_repr.rs:150:35
 +   |
 +LL |         let _: Vec<Ty2<T, u32>> = transmute(value::<Vec<Ty2<U, i32>>>()); // Err
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Vec`) may have different layouts
 +
 +error: transmute from `std::vec::Vec<Ty2<T, u32>>` to `std::vec::Vec<Ty2<U, i32>>`, both of which have an undefined layout
- error: aborting due to 10 previous errors
++  --> $DIR/transmute_undefined_repr.rs:151:35
 +   |
 +LL |         let _: Vec<Ty2<U, i32>> = transmute(value::<Vec<Ty2<T, u32>>>()); // Err
 +   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: two instances of the same generic type (`Vec`) may have different layouts
 +
++error: aborting due to 12 previous errors
 +
index 328cda369e11bb89a59a5db907abb14deefbf91e,0000000000000000000000000000000000000000..94b4723452fada45a142dcd5dc7992e028ae8b60
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,60 @@@
- #[warn(clippy::non_ascii_literal)]
- fn uni() {
-     print!("\u{dc}ben!");
-     print!("\u{DC}ben!"); // this is ok
- }
 +// run-rustfix
++// compile-flags: --test
++#![allow(dead_code)]
++
 +#[warn(clippy::invisible_characters)]
 +fn zero() {
 +    print!("Here >\u{200B}< is a ZWS, and \u{200B}another");
 +    print!("This\u{200B}is\u{200B}fine");
 +    print!("Here >\u{AD}< is a SHY, and \u{AD}another");
 +    print!("This\u{ad}is\u{ad}fine");
 +    print!("Here >\u{2060}< is a WJ, and \u{2060}another");
 +    print!("This\u{2060}is\u{2060}fine");
 +}
 +
 +#[warn(clippy::unicode_not_nfc)]
 +fn canon() {
 +    print!("̀àh?");
 +    print!("a\u{0300}h?"); // also ok
 +}
 +
- // issue 8013
- #[warn(clippy::non_ascii_literal)]
- fn single_quote() {
-     const _EMPTY_BLOCK: char = '\u{25b1}';
-     const _FULL_BLOCK: char = '\u{25b0}';
++mod non_ascii_literal {
++    #![deny(clippy::non_ascii_literal)]
++
++    fn uni() {
++        print!("\u{dc}ben!");
++        print!("\u{DC}ben!"); // this is ok
++    }
++
++    // issue 8013
++    fn single_quote() {
++        const _EMPTY_BLOCK: char = '\u{25b1}';
++        const _FULL_BLOCK: char = '\u{25b0}';
++    }
++
++    #[test]
++    pub fn issue_7739() {
++        // Ryū crate: https://github.com/dtolnay/ryu
++    }
++
++    mod issue_8263 {
++        #![deny(clippy::non_ascii_literal)]
++
++        // Re-allow for a single test
++        #[test]
++        #[allow(clippy::non_ascii_literal)]
++        fn allowed() {
++            let _ = "悲しいかな、ここに日本語を書くことはできない。";
++        }
 +
-     uni();
++        #[test]
++        fn denied() {
++            let _ = "\u{60b2}\u{3057}\u{3044}\u{304b}\u{306a}\u{3001}\u{3053}\u{3053}\u{306b}\u{65e5}\u{672c}\u{8a9e}\u{3092}\u{66f8}\u{304f}\u{3053}\u{3068}\u{306f}\u{3067}\u{304d}\u{306a}\u{3044}\u{3002}";
++        }
++    }
 +}
 +
 +fn main() {
 +    zero();
-     single_quote();
 +    canon();
 +}
index 7828d6bcbea7ad39e4a68c82ef69b9c9ede16015,0000000000000000000000000000000000000000..6ad0b255b94856c96943f0af5d329db3f7d46f8e
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,60 @@@
- #[warn(clippy::non_ascii_literal)]
- fn uni() {
-     print!("Üben!");
-     print!("\u{DC}ben!"); // this is ok
- }
 +// run-rustfix
++// compile-flags: --test
++#![allow(dead_code)]
++
 +#[warn(clippy::invisible_characters)]
 +fn zero() {
 +    print!("Here >​< is a ZWS, and ​another");
 +    print!("This\u{200B}is\u{200B}fine");
 +    print!("Here >­< is a SHY, and ­another");
 +    print!("This\u{ad}is\u{ad}fine");
 +    print!("Here >⁠< is a WJ, and ⁠another");
 +    print!("This\u{2060}is\u{2060}fine");
 +}
 +
 +#[warn(clippy::unicode_not_nfc)]
 +fn canon() {
 +    print!("̀àh?");
 +    print!("a\u{0300}h?"); // also ok
 +}
 +
- // issue 8013
- #[warn(clippy::non_ascii_literal)]
- fn single_quote() {
-     const _EMPTY_BLOCK: char = '▱';
-     const _FULL_BLOCK: char = '▰';
++mod non_ascii_literal {
++    #![deny(clippy::non_ascii_literal)]
++
++    fn uni() {
++        print!("Üben!");
++        print!("\u{DC}ben!"); // this is ok
++    }
++
++    // issue 8013
++    fn single_quote() {
++        const _EMPTY_BLOCK: char = '▱';
++        const _FULL_BLOCK: char = '▰';
++    }
++
++    #[test]
++    pub fn issue_7739() {
++        // Ryū crate: https://github.com/dtolnay/ryu
++    }
++
++    mod issue_8263 {
++        #![deny(clippy::non_ascii_literal)]
++
++        // Re-allow for a single test
++        #[test]
++        #[allow(clippy::non_ascii_literal)]
++        fn allowed() {
++            let _ = "悲しいかな、ここに日本語を書くことはできない。";
++        }
 +
-     uni();
++        #[test]
++        fn denied() {
++            let _ = "悲しいかな、ここに日本語を書くことはできない。";
++        }
++    }
 +}
 +
 +fn main() {
 +    zero();
-     single_quote();
 +    canon();
 +}
index 01d3f3c0296799cc15b4cabcbc811b786e8da427,0000000000000000000000000000000000000000..ea74a81451e3a9c3157bd99d3819c1690d602266
mode 100644,000000..100644
--- /dev/null
@@@ -1,50 -1,0 +1,66 @@@
-   --> $DIR/unicode.rs:4:12
 +error: invisible character detected
-   --> $DIR/unicode.rs:6:12
++  --> $DIR/unicode.rs:7:12
 +   |
 +LL |     print!("Here >​< is a ZWS, and ​another");
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{200B}< is a ZWS, and /u{200B}another"`
 +   |
 +   = note: `-D clippy::invisible-characters` implied by `-D warnings`
 +
 +error: invisible character detected
-   --> $DIR/unicode.rs:8:12
++  --> $DIR/unicode.rs:9:12
 +   |
 +LL |     print!("Here >­< is a SHY, and ­another");
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{AD}< is a SHY, and /u{AD}another"`
 +
 +error: invisible character detected
-   --> $DIR/unicode.rs:14:12
++  --> $DIR/unicode.rs:11:12
 +   |
 +LL |     print!("Here >⁠< is a WJ, and ⁠another");
 +   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"Here >/u{2060}< is a WJ, and /u{2060}another"`
 +
 +error: non-NFC Unicode sequence detected
-   --> $DIR/unicode.rs:20:12
++  --> $DIR/unicode.rs:17:12
 +   |
 +LL |     print!("̀àh?");
 +   |            ^^^^^ help: consider replacing the string with: `"̀àh?"`
 +   |
 +   = note: `-D clippy::unicode-not-nfc` implied by `-D warnings`
 +
 +error: literal non-ASCII character detected
- LL |     print!("Üben!");
-    |            ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
++  --> $DIR/unicode.rs:25:16
 +   |
-    = note: `-D clippy::non-ascii-literal` implied by `-D warnings`
++LL |         print!("Üben!");
++   |                ^^^^^^^ help: consider replacing the string with: `"/u{dc}ben!"`
 +   |
-   --> $DIR/unicode.rs:27:32
++note: the lint level is defined here
++  --> $DIR/unicode.rs:22:13
++   |
++LL |     #![deny(clippy::non_ascii_literal)]
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
++
++error: literal non-ASCII character detected
++  --> $DIR/unicode.rs:31:36
++   |
++LL |         const _EMPTY_BLOCK: char = '▱';
++   |                                    ^^^ help: consider replacing the string with: `'/u{25b1}'`
 +
 +error: literal non-ASCII character detected
- LL |     const _EMPTY_BLOCK: char = '▱';
-    |                                ^^^ help: consider replacing the string with: `'/u{25b1}'`
++  --> $DIR/unicode.rs:32:35
 +   |
-   --> $DIR/unicode.rs:28:31
++LL |         const _FULL_BLOCK: char = '▰';
++   |                                   ^^^ help: consider replacing the string with: `'/u{25b0}'`
 +
 +error: literal non-ASCII character detected
- LL |     const _FULL_BLOCK: char = '▰';
-    |                               ^^^ help: consider replacing the string with: `'/u{25b0}'`
++  --> $DIR/unicode.rs:52:21
++   |
++LL |             let _ = "悲しいかな、ここに日本語を書くことはできない。";
++   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider replacing the string with: `"/u{60b2}/u{3057}/u{3044}/u{304b}/u{306a}/u{3001}/u{3053}/u{3053}/u{306b}/u{65e5}/u{672c}/u{8a9e}/u{3092}/u{66f8}/u{304f}/u{3053}/u{3068}/u{306f}/u{3067}/u{304d}/u{306a}/u{3044}/u{3002}"`
++   |
++note: the lint level is defined here
++  --> $DIR/unicode.rs:41:17
 +   |
- error: aborting due to 7 previous errors
++LL |         #![deny(clippy::non_ascii_literal)]
++   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
 +
++error: aborting due to 8 previous errors
 +
index b352b285c86267f42fed1f4be2ce85d51c544afd,0000000000000000000000000000000000000000..ee9f157342d48f835a39883ffa3736e9f9a30885
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,100 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
 +#![allow(
 +    unused_must_use,
 +    clippy::borrow_as_ptr,
 +    clippy::no_effect,
 +    clippy::nonstandard_macro_braces,
 +    clippy::unnecessary_operation
 +)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1_i32;
 +    1_f32;
 +    false;
 +    &1i32 as &i32;
 +
 +    -1_i32;
 +    - 1_i32;
 +    -1_f32;
 +    1_i32;
 +    1_f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // do not lint cast to alias type
 +    1 as I32Alias;
 +    &1 as &I32Alias;
 +}
 +
 +type I32Alias = i32;
 +
 +mod fixable {
 +    #![allow(dead_code)]
 +
 +    fn main() {
 +        // casting integer literal to float is unnecessary
 +        100_f32;
 +        100_f64;
 +        100_f64;
 +        let _ = -100_f32;
 +        let _ = -100_f64;
 +        let _ = -100_f64;
 +        100_f32;
 +        100_f64;
 +        // Should not trigger
 +        #[rustfmt::skip]
 +        let v = vec!(1);
 +        &v as &[i32];
 +        0x10 as f32;
 +        0o10 as f32;
 +        0b10 as f32;
 +        0x11 as f64;
 +        0o11 as f64;
 +        0b11 as f64;
 +
 +        1_u32;
 +        0x10_i32;
 +        0b10_usize;
 +        0o73_u16;
 +        1_000_000_000_u32;
 +
 +        1.0_f64;
 +        0.5_f32;
 +
 +        1.0 as u16;
 +
 +        let _ = -1_i32;
 +        let _ = -1.0_f32;
 +
 +        let _ = 1 as I32Alias;
 +        let _ = &1 as &I32Alias;
 +    }
 +
 +    type I32Alias = i32;
++
++    fn issue_9380() {
++        let _: i32 = -1_i32;
++        let _: f32 = -(1) as f32;
++        let _: i64 = -1_i64;
++        let _: i64 = -(1.0) as i64;
++
++        let _ = -(1 + 1) as i64;
++    }
 +}
index 6c8cc3effe8fefcc9ca0cf5c806549bd0037b0f2,0000000000000000000000000000000000000000..5b70412424c06f602e02d3ecf02025f5f7b59dc7
mode 100644,000000..100644
--- /dev/null
@@@ -1,91 -1,0 +1,100 @@@
 +// run-rustfix
 +#![warn(clippy::unnecessary_cast)]
 +#![allow(
 +    unused_must_use,
 +    clippy::borrow_as_ptr,
 +    clippy::no_effect,
 +    clippy::nonstandard_macro_braces,
 +    clippy::unnecessary_operation
 +)]
 +
 +#[rustfmt::skip]
 +fn main() {
 +    // Test cast_unnecessary
 +    1i32 as i32;
 +    1f32 as f32;
 +    false as bool;
 +    &1i32 as &i32;
 +
 +    -1_i32 as i32;
 +    - 1_i32 as i32;
 +    -1f32 as f32;
 +    1_i32 as i32;
 +    1_f32 as f32;
 +
 +    // macro version
 +    macro_rules! foo {
 +        ($a:ident, $b:ident) => {
 +            #[allow(unused)]
 +            pub fn $a() -> $b {
 +                1 as $b
 +            }
 +        };
 +    }
 +    foo!(a, i32);
 +    foo!(b, f32);
 +    foo!(c, f64);
 +
 +    // do not lint cast to cfg-dependant type
 +    1 as std::os::raw::c_char;
 +
 +    // do not lint cast to alias type
 +    1 as I32Alias;
 +    &1 as &I32Alias;
 +}
 +
 +type I32Alias = i32;
 +
 +mod fixable {
 +    #![allow(dead_code)]
 +
 +    fn main() {
 +        // casting integer literal to float is unnecessary
 +        100 as f32;
 +        100 as f64;
 +        100_i32 as f64;
 +        let _ = -100 as f32;
 +        let _ = -100 as f64;
 +        let _ = -100_i32 as f64;
 +        100. as f32;
 +        100. as f64;
 +        // Should not trigger
 +        #[rustfmt::skip]
 +        let v = vec!(1);
 +        &v as &[i32];
 +        0x10 as f32;
 +        0o10 as f32;
 +        0b10 as f32;
 +        0x11 as f64;
 +        0o11 as f64;
 +        0b11 as f64;
 +
 +        1 as u32;
 +        0x10 as i32;
 +        0b10 as usize;
 +        0o73 as u16;
 +        1_000_000_000 as u32;
 +
 +        1.0 as f64;
 +        0.5 as f32;
 +
 +        1.0 as u16;
 +
 +        let _ = -1 as i32;
 +        let _ = -1.0 as f32;
 +
 +        let _ = 1 as I32Alias;
 +        let _ = &1 as &I32Alias;
 +    }
 +
 +    type I32Alias = i32;
++
++    fn issue_9380() {
++        let _: i32 = -(1) as i32;
++        let _: f32 = -(1) as f32;
++        let _: i64 = -(1) as i64;
++        let _: i64 = -(1.0) as i64;
++
++        let _ = -(1 + 1) as i64;
++    }
 +}
index bad45f0025b2292fe0360c52ec59effefac4c361,0000000000000000000000000000000000000000..f7829ff3b0efd049d72f7619bcdeb6e7708b1d8d
mode 100644,000000..100644
--- /dev/null
@@@ -1,154 -1,0 +1,166 @@@
- error: aborting due to 25 previous errors
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:14:5
 +   |
 +LL |     1i32 as i32;
 +   |     ^^^^^^^^^^^ help: try: `1_i32`
 +   |
 +   = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:15:5
 +   |
 +LL |     1f32 as f32;
 +   |     ^^^^^^^^^^^ help: try: `1_f32`
 +
 +error: casting to the same type is unnecessary (`bool` -> `bool`)
 +  --> $DIR/unnecessary_cast.rs:16:5
 +   |
 +LL |     false as bool;
 +   |     ^^^^^^^^^^^^^ help: try: `false`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:19:5
 +   |
 +LL |     -1_i32 as i32;
 +   |     ^^^^^^^^^^^^^ help: try: `-1_i32`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:20:5
 +   |
 +LL |     - 1_i32 as i32;
 +   |     ^^^^^^^^^^^^^^ help: try: `- 1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:21:5
 +   |
 +LL |     -1f32 as f32;
 +   |     ^^^^^^^^^^^^ help: try: `-1_f32`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:22:5
 +   |
 +LL |     1_i32 as i32;
 +   |     ^^^^^^^^^^^^ help: try: `1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:23:5
 +   |
 +LL |     1_f32 as f32;
 +   |     ^^^^^^^^^^^^ help: try: `1_f32`
 +
 +error: casting integer literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:53:9
 +   |
 +LL |         100 as f32;
 +   |         ^^^^^^^^^^ help: try: `100_f32`
 +
 +error: casting integer literal to `f64` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:54:9
 +   |
 +LL |         100 as f64;
 +   |         ^^^^^^^^^^ help: try: `100_f64`
 +
 +error: casting integer literal to `f64` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:55:9
 +   |
 +LL |         100_i32 as f64;
 +   |         ^^^^^^^^^^^^^^ help: try: `100_f64`
 +
 +error: casting integer literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:56:17
 +   |
 +LL |         let _ = -100 as f32;
 +   |                 ^^^^^^^^^^^ help: try: `-100_f32`
 +
 +error: casting integer literal to `f64` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:57:17
 +   |
 +LL |         let _ = -100 as f64;
 +   |                 ^^^^^^^^^^^ help: try: `-100_f64`
 +
 +error: casting integer literal to `f64` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:58:17
 +   |
 +LL |         let _ = -100_i32 as f64;
 +   |                 ^^^^^^^^^^^^^^^ help: try: `-100_f64`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:59:9
 +   |
 +LL |         100. as f32;
 +   |         ^^^^^^^^^^^ help: try: `100_f32`
 +
 +error: casting float literal to `f64` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:60:9
 +   |
 +LL |         100. as f64;
 +   |         ^^^^^^^^^^^ help: try: `100_f64`
 +
 +error: casting integer literal to `u32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:72:9
 +   |
 +LL |         1 as u32;
 +   |         ^^^^^^^^ help: try: `1_u32`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:73:9
 +   |
 +LL |         0x10 as i32;
 +   |         ^^^^^^^^^^^ help: try: `0x10_i32`
 +
 +error: casting integer literal to `usize` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:74:9
 +   |
 +LL |         0b10 as usize;
 +   |         ^^^^^^^^^^^^^ help: try: `0b10_usize`
 +
 +error: casting integer literal to `u16` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:75:9
 +   |
 +LL |         0o73 as u16;
 +   |         ^^^^^^^^^^^ help: try: `0o73_u16`
 +
 +error: casting integer literal to `u32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:76:9
 +   |
 +LL |         1_000_000_000 as u32;
 +   |         ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
 +
 +error: casting float literal to `f64` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:78:9
 +   |
 +LL |         1.0 as f64;
 +   |         ^^^^^^^^^^ help: try: `1.0_f64`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:79:9
 +   |
 +LL |         0.5 as f32;
 +   |         ^^^^^^^^^^ help: try: `0.5_f32`
 +
 +error: casting integer literal to `i32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:83:17
 +   |
 +LL |         let _ = -1 as i32;
 +   |                 ^^^^^^^^^ help: try: `-1_i32`
 +
 +error: casting float literal to `f32` is unnecessary
 +  --> $DIR/unnecessary_cast.rs:84:17
 +   |
 +LL |         let _ = -1.0 as f32;
 +   |                 ^^^^^^^^^^^ help: try: `-1.0_f32`
 +
++error: casting integer literal to `i32` is unnecessary
++  --> $DIR/unnecessary_cast.rs:93:22
++   |
++LL |         let _: i32 = -(1) as i32;
++   |                      ^^^^^^^^^^^ help: try: `-1_i32`
++
++error: casting integer literal to `i64` is unnecessary
++  --> $DIR/unnecessary_cast.rs:95:22
++   |
++LL |         let _: i64 = -(1) as i64;
++   |                      ^^^^^^^^^^^ help: try: `-1_i64`
++
++error: aborting due to 27 previous errors
 +
index f95f91329a2faeeae22da74496d42568e89512c8,0000000000000000000000000000000000000000..40052c41039e5d01c320f34743fe48331fc9e81a
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,23 @@@
 +// run-rustfix
 +
 +#![warn(clippy::unnecessary_owned_empty_strings)]
 +
 +fn ref_str_argument(_value: &str) {}
 +
 +#[allow(clippy::ptr_arg)]
 +fn ref_string_argument(_value: &String) {}
 +
 +fn main() {
 +    // should be linted
 +    ref_str_argument("");
 +
 +    // should be linted
++    #[allow(clippy::manual_string_new)]
 +    ref_str_argument("");
 +
 +    // should not be linted
 +    ref_str_argument("");
 +
 +    // should not be linted
 +    ref_string_argument(&String::new());
 +}
index 0cbdc151ed9b1b0b3a2056ed6e4e78665f175ee0,0000000000000000000000000000000000000000..2304dff5192b9ee66c34805feab80ab2d55bd62c
mode 100644,000000..100644
--- /dev/null
@@@ -1,22 -1,0 +1,23 @@@
 +// run-rustfix
 +
 +#![warn(clippy::unnecessary_owned_empty_strings)]
 +
 +fn ref_str_argument(_value: &str) {}
 +
 +#[allow(clippy::ptr_arg)]
 +fn ref_string_argument(_value: &String) {}
 +
 +fn main() {
 +    // should be linted
 +    ref_str_argument(&String::new());
 +
 +    // should be linted
++    #[allow(clippy::manual_string_new)]
 +    ref_str_argument(&String::from(""));
 +
 +    // should not be linted
 +    ref_str_argument("");
 +
 +    // should not be linted
 +    ref_string_argument(&String::new());
 +}
index 46bc4597b335f0bfb7be4ce8053f46b3735e95b4,0000000000000000000000000000000000000000..1eb198a8675ea1b2f172667f6f8807d2f624d0fa
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,16 @@@
-   --> $DIR/unnecessary_owned_empty_strings.rs:15:22
 +error: usage of `&String::new()` for a function expecting a `&str` argument
 +  --> $DIR/unnecessary_owned_empty_strings.rs:12:22
 +   |
 +LL |     ref_str_argument(&String::new());
 +   |                      ^^^^^^^^^^^^^^ help: try: `""`
 +   |
 +   = note: `-D clippy::unnecessary-owned-empty-strings` implied by `-D warnings`
 +
 +error: usage of `&String::from("")` for a function expecting a `&str` argument
++  --> $DIR/unnecessary_owned_empty_strings.rs:16:22
 +   |
 +LL |     ref_str_argument(&String::from(""));
 +   |                      ^^^^^^^^^^^^^^^^^ help: try: `""`
 +
 +error: aborting due to 2 previous errors
 +
index f4f76cd3dd493e159a656fe95276b2a41002299b,0000000000000000000000000000000000000000..9cd5bc73b1ec51852af3c2bbeae84cb5ddf05594
mode 100644,000000..100644
--- /dev/null
@@@ -1,331 -1,0 +1,359 @@@
 +// run-rustfix
 +
 +#![allow(clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str));
 +    require_c_str(c_str);
 +
 +    require_os_str(os_str);
 +    require_os_str(&Cow::from(os_str));
 +    require_os_str(os_str);
 +
 +    require_path(path);
 +    require_path(&Cow::from(path));
 +    require_path(path);
 +
 +    require_str(s);
 +    require_str(&Cow::from(s));
 +    require_str(s);
 +    require_str(x_ref.as_ref());
 +
 +    require_slice(slice);
 +    require_slice(&Cow::from(slice));
 +    require_slice(array.as_ref());
 +    require_slice(array_ref.as_ref());
 +    require_slice(slice);
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()));
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str);
 +    require_deref_os_str(os_str);
 +    require_deref_path(path);
 +    require_deref_str(s);
 +    require_deref_slice(slice);
 +
 +    require_impl_deref_c_str(c_str);
 +    require_impl_deref_os_str(os_str);
 +    require_impl_deref_path(path);
 +    require_impl_deref_str(s);
 +    require_impl_deref_slice(slice);
 +
 +    require_deref_str_slice(s, slice);
 +    require_deref_slice_str(slice, s);
 +
 +    require_as_ref_c_str(c_str);
 +    require_as_ref_os_str(os_str);
 +    require_as_ref_path(path);
 +    require_as_ref_str(s);
 +    require_as_ref_str(&x);
 +    require_as_ref_slice(array);
 +    require_as_ref_slice(array_ref);
 +    require_as_ref_slice(slice);
 +
 +    require_impl_as_ref_c_str(c_str);
 +    require_impl_as_ref_os_str(os_str);
 +    require_impl_as_ref_path(path);
 +    require_impl_as_ref_str(s);
 +    require_impl_as_ref_str(&x);
 +    require_impl_as_ref_slice(array);
 +    require_impl_as_ref_slice(array_ref);
 +    require_impl_as_ref_slice(slice);
 +
 +    require_as_ref_str_slice(s, array);
 +    require_as_ref_str_slice(s, array_ref);
 +    require_as_ref_str_slice(s, slice);
 +    require_as_ref_slice_str(array, s);
 +    require_as_ref_slice_str(array_ref, s);
 +    require_as_ref_slice_str(slice, s);
 +
 +    let _ = x.join(x_ref);
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = slice.iter().copied();
 +    let _ = slice.iter().copied();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +    let _ = [std::path::PathBuf::new()][..].iter().cloned();
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap());
 +    require_os_str(&OsString::from("x"));
 +    require_path(&std::path::PathBuf::from("x"));
 +    require_str(&String::from("x"));
 +    require_slice(&[String::from("x")]);
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types {
 +        let path = match get_file_path(t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
 +fn _msrv_1_35() {
 +    #![clippy::msrv = "1.35"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].iter().cloned();
 +}
 +
 +fn _msrv_1_36() {
 +    #![clippy::msrv = "1.36"]
 +    let _ = &["x"][..].iter().copied();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
++
++mod issue_9317 {
++    #![allow(dead_code)]
++
++    struct Bytes {}
++
++    impl ToString for Bytes {
++        fn to_string(&self) -> String {
++            "123".to_string()
++        }
++    }
++
++    impl AsRef<[u8]> for Bytes {
++        fn as_ref(&self) -> &[u8] {
++            &[1, 2, 3]
++        }
++    }
++
++    fn consume<C: AsRef<[u8]>>(c: C) {
++        let _ = c;
++    }
++
++    pub fn main() {
++        let b = Bytes {};
++        // Should not lint.
++        consume(b.to_string());
++    }
++}
index fe09a489ab0a67b81cf5280f3dc8b9c1140aa061,0000000000000000000000000000000000000000..7f62ba3ab5d559ea0d8a60da23f977389044f054
mode 100644,000000..100644
--- /dev/null
@@@ -1,331 -1,0 +1,359 @@@
 +// run-rustfix
 +
 +#![allow(clippy::ptr_arg)]
 +#![warn(clippy::unnecessary_to_owned)]
 +#![feature(custom_inner_attributes)]
 +
 +use std::borrow::Cow;
 +use std::ffi::{CStr, CString, OsStr, OsString};
 +use std::ops::Deref;
 +
 +#[derive(Clone)]
 +struct X(String);
 +
 +impl Deref for X {
 +    type Target = [u8];
 +    fn deref(&self) -> &[u8] {
 +        self.0.as_bytes()
 +    }
 +}
 +
 +impl AsRef<str> for X {
 +    fn as_ref(&self) -> &str {
 +        self.0.as_str()
 +    }
 +}
 +
 +impl ToString for X {
 +    fn to_string(&self) -> String {
 +        self.0.to_string()
 +    }
 +}
 +
 +impl X {
 +    fn join(&self, other: impl AsRef<str>) -> Self {
 +        let mut s = self.0.clone();
 +        s.push_str(other.as_ref());
 +        Self(s)
 +    }
 +}
 +
 +#[allow(dead_code)]
 +#[derive(Clone)]
 +enum FileType {
 +    Account,
 +    PrivateKey,
 +    Certificate,
 +}
 +
 +fn main() {
 +    let c_str = CStr::from_bytes_with_nul(&[0]).unwrap();
 +    let os_str = OsStr::new("x");
 +    let path = std::path::Path::new("x");
 +    let s = "x";
 +    let array = ["x"];
 +    let array_ref = &["x"];
 +    let slice = &["x"][..];
 +    let x = X(String::from("x"));
 +    let x_ref = &x;
 +
 +    require_c_str(&Cow::from(c_str).into_owned());
 +    require_c_str(&c_str.to_owned());
 +
 +    require_os_str(&os_str.to_os_string());
 +    require_os_str(&Cow::from(os_str).into_owned());
 +    require_os_str(&os_str.to_owned());
 +
 +    require_path(&path.to_path_buf());
 +    require_path(&Cow::from(path).into_owned());
 +    require_path(&path.to_owned());
 +
 +    require_str(&s.to_string());
 +    require_str(&Cow::from(s).into_owned());
 +    require_str(&s.to_owned());
 +    require_str(&x_ref.to_string());
 +
 +    require_slice(&slice.to_vec());
 +    require_slice(&Cow::from(slice).into_owned());
 +    require_slice(&array.to_owned());
 +    require_slice(&array_ref.to_owned());
 +    require_slice(&slice.to_owned());
 +    require_slice(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_x(&Cow::<X>::Owned(x.clone()).into_owned());
 +    require_x(&x_ref.to_owned()); // No longer flagged because of #8759.
 +
 +    require_deref_c_str(c_str.to_owned());
 +    require_deref_os_str(os_str.to_owned());
 +    require_deref_path(path.to_owned());
 +    require_deref_str(s.to_owned());
 +    require_deref_slice(slice.to_owned());
 +
 +    require_impl_deref_c_str(c_str.to_owned());
 +    require_impl_deref_os_str(os_str.to_owned());
 +    require_impl_deref_path(path.to_owned());
 +    require_impl_deref_str(s.to_owned());
 +    require_impl_deref_slice(slice.to_owned());
 +
 +    require_deref_str_slice(s.to_owned(), slice.to_owned());
 +    require_deref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    require_as_ref_c_str(c_str.to_owned());
 +    require_as_ref_os_str(os_str.to_owned());
 +    require_as_ref_path(path.to_owned());
 +    require_as_ref_str(s.to_owned());
 +    require_as_ref_str(x.to_owned());
 +    require_as_ref_slice(array.to_owned());
 +    require_as_ref_slice(array_ref.to_owned());
 +    require_as_ref_slice(slice.to_owned());
 +
 +    require_impl_as_ref_c_str(c_str.to_owned());
 +    require_impl_as_ref_os_str(os_str.to_owned());
 +    require_impl_as_ref_path(path.to_owned());
 +    require_impl_as_ref_str(s.to_owned());
 +    require_impl_as_ref_str(x.to_owned());
 +    require_impl_as_ref_slice(array.to_owned());
 +    require_impl_as_ref_slice(array_ref.to_owned());
 +    require_impl_as_ref_slice(slice.to_owned());
 +
 +    require_as_ref_str_slice(s.to_owned(), array.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
 +    require_as_ref_str_slice(s.to_owned(), slice.to_owned());
 +    require_as_ref_slice_str(array.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
 +    require_as_ref_slice_str(slice.to_owned(), s.to_owned());
 +
 +    let _ = x.join(&x_ref.to_string());
 +
 +    let _ = slice.to_vec().into_iter();
 +    let _ = slice.to_owned().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
 +    let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
 +
 +    let _ = IntoIterator::into_iter(slice.to_vec());
 +    let _ = IntoIterator::into_iter(slice.to_owned());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
 +    let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
 +
 +    let _ = check_files(&[FileType::Account]);
 +
 +    // negative tests
 +    require_string(&s.to_string());
 +    require_string(&Cow::from(s).into_owned());
 +    require_string(&s.to_owned());
 +    require_string(&x_ref.to_string());
 +
 +    // `X` isn't copy.
 +    require_slice(&x.to_owned());
 +    require_deref_slice(x.to_owned());
 +
 +    // The following should be flagged by `redundant_clone`, but not by this lint.
 +    require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
 +    require_os_str(&OsString::from("x").to_os_string());
 +    require_path(&std::path::PathBuf::from("x").to_path_buf());
 +    require_str(&String::from("x").to_string());
 +    require_slice(&[String::from("x")].to_owned());
 +}
 +
 +fn require_c_str(_: &CStr) {}
 +fn require_os_str(_: &OsStr) {}
 +fn require_path(_: &std::path::Path) {}
 +fn require_str(_: &str) {}
 +fn require_slice<T>(_: &[T]) {}
 +fn require_x(_: &X) {}
 +
 +fn require_deref_c_str<T: Deref<Target = CStr>>(_: T) {}
 +fn require_deref_os_str<T: Deref<Target = OsStr>>(_: T) {}
 +fn require_deref_path<T: Deref<Target = std::path::Path>>(_: T) {}
 +fn require_deref_str<T: Deref<Target = str>>(_: T) {}
 +fn require_deref_slice<T, U: Deref<Target = [T]>>(_: U) {}
 +
 +fn require_impl_deref_c_str(_: impl Deref<Target = CStr>) {}
 +fn require_impl_deref_os_str(_: impl Deref<Target = OsStr>) {}
 +fn require_impl_deref_path(_: impl Deref<Target = std::path::Path>) {}
 +fn require_impl_deref_str(_: impl Deref<Target = str>) {}
 +fn require_impl_deref_slice<T>(_: impl Deref<Target = [T]>) {}
 +
 +fn require_deref_str_slice<T: Deref<Target = str>, U, V: Deref<Target = [U]>>(_: T, _: V) {}
 +fn require_deref_slice_str<T, U: Deref<Target = [T]>, V: Deref<Target = str>>(_: U, _: V) {}
 +
 +fn require_as_ref_c_str<T: AsRef<CStr>>(_: T) {}
 +fn require_as_ref_os_str<T: AsRef<OsStr>>(_: T) {}
 +fn require_as_ref_path<T: AsRef<std::path::Path>>(_: T) {}
 +fn require_as_ref_str<T: AsRef<str>>(_: T) {}
 +fn require_as_ref_slice<T, U: AsRef<[T]>>(_: U) {}
 +
 +fn require_impl_as_ref_c_str(_: impl AsRef<CStr>) {}
 +fn require_impl_as_ref_os_str(_: impl AsRef<OsStr>) {}
 +fn require_impl_as_ref_path(_: impl AsRef<std::path::Path>) {}
 +fn require_impl_as_ref_str(_: impl AsRef<str>) {}
 +fn require_impl_as_ref_slice<T>(_: impl AsRef<[T]>) {}
 +
 +fn require_as_ref_str_slice<T: AsRef<str>, U, V: AsRef<[U]>>(_: T, _: V) {}
 +fn require_as_ref_slice_str<T, U: AsRef<[T]>, V: AsRef<str>>(_: U, _: V) {}
 +
 +// `check_files` is based on:
 +// https://github.com/breard-r/acmed/blob/1f0dcc32aadbc5e52de6d23b9703554c0f925113/acmed/src/storage.rs#L262
 +fn check_files(file_types: &[FileType]) -> bool {
 +    for t in file_types.to_vec() {
 +        let path = match get_file_path(&t) {
 +            Ok(p) => p,
 +            Err(_) => {
 +                return false;
 +            },
 +        };
 +        if !path.is_file() {
 +            return false;
 +        }
 +    }
 +    true
 +}
 +
 +fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
 +    Ok(std::path::PathBuf::new())
 +}
 +
 +fn require_string(_: &String) {}
 +
 +fn _msrv_1_35() {
 +    #![clippy::msrv = "1.35"]
 +    // `copied` was stabilized in 1.36, so clippy should use `cloned`.
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +fn _msrv_1_36() {
 +    #![clippy::msrv = "1.36"]
 +    let _ = &["x"][..].to_vec().into_iter();
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8507
 +mod issue_8507 {
 +    #![allow(dead_code)]
 +
 +    struct Opaque<P>(P);
 +
 +    pub trait Abstracted {}
 +
 +    impl<P> Abstracted for Opaque<P> {}
 +
 +    fn build<P>(p: P) -> Opaque<P>
 +    where
 +        P: AsRef<str>,
 +    {
 +        Opaque(p)
 +    }
 +
 +    // Should not lint.
 +    fn test_str(s: &str) -> Box<dyn Abstracted> {
 +        Box::new(build(s.to_string()))
 +    }
 +
 +    // Should not lint.
 +    fn test_x(x: super::X) -> Box<dyn Abstracted> {
 +        Box::new(build(x))
 +    }
 +
 +    #[derive(Clone, Copy)]
 +    struct Y(&'static str);
 +
 +    impl AsRef<str> for Y {
 +        fn as_ref(&self) -> &str {
 +            self.0
 +        }
 +    }
 +
 +    impl ToString for Y {
 +        fn to_string(&self) -> String {
 +            self.0.to_string()
 +        }
 +    }
 +
 +    // Should lint because Y is copy.
 +    fn test_y(y: Y) -> Box<dyn Abstracted> {
 +        Box::new(build(y.to_string()))
 +    }
 +}
 +
 +// https://github.com/rust-lang/rust-clippy/issues/8759
 +mod issue_8759 {
 +    #![allow(dead_code)]
 +
 +    #[derive(Default)]
 +    struct View {}
 +
 +    impl std::borrow::ToOwned for View {
 +        type Owned = View;
 +        fn to_owned(&self) -> Self::Owned {
 +            View {}
 +        }
 +    }
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
 +
 +mod issue_8759_variant {
 +    #![allow(dead_code)]
 +
 +    #[derive(Clone, Default)]
 +    struct View {}
 +
 +    #[derive(Default)]
 +    struct RenderWindow {
 +        default_view: View,
 +    }
 +
 +    impl RenderWindow {
 +        fn default_view(&self) -> &View {
 +            &self.default_view
 +        }
 +        fn set_view(&mut self, _view: &View) {}
 +    }
 +
 +    fn main() {
 +        let mut rw = RenderWindow::default();
 +        rw.set_view(&rw.default_view().to_owned());
 +    }
 +}
++
++mod issue_9317 {
++    #![allow(dead_code)]
++
++    struct Bytes {}
++
++    impl ToString for Bytes {
++        fn to_string(&self) -> String {
++            "123".to_string()
++        }
++    }
++
++    impl AsRef<[u8]> for Bytes {
++        fn as_ref(&self) -> &[u8] {
++            &[1, 2, 3]
++        }
++    }
++
++    fn consume<C: AsRef<[u8]>>(c: C) {
++        let _ = c;
++    }
++
++    pub fn main() {
++        let b = Bytes {};
++        // Should not lint.
++        consume(b.to_string());
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..153457e367165c6ce2fa6d3370a4c81a2645d265
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,144 @@@
++#![warn(clippy::unused_peekable)]
++#![allow(clippy::no_effect)]
++
++use std::iter::Empty;
++use std::iter::Peekable;
++
++fn main() {
++    invalid();
++    valid();
++}
++
++#[allow(clippy::unused_unit)]
++fn invalid() {
++    let peekable = std::iter::empty::<u32>().peekable();
++
++    // Only lint `new_local`
++    let old_local = std::iter::empty::<u32>().peekable();
++    let new_local = old_local;
++
++    // Behind mut ref
++    let mut by_mut_ref_test = std::iter::empty::<u32>().peekable();
++    let by_mut_ref = &mut by_mut_ref_test;
++
++    // Explicitly returns `Peekable`
++    fn returns_peekable() -> Peekable<Empty<u32>> {
++        std::iter::empty().peekable()
++    }
++
++    let peekable_from_fn = returns_peekable();
++
++    // Using a method not exclusive to `Peekable`
++    let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
++    peekable_using_iterator_method.next();
++
++    // Passed by ref to another function
++    fn takes_ref(_peek: &Peekable<Empty<u32>>) {}
++    let passed_along_ref = std::iter::empty::<u32>().peekable();
++    takes_ref(&passed_along_ref);
++
++    // `by_ref` without `peek`
++    let mut by_ref_test = std::iter::empty::<u32>().peekable();
++    let _by_ref = by_ref_test.by_ref();
++
++    let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
++    for x in peekable_in_for_loop {}
++}
++
++fn valid() {
++    fn takes_peekable(_peek: Peekable<Empty<u32>>) {}
++
++    // Passed to another function
++    let passed_along = std::iter::empty::<u32>().peekable();
++    takes_peekable(passed_along);
++
++    // Passed to another method
++    struct PeekableConsumer;
++    impl PeekableConsumer {
++        fn consume(&self, _: Peekable<Empty<u32>>) {}
++        fn consume_mut_ref(&self, _: &mut Peekable<Empty<u32>>) {}
++    }
++
++    let peekable_consumer = PeekableConsumer;
++    let mut passed_along_to_method = std::iter::empty::<u32>().peekable();
++    peekable_consumer.consume_mut_ref(&mut passed_along_to_method);
++    peekable_consumer.consume(passed_along_to_method);
++
++    // `peek` called in another block
++    let mut peekable_in_block = std::iter::empty::<u32>().peekable();
++    {
++        peekable_in_block.peek();
++    }
++
++    // Check the other `Peekable` methods :)
++    {
++        let mut peekable_with_peek_mut = std::iter::empty::<u32>().peekable();
++        peekable_with_peek_mut.peek_mut();
++
++        let mut peekable_with_next_if = std::iter::empty::<u32>().peekable();
++        peekable_with_next_if.next_if(|_| true);
++
++        let mut peekable_with_next_if_eq = std::iter::empty::<u32>().peekable();
++        peekable_with_next_if_eq.next_if_eq(&3);
++    }
++
++    let mut peekable_in_closure = std::iter::empty::<u32>().peekable();
++    let call_peek = |p: &mut Peekable<Empty<u32>>| {
++        p.peek();
++    };
++    call_peek(&mut peekable_in_closure);
++
++    // From a macro
++    macro_rules! make_me_a_peekable_please {
++        () => {
++            std::iter::empty::<u32>().peekable()
++        };
++    }
++
++    let _unsuspecting_macro_user = make_me_a_peekable_please!();
++
++    // Generic Iterator returned
++    fn return_an_iter() -> impl Iterator<Item = u32> {
++        std::iter::empty::<u32>().peekable()
++    }
++
++    let _unsuspecting_user = return_an_iter();
++
++    // Call `peek` in a macro
++    macro_rules! peek_iter {
++        ($iter:ident) => {
++            $iter.peek();
++        };
++    }
++
++    let mut peek_in_macro = std::iter::empty::<u32>().peekable();
++    peek_iter!(peek_in_macro);
++
++    // Behind mut ref
++    let mut by_mut_ref_test = std::iter::empty::<u32>().peekable();
++    let by_mut_ref = &mut by_mut_ref_test;
++    by_mut_ref.peek();
++
++    // Behind ref
++    let mut by_ref_test = std::iter::empty::<u32>().peekable();
++    let by_ref = &by_ref_test;
++    by_ref_test.peek();
++
++    // In struct
++    struct PeekableWrapper {
++        f: Peekable<Empty<u32>>,
++    }
++
++    let struct_test = std::iter::empty::<u32>().peekable();
++    PeekableWrapper { f: struct_test };
++
++    // `by_ref` before `peek`
++    let mut by_ref_test = std::iter::empty::<u32>().peekable();
++    let peeked_val = by_ref_test.by_ref().peek();
++
++    // `peek` called in another block as the last expression
++    let mut peekable_last_expr = std::iter::empty::<u32>().peekable();
++    {
++        peekable_last_expr.peek();
++    }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d557f54179dbaf680ccf899b14e9e3b242393935
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,67 @@@
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:14:9
++   |
++LL |     let peekable = std::iter::empty::<u32>().peekable();
++   |         ^^^^^^^^
++   |
++   = note: `-D clippy::unused-peekable` implied by `-D warnings`
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:18:9
++   |
++LL |     let new_local = old_local;
++   |         ^^^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:22:9
++   |
++LL |     let by_mut_ref = &mut by_mut_ref_test;
++   |         ^^^^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:29:9
++   |
++LL |     let peekable_from_fn = returns_peekable();
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:32:13
++   |
++LL |     let mut peekable_using_iterator_method = std::iter::empty::<u32>().peekable();
++   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:37:9
++   |
++LL |     let passed_along_ref = std::iter::empty::<u32>().peekable();
++   |         ^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:42:9
++   |
++LL |     let _by_ref = by_ref_test.by_ref();
++   |         ^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: `peek` never called on `Peekable` iterator
++  --> $DIR/unused_peekable.rs:44:13
++   |
++LL |     let mut peekable_in_for_loop = std::iter::empty::<u32>().peekable();
++   |             ^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: consider removing the call to `peekable`
++
++error: aborting due to 8 previous errors
++
index a4a3cd1d37977d3dcb16181db4ad8c20a54ffaab,0000000000000000000000000000000000000000..d9fd402e7cfb9d200cc79f794e9b4b41c9c94c82
mode 100644,000000..100644
--- /dev/null
@@@ -1,16 -1,0 +1,17 @@@
-     let res: Result<u8, ()> = Ok(0);
 +#![warn(clippy::unwrap_used)]
 +
 +fn unwrap_option() {
 +    let opt = Some(0);
 +    let _ = opt.unwrap();
 +}
 +
 +fn unwrap_result() {
++    let res: Result<u8, u8> = Ok(0);
 +    let _ = res.unwrap();
++    let _ = res.unwrap_err();
 +}
 +
 +fn main() {
 +    unwrap_option();
 +    unwrap_result();
 +}
index 4f0858005f6e7f140cd0c69bcea3c9111e82b673,0000000000000000000000000000000000000000..78422757819d502d3ba231fdc6b803e2a5407205
mode 100644,000000..100644
--- /dev/null
@@@ -1,19 -1,0 +1,27 @@@
- error: aborting due to 2 previous errors
 +error: used `unwrap()` on `an Option` value
 +  --> $DIR/unwrap.rs:5:13
 +   |
 +LL |     let _ = opt.unwrap();
 +   |             ^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::unwrap-used` implied by `-D warnings`
 +   = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message
 +
 +error: used `unwrap()` on `a Result` value
 +  --> $DIR/unwrap.rs:10:13
 +   |
 +LL |     let _ = res.unwrap();
 +   |             ^^^^^^^^^^^^
 +   |
 +   = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message
 +
++error: used `unwrap_err()` on `a Result` value
++  --> $DIR/unwrap.rs:11:13
++   |
++LL |     let _ = res.unwrap_err();
++   |             ^^^^^^^^^^^^^^^^
++   |
++   = help: if you don't want to handle the `Ok` case gracefully, consider using `expect_err()` to provide a better panic message
++
++error: aborting due to 3 previous errors
 +
index 0d4a0504a6e04142ea69b6940c01833b7d9f7bbb,0000000000000000000000000000000000000000..9f27fef82494b8207437530017889d3bd523455d
mode 100644,000000..100644
--- /dev/null
@@@ -1,10 -1,0 +1,35 @@@
 +#![warn(clippy::unwrap_used, clippy::expect_used)]
 +
++trait OptionExt {
++    type Item;
++
++    fn unwrap_err(self) -> Self::Item;
++
++    fn expect_err(self, msg: &str) -> Self::Item;
++}
++
++impl<T> OptionExt for Option<T> {
++    type Item = T;
++    fn unwrap_err(self) -> T {
++        panic!();
++    }
++
++    fn expect_err(self, msg: &str) -> T {
++        panic!();
++    }
++}
++
 +fn main() {
 +    Some(3).unwrap();
 +    Some(3).expect("Hello world!");
 +
++    // Don't trigger on unwrap_err on an option
++    Some(3).unwrap_err();
++    Some(3).expect_err("Hellow none!");
++
 +    let a: Result<i32, i32> = Ok(3);
 +    a.unwrap();
 +    a.expect("Hello world!");
++    a.unwrap_err();
++    a.expect_err("Hello error!");
 +}
index f54bfd617c4ee5589b1c906e4ab066c99fb7981e,0000000000000000000000000000000000000000..1a19459b2c174c0ca8dfaee96d6148effd554094
mode 100644,000000..100644
--- /dev/null
@@@ -1,36 -1,0 +1,52 @@@
-   --> $DIR/unwrap_expect_used.rs:4:5
 +error: used `unwrap()` on `an Option` value
-   --> $DIR/unwrap_expect_used.rs:5:5
++  --> $DIR/unwrap_expect_used.rs:23:5
 +   |
 +LL |     Some(3).unwrap();
 +   |     ^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::unwrap-used` implied by `-D warnings`
 +   = help: if this value is `None`, it will panic
 +
 +error: used `expect()` on `an Option` value
-   --> $DIR/unwrap_expect_used.rs:8:5
++  --> $DIR/unwrap_expect_used.rs:24:5
 +   |
 +LL |     Some(3).expect("Hello world!");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = note: `-D clippy::expect-used` implied by `-D warnings`
 +   = help: if this value is `None`, it will panic
 +
 +error: used `unwrap()` on `a Result` value
-   --> $DIR/unwrap_expect_used.rs:9:5
++  --> $DIR/unwrap_expect_used.rs:31:5
 +   |
 +LL |     a.unwrap();
 +   |     ^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
 +error: used `expect()` on `a Result` value
- error: aborting due to 4 previous errors
++  --> $DIR/unwrap_expect_used.rs:32:5
 +   |
 +LL |     a.expect("Hello world!");
 +   |     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: if this value is an `Err`, it will panic
 +
++error: used `unwrap_err()` on `a Result` value
++  --> $DIR/unwrap_expect_used.rs:33:5
++   |
++LL |     a.unwrap_err();
++   |     ^^^^^^^^^^^^^^
++   |
++   = help: if this value is an `Ok`, it will panic
++
++error: used `expect_err()` on `a Result` value
++  --> $DIR/unwrap_expect_used.rs:34:5
++   |
++LL |     a.expect_err("Hello error!");
++   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
++   |
++   = help: if this value is an `Ok`, it will panic
++
++error: aborting due to 6 previous errors
 +
index 39f54c27bee1a6356e40ad4628a9b6f10e086e14,0000000000000000000000000000000000000000..4acf5b5fa2d1ba8ab717631f61210aa301ff90c5
mode 100644,000000..100644
--- /dev/null
@@@ -1,40 -1,0 +1,40 @@@
-     let _: String = "".to_owned().try_into().unwrap();
 +#![deny(clippy::useless_conversion)]
 +
 +fn test_generic<T: Copy>(val: T) -> T {
 +    let _ = T::try_from(val).unwrap();
 +    val.try_into().unwrap()
 +}
 +
 +fn test_generic2<T: Copy + Into<i32> + Into<U>, U: From<T>>(val: T) {
 +    // ok
 +    let _: i32 = val.try_into().unwrap();
 +    let _: U = val.try_into().unwrap();
 +    let _ = U::try_from(val).unwrap();
 +}
 +
 +fn main() {
 +    test_generic(10i32);
 +    test_generic2::<i32, i32>(10i32);
 +
 +    let _: String = "foo".try_into().unwrap();
 +    let _: String = TryFrom::try_from("foo").unwrap();
 +    let _ = String::try_from("foo").unwrap();
 +    #[allow(clippy::useless_conversion)]
 +    {
 +        let _ = String::try_from("foo").unwrap();
 +        let _: String = "foo".try_into().unwrap();
 +    }
 +    let _: String = "foo".to_string().try_into().unwrap();
 +    let _: String = TryFrom::try_from("foo".to_string()).unwrap();
 +    let _ = String::try_from("foo".to_string()).unwrap();
 +    let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
 +    let _: String = format!("Hello {}", "world").try_into().unwrap();
-         Err(_) => "".into(),
++    let _: String = String::new().try_into().unwrap();
 +    let _: String = match String::from("_").try_into() {
 +        Ok(a) => a,
++        Err(_) => String::new(),
 +    };
 +    // FIXME this is a false negative
 +    #[allow(clippy::cmp_owned)]
 +    if String::from("a") == TryInto::<String>::try_into(String::from("a")).unwrap() {}
 +}
index b691c13f7dbb747ac540e2258a071b53419a186e,0000000000000000000000000000000000000000..12e74d614717db6ee5c6d8068f894958d53e7dad
mode 100644,000000..100644
--- /dev/null
@@@ -1,79 -1,0 +1,79 @@@
- LL |     let _: String = "".to_owned().try_into().unwrap();
 +error: useless conversion to the same type: `T`
 +  --> $DIR/useless_conversion_try.rs:4:13
 +   |
 +LL |     let _ = T::try_from(val).unwrap();
 +   |             ^^^^^^^^^^^^^^^^
 +   |
 +note: the lint level is defined here
 +  --> $DIR/useless_conversion_try.rs:1:9
 +   |
 +LL | #![deny(clippy::useless_conversion)]
 +   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   = help: consider removing `T::try_from()`
 +
 +error: useless conversion to the same type: `T`
 +  --> $DIR/useless_conversion_try.rs:5:5
 +   |
 +LL |     val.try_into().unwrap()
 +   |     ^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `.try_into()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:27:21
 +   |
 +LL |     let _: String = "foo".to_string().try_into().unwrap();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `.try_into()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:28:21
 +   |
 +LL |     let _: String = TryFrom::try_from("foo".to_string()).unwrap();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `TryFrom::try_from()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:29:13
 +   |
 +LL |     let _ = String::try_from("foo".to_string()).unwrap();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `String::try_from()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:30:13
 +   |
 +LL |     let _ = String::try_from(format!("A: {:04}", 123)).unwrap();
 +   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `String::try_from()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:31:21
 +   |
 +LL |     let _: String = format!("Hello {}", "world").try_into().unwrap();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `.try_into()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:32:21
 +   |
++LL |     let _: String = String::new().try_into().unwrap();
 +   |                     ^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `.try_into()`
 +
 +error: useless conversion to the same type: `std::string::String`
 +  --> $DIR/useless_conversion_try.rs:33:27
 +   |
 +LL |     let _: String = match String::from("_").try_into() {
 +   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 +   |
 +   = help: consider removing `.try_into()`
 +
 +error: aborting due to 9 previous errors
 +
index 7ed27439ec6e4432372224dd696d485024918b96,0000000000000000000000000000000000000000..a8307e741cf17b0c8ff64fd53cf2096b22c7f068
mode 100644,000000..100644
--- /dev/null
@@@ -1,15 -1,0 +1,19 @@@
-     vec![1, 2, 3, 4, 5].resize(0, 5);
 +#![warn(clippy::vec_resize_to_zero)]
 +
 +fn main() {
++    let mut v = vec![1, 2, 3, 4, 5];
++
 +    // applicable here
-     vec![1, 2, 3, 4, 5].resize(2, 5);
++    v.resize(0, 5);
 +
 +    // not applicable
-     vec!["foo", "bar", "baz"].resize(0, "bar");
++    v.resize(2, 5);
++
++    let mut v = vec!["foo", "bar", "baz"];
 +
 +    // applicable here, but only implemented for integer literals for now
-     vec!["foo", "bar", "baz"].resize(2, "bar")
++    v.resize(0, "bar");
 +
 +    // not applicable
++    v.resize(2, "bar")
 +}
index feb846298c656878246193fd8968b2c67127b260,0000000000000000000000000000000000000000..7428cf62d6c429554d8a54e37cc641dfb93f8536
mode 100644,000000..100644
--- /dev/null
@@@ -1,13 -1,0 +1,13 @@@
-   --> $DIR/vec_resize_to_zero.rs:5:5
 +error: emptying a vector with `resize`
- LL |     vec![1, 2, 3, 4, 5].resize(0, 5);
-    |     ^^^^^^^^^^^^^^^^^^^^------------
-    |                         |
-    |                         help: ...or you can empty the vector with: `clear()`
++  --> $DIR/vec_resize_to_zero.rs:7:5
 +   |
++LL |     v.resize(0, 5);
++   |     ^^------------
++   |       |
++   |       help: ...or you can empty the vector with: `clear()`
 +   |
 +   = note: `-D clippy::vec-resize-to-zero` implied by `-D warnings`
 +   = help: the arguments may be inverted...
 +
 +error: aborting due to previous error
 +
index e0065e05ade62d31ba9c21f7fe8b75f2b368d973,0000000000000000000000000000000000000000..df267e9872a0a0a6bdd6984c7aeca3e77c4e4d88
mode 100644,000000..100644
--- /dev/null
@@@ -1,28 -1,0 +1,28 @@@
-     let mut f = File::open(&path)?;
 +#![warn(clippy::verbose_file_reads)]
 +use std::env::temp_dir;
 +use std::fs::File;
 +use std::io::Read;
 +
 +struct Struct;
 +// To make sure we only warn on File::{read_to_end, read_to_string} calls
 +impl Struct {
 +    pub fn read_to_end(&self) {}
 +
 +    pub fn read_to_string(&self) {}
 +}
 +
 +fn main() -> std::io::Result<()> {
 +    let path = "foo.txt";
 +    // Lint shouldn't catch this
 +    let s = Struct;
 +    s.read_to_end();
 +    s.read_to_string();
 +    // Should catch this
++    let mut f = File::open(path)?;
 +    let mut buffer = Vec::new();
 +    f.read_to_end(&mut buffer)?;
 +    // ...and this
 +    let mut string_buffer = String::new();
 +    f.read_to_string(&mut string_buffer)?;
 +    Ok(())
 +}
index e13efb3e0164b638f96b05861b818fdc70b23ed0,0000000000000000000000000000000000000000..95325e06037829c2fb9a519f5b3a50e10ddee1e1
mode 100644,000000..100644
--- /dev/null
@@@ -1,107 -1,0 +1,107 @@@
-         .args(&["-p", "subcrate"])
-         .args(&["-p", "path_dep"])
 +#![feature(once_cell)]
 +
 +use std::path::PathBuf;
 +use std::process::Command;
 +use test_utils::{CARGO_CLIPPY_PATH, IS_RUSTC_TEST_SUITE};
 +
 +mod test_utils;
 +
 +#[test]
 +fn test_no_deps_ignores_path_deps_in_workspaces() {
 +    if IS_RUSTC_TEST_SUITE {
 +        return;
 +    }
 +    let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
 +    let target_dir = root.join("target").join("workspace_test");
 +    let cwd = root.join("tests/workspace_test");
 +
 +    // Make sure we start with a clean state
 +    Command::new("cargo")
 +        .current_dir(&cwd)
 +        .env("CARGO_TARGET_DIR", &target_dir)
 +        .arg("clean")
-         .args(&["-p", "subcrate"])
++        .args(["-p", "subcrate"])
++        .args(["-p", "path_dep"])
 +        .output()
 +        .unwrap();
 +
 +    // `path_dep` is a path dependency of `subcrate` that would trigger a denied lint.
 +    // Make sure that with the `--no-deps` argument Clippy does not run on `path_dep`.
 +    let output = Command::new(&*CARGO_CLIPPY_PATH)
 +        .current_dir(&cwd)
 +        .env("CARGO_INCREMENTAL", "0")
 +        .env("CARGO_TARGET_DIR", &target_dir)
 +        .arg("clippy")
-         .args(&["--cfg", r#"feature="primary_package_test""#])
++        .args(["-p", "subcrate"])
 +        .arg("--no-deps")
 +        .arg("--")
 +        .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-             .args(&["-p", "subcrate"])
++        .args(["--cfg", r#"feature="primary_package_test""#])
 +        .output()
 +        .unwrap();
 +    println!("status: {}", output.status);
 +    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +    assert!(output.status.success());
 +
 +    let lint_path_dep = || {
 +        // Test that without the `--no-deps` argument, `path_dep` is linted.
 +        let output = Command::new(&*CARGO_CLIPPY_PATH)
 +            .current_dir(&cwd)
 +            .env("CARGO_INCREMENTAL", "0")
 +            .env("CARGO_TARGET_DIR", &target_dir)
 +            .arg("clippy")
-             .args(&["--cfg", r#"feature="primary_package_test""#])
++            .args(["-p", "subcrate"])
 +            .arg("--")
 +            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
-             .args(&["-p", "subcrate"])
++            .args(["--cfg", r#"feature="primary_package_test""#])
 +            .output()
 +            .unwrap();
 +        println!("status: {}", output.status);
 +        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +        assert!(!output.status.success());
 +        assert!(
 +            String::from_utf8(output.stderr)
 +                .unwrap()
 +                .contains("error: empty `loop {}` wastes CPU cycles")
 +        );
 +    };
 +
 +    // Make sure Cargo is aware of the removal of `--no-deps`.
 +    lint_path_dep();
 +
 +    let successful_build = || {
 +        let output = Command::new(&*CARGO_CLIPPY_PATH)
 +            .current_dir(&cwd)
 +            .env("CARGO_INCREMENTAL", "0")
 +            .env("CARGO_TARGET_DIR", &target_dir)
 +            .arg("clippy")
++            .args(["-p", "subcrate"])
 +            .arg("--")
 +            .arg("-Cdebuginfo=0") // disable debuginfo to generate less data in the target dir
 +            .output()
 +            .unwrap();
 +        println!("status: {}", output.status);
 +        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
 +        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
 +
 +        assert!(output.status.success());
 +
 +        output
 +    };
 +
 +    // Trigger a successful build, so Cargo would like to cache the build result.
 +    successful_build();
 +
 +    // Make sure there's no spurious rebuild when nothing changes.
 +    let stderr = String::from_utf8(successful_build().stderr).unwrap();
 +    assert!(!stderr.contains("Compiling"));
 +    assert!(!stderr.contains("Checking"));
 +    assert!(stderr.contains("Finished"));
 +
 +    // Make sure Cargo is aware of the new `--cfg` flag.
 +    lint_path_dep();
 +}